/******************************************************************************* * Cloud Foundry * Copyright (c) [2009-2016] Pivotal Software, Inc. All Rights Reserved. * * This product is licensed to you under the Apache License, Version 2.0 (the "License"). * You may not use this product except in compliance with the License. * * This product includes a number of subcomponents with * separate copyright notices and license terms. Your use of these * subcomponents is subject to the terms and conditions of the * subcomponent's license, as noted in the LICENSE file. *******************************************************************************/ package org.cloudfoundry.identity.uaa.test; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.cloudfoundry.identity.uaa.constants.OriginKeys; import org.cloudfoundry.identity.uaa.user.UaaUser; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.junit.rules.TestWatchman; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.Statement; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; import org.springframework.security.oauth2.client.OAuth2ClientContext; import org.springframework.security.oauth2.client.OAuth2RestTemplate; import org.springframework.security.oauth2.client.http.OAuth2ErrorHandler; import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; import org.springframework.security.oauth2.client.token.AccessTokenRequest; import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest; import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.security.oauth2.provider.client.BaseClientDetails; import org.springframework.util.Assert; import org.springframework.web.client.RestOperations; import java.io.IOException; import java.net.HttpURLConnection; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.junit.Assert.assertEquals; /** * @author Dave Syer * */ public class TestAccountSetup extends TestWatchman { private static Log logger = LogFactory.getLog(TestAccountSetup.class); private final UrlHelper serverRunning; private final UaaTestAccounts testAccounts; private UaaUser user; private static boolean initialized = false; private TestAccountSetup(UrlHelper serverRunning, UaaTestAccounts testAccounts) { this.serverRunning = serverRunning; this.testAccounts = testAccounts; } public static TestAccountSetup standard(UrlHelper serverRunning, UaaTestAccounts testAccounts) { return new TestAccountSetup(serverRunning, testAccounts); } @Override public Statement apply(Statement base, FrameworkMethod method, Object target) { initializeIfNecessary(method, target); return super.apply(base, method, target); } /** * @return the user (if already created null otherwise) */ public UaaUser getUser() { return user; } private void initializeIfNecessary(FrameworkMethod method, Object target) { OAuth2ProtectedResourceDetails resource = testAccounts.getAdminClientCredentialsResource(); OAuth2RestTemplate client = createRestTemplate(resource, new DefaultAccessTokenRequest()); // Cache statically to save time on a test suite if (!initialized) { logger.info("Checking user account context for server=" + resource.getAccessTokenUri()); if (!scimClientExists(client)) { createScimClient(client); } if (!appClientExists(client)) { createAppClient(client); } if (!cfClientExists(client)) { createCfClient(client); } initialized = true; } resource = testAccounts.getClientCredentialsResource("oauth.clients.scim", "scim", "scimsecret"); client = createRestTemplate(resource, new DefaultAccessTokenRequest()); initializeUserAccount(client); } private void createCfClient(RestOperations client) { BaseClientDetails clientDetails = new BaseClientDetails("cf", "cloud_controller,openid,password", "openid,cloud_controller.read,cloud_controller_service_permissions.read,password.write,scim.userids", "implicit", "uaa.none", "https://uaa.cloudfoundry.com/redirect/cf"); createClient(client, testAccounts.getClientDetails("oauth.clients.cf", clientDetails)); } private void createScimClient(RestOperations client) { BaseClientDetails clientDetails = new BaseClientDetails("scim", "oauth", "uaa.none", "client_credentials", "scim.read,scim.write,password.write,oauth.approvals","http://some.redirect.url.com"); clientDetails.setClientSecret("scimsecret"); createClient(client, testAccounts.getClientDetails("oauth.clients.scim", clientDetails)); } private void createAppClient(RestOperations client) { BaseClientDetails clientDetails = new BaseClientDetails("app", "none", "cloud_controller.read,cloud_controller_service_permissions.read,openid,password.write", "password,authorization_code,refresh_token", "uaa.resource"); clientDetails.setClientSecret("appclientsecret"); createClient(client, testAccounts.getClientDetails("oauth.clients.app", clientDetails)); } private void createClient(RestOperations client, ClientDetails clientDetails) { ResponseEntity<String> response = client.postForEntity(serverRunning.getClientsUri(), clientDetails, String.class); assertEquals(HttpStatus.CREATED, response.getStatusCode()); } private boolean clientExists(RestOperations client, OAuth2ProtectedResourceDetails resource) { ResponseEntity<String> response = client.getForEntity( serverRunning.getClientsUri() + "/" + resource.getClientId(), String.class); return response != null && response.getStatusCode() == HttpStatus.OK; } private boolean cfClientExists(RestOperations client) { return clientExists(client, testAccounts.getImplicitResource("oauth.clients.cf", "cf", null)); } private boolean scimClientExists(RestOperations client) { return clientExists(client, testAccounts.getClientCredentialsResource("oauth.clients.scim", "scim", "scimsecret")); } private boolean appClientExists(RestOperations client) { return clientExists(client, testAccounts.getClientCredentialsResource("oauth.clients.app", "app", "appclientsecret")); } private void initializeUserAccount(RestOperations client) { if (this.user == null) { UaaUser user = testAccounts.getUserWithRandomID(); @SuppressWarnings("rawtypes") ResponseEntity<Map> results = client.getForEntity(serverRunning.getUserUri() + "?filter=userName eq \"" + user.getUsername() + "\"", Map.class); assertEquals(HttpStatus.OK, results.getStatusCode()); @SuppressWarnings("unchecked") List<Map<String, ?>> resources = (List<Map<String, ?>>) results.getBody().get("resources"); Map<String, ?> map; if (!resources.isEmpty()) { map = resources.get(0); } else { map = getUserAsMap(user); @SuppressWarnings("rawtypes") ResponseEntity<Map> response = client.postForEntity(serverRunning.getUserUri(), map, Map.class); Assert.state(response.getStatusCode() == HttpStatus.CREATED, "User account not created: status was " + response.getStatusCode()); @SuppressWarnings("unchecked") Map<String, ?> value = response.getBody(); map = value; } this.user = getUserFromMap(map); } } private UaaUser getUserFromMap(Map<String, ?> map) { String id = (String) map.get("id"); String userName = (String) map.get("userName"); String email = null; if (map.containsKey("emails")) { @SuppressWarnings("unchecked") Collection<Map<String, String>> emails = (Collection<Map<String, String>>) map.get("emails"); if (!emails.isEmpty()) { email = emails.iterator().next().get("value"); } } String givenName = null; String familyName = null; if (map.containsKey("name")) { @SuppressWarnings("unchecked") Map<String, String> name = (Map<String, String>) map.get("name"); givenName = name.get("givenName"); familyName = name.get("familyName"); } @SuppressWarnings("unchecked") Collection<Map<String, String>> groups = (Collection<Map<String, String>>) map.get("groups"); return new UaaUser(id, userName, "<N/A>", email, extractAuthorities(groups), givenName, familyName, new Date(), new Date(), OriginKeys.UAA, "externalId", false, IdentityZoneHolder.get().getId(), null,null); } private List<? extends GrantedAuthority> extractAuthorities(Collection<Map<String, String>> groups) { List<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>(); for (Map<String, String> group : groups) { String role = group.get("display"); Assert.state(role != null, "Role is null in this group: " + group); authorities.add(new SimpleGrantedAuthority(role)); } return authorities; } private Map<String, ?> getUserAsMap(UaaUser user) { HashMap<String, Object> result = new HashMap<String, Object>(); if (user.getId() != null) { result.put("id", user.getId()); } if (user.getUsername() != null) { result.put("userName", user.getUsername()); } if (user.getPassword() != null) { result.put("password", user.getPassword()); } else { result.put("password", "password"); } String email = user.getEmail(); if (email != null) { @SuppressWarnings("unchecked") List<Map<String, String>> emails = Arrays.asList(Collections.singletonMap("value", email)); result.put("emails", emails); } String givenName = user.getGivenName(); if (givenName != null) { Map<String, String> name = new HashMap<String, String>(); name.put("givenName", givenName); if (user.getFamilyName() != null) { name.put("familyName", user.getFamilyName()); } result.put("name", name); } return result; } private OAuth2RestTemplate createRestTemplate(OAuth2ProtectedResourceDetails resource, AccessTokenRequest accessTokenRequest) { OAuth2ClientContext context = new DefaultOAuth2ClientContext(accessTokenRequest); OAuth2RestTemplate client = new OAuth2RestTemplate(resource, context); client.setRequestFactory(new SimpleClientHttpRequestFactory() { @Override protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException { super.prepareConnection(connection, httpMethod); connection.setInstanceFollowRedirects(false); } }); client.setErrorHandler(new OAuth2ErrorHandler(client.getResource()) { // Pass errors through in response entity for status code analysis @Override public boolean hasError(ClientHttpResponse response) throws IOException { return false; } @Override public void handleError(ClientHttpResponse response) throws IOException { } }); List<HttpMessageConverter<?>> list = new ArrayList<HttpMessageConverter<?>>(); list.add(new StringHttpMessageConverter()); list.add(new MappingJackson2HttpMessageConverter()); client.setMessageConverters(list); return client; } }