diff --git a/model/src/main/java/org/cloudfoundry/identity/uaa/provider/ExternalIdentityProviderDefinition.java b/model/src/main/java/org/cloudfoundry/identity/uaa/provider/ExternalIdentityProviderDefinition.java index 1d59aa68fac..cfa09bb1dee 100644 --- a/model/src/main/java/org/cloudfoundry/identity/uaa/provider/ExternalIdentityProviderDefinition.java +++ b/model/src/main/java/org/cloudfoundry/identity/uaa/provider/ExternalIdentityProviderDefinition.java @@ -3,13 +3,14 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import java.util.Collections; -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; + /******************************************************************************* * Cloud Foundry * Copyright (c) [2009-2015] Pivotal Software, Inc. All Rights Reserved. @@ -88,7 +89,7 @@ public boolean equals(Object o) { ExternalIdentityProviderDefinition that = (ExternalIdentityProviderDefinition) o; if (addShadowUserOnLogin != that.addShadowUserOnLogin) return false; - if(this.areCustomAttributesStored() != that.areCustomAttributesStored()) return false; + if(this.isStoreCustomAttributes() != that.isStoreCustomAttributes()) return false; if (getExternalGroupsWhitelist() != null ? !getExternalGroupsWhitelist().equals(that.getExternalGroupsWhitelist()) : that.getExternalGroupsWhitelist() != null) return false; return attributeMappings != null ? attributeMappings.equals(that.attributeMappings) : that.attributeMappings == null; @@ -103,7 +104,7 @@ public int hashCode() { return result; } - public boolean areCustomAttributesStored() { + public boolean isStoreCustomAttributes() { return storeCustomAttributes; } diff --git a/model/src/main/java/org/cloudfoundry/identity/uaa/provider/SamlIdentityProviderDefinition.java b/model/src/main/java/org/cloudfoundry/identity/uaa/provider/SamlIdentityProviderDefinition.java index 954aab36f39..573f33d0c8c 100644 --- a/model/src/main/java/org/cloudfoundry/identity/uaa/provider/SamlIdentityProviderDefinition.java +++ b/model/src/main/java/org/cloudfoundry/identity/uaa/provider/SamlIdentityProviderDefinition.java @@ -81,6 +81,7 @@ public SamlIdentityProviderDefinition clone() { def.setGroupMappingMode(getGroupMappingMode()); def.setSocketFactoryClassName(getSocketFactoryClassName()); def.setSkipSslValidation(isSkipSslValidation()); + def.setStoreCustomAttributes(isStoreCustomAttributes()); return def; } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/account/UserInfoEndpoint.java b/server/src/main/java/org/cloudfoundry/identity/uaa/account/UserInfoEndpoint.java index 6d37a57de66..a698547a265 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/account/UserInfoEndpoint.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/account/UserInfoEndpoint.java @@ -78,7 +78,9 @@ protected UserInfoResponse getResponse(UaaPrincipal principal, boolean addCustom // TODO: other attributes if (addCustomAttributes) { UserInfo info = userDatabase.getUserInfo(user.getId()); - response.setAttributeValue(USER_ATTRIBUTES, info); + if (info!=null && info.size()>0) { + response.setAttributeValue(USER_ATTRIBUTES, info); + } } return response; } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/manager/ExternalLoginAuthenticationManager.java b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/manager/ExternalLoginAuthenticationManager.java index 6d32cd2191a..6a9070dd430 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/manager/ExternalLoginAuthenticationManager.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/manager/ExternalLoginAuthenticationManager.java @@ -167,7 +167,7 @@ protected void populateAuthenticationAttributes(UaaAuthentication authentication authentication.getAuthenticationMethods().add("ext"); if (authentication.getUserAttributes()!=null && authentication.getUserAttributes().size()>0 && getProviderProvisioning()!=null) { IdentityProvider provider = getProviderProvisioning().retrieveByOrigin(getOrigin(), IdentityZoneHolder.get().getId()); - if (provider.getConfig()!=null && provider.getConfig().areCustomAttributesStored()) { + if (provider.getConfig()!=null && provider.getConfig().isStoreCustomAttributes()) { logger.debug("Storing custom attributes for user_id:"+authentication.getPrincipal().getId()); getUserDatabase().storeUserInfo(authentication.getPrincipal().getId(), new UserInfo(authentication.getUserAttributes())); } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/LoginSamlAuthenticationProvider.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/LoginSamlAuthenticationProvider.java index e5957c431d7..8816d3005c2 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/LoginSamlAuthenticationProvider.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/LoginSamlAuthenticationProvider.java @@ -32,6 +32,7 @@ import org.cloudfoundry.identity.uaa.user.UaaUser; import org.cloudfoundry.identity.uaa.user.UaaUserDatabase; import org.cloudfoundry.identity.uaa.user.UaaUserPrototype; +import org.cloudfoundry.identity.uaa.user.UserInfo; import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.joda.time.DateTime; @@ -161,7 +162,11 @@ public Authentication authenticate(Authentication authentication) throws Authent MultiValueMap userAttributes = retrieveUserAttributes(samlConfig, (SAMLCredential) result.getCredentials()); UaaUser user = createIfMissing(samlPrincipal, addNew, authorities, userAttributes); UaaPrincipal principal = new UaaPrincipal(user); - return new LoginSamlAuthenticationToken(principal, result).getUaaAuthentication(user.getAuthorities(), filteredExternalGroups, userAttributes); + UaaAuthentication resultUaaAuthentication = new LoginSamlAuthenticationToken(principal, result).getUaaAuthentication(user.getAuthorities(), filteredExternalGroups, userAttributes); + if (samlConfig.isStoreCustomAttributes()) { + userDatabase.storeUserInfo(user.getId(), new UserInfo(resultUaaAuthentication.getUserAttributes())); + } + return resultUaaAuthentication; } protected ExpiringUsernameAuthenticationToken getExpiringUsernameAuthenticationToken(Authentication authentication) { diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/user/JdbcUaaUserDatabase.java b/server/src/main/java/org/cloudfoundry/identity/uaa/user/JdbcUaaUserDatabase.java index d4218f89550..2aaba9f086b 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/user/JdbcUaaUserDatabase.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/user/JdbcUaaUserDatabase.java @@ -13,6 +13,8 @@ package org.cloudfoundry.identity.uaa.user; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.cloudfoundry.identity.uaa.util.JsonUtils; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.springframework.dao.EmptyResultDataAccessException; @@ -43,6 +45,8 @@ */ public class JdbcUaaUserDatabase implements UaaUserDatabase { + private static Log logger = LogFactory.getLog(JdbcUaaUserDatabase.class); + public static final String USER_FIELDS = "id,username,password,email,givenName,familyName,created,lastModified,authorities,origin,external_id,verified,identity_zone_id,salt,passwd_lastmodified,phoneNumber,legacy_verification_behavior,passwd_change_required "; public static final String PRE_DEFAULT_USER_BY_USERNAME_QUERY = "select " + USER_FIELDS + "from users where %s = ? and active=? and origin=? and identity_zone_id=?"; @@ -130,7 +134,12 @@ else if(results.size() == 1) { @Override public UserInfo getUserInfo(String id) { - return jdbcTemplate.queryForObject("select user_id, info from user_info where user_id = ?", userInfoMapper, id); + try { + return jdbcTemplate.queryForObject("select user_id, info from user_info where user_id = ?", userInfoMapper, id); + } catch (EmptyResultDataAccessException e) { + logger.debug("No custom attributes stored for user:"+id); + return null; + } } @Override @@ -139,10 +148,15 @@ public UserInfo storeUserInfo(String id, UserInfo info) { throw new NullPointerException("id is a required field"); } final String insertUserInfoSQL = "insert into user_info(user_id, info) values (?,?)"; + final String updateUserInfoSQL = "update user_info set info = ? where user_id = ?"; if (info == null) { info = new UserInfo(); } - jdbcTemplate.update(insertUserInfoSQL, id, JsonUtils.writeValueAsString(info)); + String json = JsonUtils.writeValueAsString(info); + int count = jdbcTemplate.update(updateUserInfoSQL, json, id); + if (count == 0) { + jdbcTemplate.update(insertUserInfoSQL, id, json); + } return getUserInfo(id); } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/SamlIdentityProviderDefinitionTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/SamlIdentityProviderDefinitionTests.java index d8034343fde..48de9c52375 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/SamlIdentityProviderDefinitionTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/SamlIdentityProviderDefinitionTests.java @@ -57,7 +57,7 @@ public void testEquals() { public void test_serialize_custom_attributes_field() { definition.setStoreCustomAttributes(true); SamlIdentityProviderDefinition def = JsonUtils.readValue(JsonUtils.writeValueAsString(definition), SamlIdentityProviderDefinition.class); - assertTrue(def.areCustomAttributesStored()); + assertTrue(def.isStoreCustomAttributes()); } @Test diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/user/JdbcUaaUserDatabaseTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/user/JdbcUaaUserDatabaseTests.java index 283ca5a9f5f..de5ebf7da51 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/user/JdbcUaaUserDatabaseTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/user/JdbcUaaUserDatabaseTests.java @@ -150,6 +150,11 @@ public void testStoreUserInfo() { db.storeUserInfo(id, info); UserInfo info2 = db.getUserInfo(id); assertEquals(info, info2); + + info.put("new","value"); + db.storeUserInfo(id, info); + UserInfo info3 = db.getUserInfo(id); + assertEquals(info, info3); } @Test diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/SamlLoginIT.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/SamlLoginIT.java index ea3b196079d..27aaa89f1b8 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/SamlLoginIT.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/SamlLoginIT.java @@ -14,6 +14,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import org.cloudfoundry.identity.uaa.ServerRunning; +import org.cloudfoundry.identity.uaa.account.UserInfoResponse; import org.cloudfoundry.identity.uaa.constants.OriginKeys; import org.cloudfoundry.identity.uaa.integration.util.IntegrationTestUtils; import org.cloudfoundry.identity.uaa.integration.util.ScreenshotOnFail; @@ -75,6 +76,7 @@ import static org.cloudfoundry.identity.uaa.integration.util.IntegrationTestUtils.createSimplePHPSamlIDP; import static org.cloudfoundry.identity.uaa.integration.util.IntegrationTestUtils.doesSupportZoneDNS; import static org.cloudfoundry.identity.uaa.integration.util.IntegrationTestUtils.getZoneAdminToken; +import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.USER_ATTRIBUTES; import static org.cloudfoundry.identity.uaa.provider.ExternalIdentityProviderDefinition.EMAIL_ATTRIBUTE_NAME; import static org.cloudfoundry.identity.uaa.provider.ExternalIdentityProviderDefinition.GROUP_ATTRIBUTE_NAME; import static org.cloudfoundry.identity.uaa.provider.ExternalIdentityProviderDefinition.USER_ATTRIBUTE_PREFIX; @@ -797,6 +799,7 @@ public void testSamlLogin_Custom_User_Attributes_In_ID_Token() throws Exception "secr3T"); SamlIdentityProviderDefinition samlIdentityProviderDefinition = createTestZone1IDP("simplesamlphp"); + samlIdentityProviderDefinition.setStoreCustomAttributes(true); samlIdentityProviderDefinition.addAttributeMapping(USER_ATTRIBUTE_PREFIX+COST_CENTERS, COST_CENTER); samlIdentityProviderDefinition.addAttributeMapping(USER_ATTRIBUTE_PREFIX+MANAGERS, MANAGER); @@ -865,8 +868,8 @@ public void testSamlLogin_Custom_User_Attributes_In_ID_Token() throws Exception Jwt idTokenClaims = JwtHelper.decode(idToken); Map claims = JsonUtils.readValue(idTokenClaims.getClaims(), new TypeReference>() {}); - assertNotNull(claims.get(ClaimConstants.USER_ATTRIBUTES)); - Map> userAttributes = (Map>) claims.get(ClaimConstants.USER_ATTRIBUTES); + assertNotNull(claims.get(USER_ATTRIBUTES)); + Map> userAttributes = (Map>) claims.get(USER_ATTRIBUTES); assertThat(userAttributes.get(COST_CENTERS), containsInAnyOrder(DENVER_CO)); assertThat(userAttributes.get(MANAGERS), containsInAnyOrder(JOHN_THE_SLOTH, KARI_THE_ANT_EATER)); @@ -874,6 +877,15 @@ public void testSamlLogin_Custom_User_Attributes_In_ID_Token() throws Exception Map acr = (Map) claims.get(ClaimConstants.ACR); assertNotNull("acr claim should contain values attribute", acr.get("values")); assertThat((List) acr.get("values"), containsInAnyOrder(AuthnContext.PASSWORD_AUTHN_CTX)); + + UserInfoResponse userInfo = IntegrationTestUtils.getUserInfo(zoneUrl, authCodeTokenResponse.get("access_token")); + + Map> userAttributeMap = (Map>) userInfo.getAttributeValue(USER_ATTRIBUTES); + List costCenterData = userAttributeMap.get(COST_CENTERS); + List managerData = userAttributeMap.get(MANAGERS); + assertThat(costCenterData, containsInAnyOrder(DENVER_CO)); + assertThat(managerData, containsInAnyOrder(JOHN_THE_SLOTH, KARI_THE_ANT_EATER)); + } @Test @@ -977,7 +989,7 @@ public void testSamlLogin_Email_In_ID_Token_When_UserID_IsNotEmail() throws Exce Jwt idTokenClaims = JwtHelper.decode(idToken); Map claims = JsonUtils.readValue(idTokenClaims.getClaims(), new TypeReference>() {}); - assertNotNull(claims.get(ClaimConstants.USER_ATTRIBUTES)); + assertNotNull(claims.get(USER_ATTRIBUTES)); assertEquals("marissa6", claims.get(ClaimConstants.USER_NAME)); assertEquals("marissa6@test.org", claims.get(ClaimConstants.EMAIL)); } diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/util/IntegrationTestUtils.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/util/IntegrationTestUtils.java index 2063a13f6e7..abf46d1f0e0 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/util/IntegrationTestUtils.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/util/IntegrationTestUtils.java @@ -19,6 +19,7 @@ import org.apache.http.client.HttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.cloudfoundry.identity.uaa.ServerRunning; +import org.cloudfoundry.identity.uaa.account.UserInfoResponse; import org.cloudfoundry.identity.uaa.constants.OriginKeys; import org.cloudfoundry.identity.uaa.provider.AbstractXOAuthIdentityProviderDefinition; import org.cloudfoundry.identity.uaa.provider.IdentityProvider; @@ -49,6 +50,7 @@ import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; @@ -75,6 +77,7 @@ import java.io.IOException; import java.net.Inet4Address; import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.net.UnknownHostException; import java.util.Arrays; @@ -91,11 +94,15 @@ import static org.cloudfoundry.identity.uaa.provider.ExternalIdentityProviderDefinition.USER_NAME_ATTRIBUTE_NAME; import static org.cloudfoundry.identity.uaa.security.web.CookieBasedCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME; +import static org.cloudfoundry.identity.uaa.util.UaaHttpRequestUtils.createRequestFactory; import static org.hamcrest.core.StringStartsWith.startsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.springframework.http.HttpHeaders.ACCEPT; +import static org.springframework.http.HttpHeaders.AUTHORIZATION; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static org.springframework.security.oauth2.common.util.OAuth2Utils.USER_OAUTH_APPROVAL; public class IntegrationTestUtils { @@ -120,6 +127,15 @@ public static ScimUser createUnapprovedUser(ServerRunning serverRunning) throws return user; } + public static UserInfoResponse getUserInfo(String url, String token) throws URISyntaxException { + RestTemplate rest = new RestTemplate(createRequestFactory(true)); + MultiValueMap headers = new LinkedMultiValueMap<>(); + headers.add(AUTHORIZATION, "Bearer "+token); + headers.add(ACCEPT, APPLICATION_JSON_VALUE); + RequestEntity request = new RequestEntity<>(headers, HttpMethod.GET, new URI(url+"/userinfo")); + return rest.exchange(request, UserInfoResponse.class).getBody(); + } + public static class RegexMatcher extends TypeSafeMatcher { private final String regex; @@ -217,9 +233,9 @@ public static ScimUser createUser(RestTemplate client, public static ScimUser updateUser(String token, String url, ScimUser user) { RestTemplate template = new RestTemplate(); MultiValueMap headers = new LinkedMultiValueMap<>(); - headers.add("Accept", MediaType.APPLICATION_JSON_VALUE); + headers.add("Accept", APPLICATION_JSON_VALUE); headers.add("Authorization", "bearer " + token); - headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE); + headers.add("Content-Type", APPLICATION_JSON_VALUE); headers.add("If-Match", String.valueOf(user.getVersion())); HttpEntity getHeaders = new HttpEntity(user,headers); ResponseEntity userInfoGet = template.exchange( @@ -242,9 +258,9 @@ public static ScimUser getUser(String token, String url, String origin, String u public static ScimUser getUser(String token, String url, String userId) { RestTemplate template = new RestTemplate(); MultiValueMap headers = new LinkedMultiValueMap<>(); - headers.add("Accept", MediaType.APPLICATION_JSON_VALUE); + headers.add("Accept", APPLICATION_JSON_VALUE); headers.add("Authorization", "bearer " + token); - headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE); + headers.add("Content-Type", APPLICATION_JSON_VALUE); HttpEntity getHeaders = new HttpEntity(headers); ResponseEntity userInfoGet = template.exchange( url+"/Users/"+userId, @@ -264,9 +280,9 @@ public static String getUserId(String token, String url, String origin, String u public static String getUserIdByField(String token, String url, String origin, String field, String fieldValue) { RestTemplate template = new RestTemplate(); MultiValueMap headers = new LinkedMultiValueMap<>(); - headers.add("Accept", MediaType.APPLICATION_JSON_VALUE); + headers.add("Accept", APPLICATION_JSON_VALUE); headers.add("Authorization", "bearer " + token); - headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE); + headers.add("Content-Type", APPLICATION_JSON_VALUE); HttpEntity getHeaders = new HttpEntity(headers); ResponseEntity userInfoGet = template.exchange( url+"/Users" @@ -297,9 +313,9 @@ public static void deleteUser(String zoneAdminToken, String url, String userId) RestTemplate template = new RestTemplate(); MultiValueMap headers = new LinkedMultiValueMap<>(); - headers.add("Accept", MediaType.APPLICATION_JSON_VALUE); + headers.add("Accept", APPLICATION_JSON_VALUE); headers.add("Authorization", "bearer " + zoneAdminToken); - headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE); + headers.add("Content-Type", APPLICATION_JSON_VALUE); HttpEntity deleteHeaders = new HttpEntity(headers); ResponseEntity userDelete = template.exchange( url + "/Users/" + userId, @@ -380,9 +396,9 @@ public static ScimGroup getGroup(String token, String displayName) { RestTemplate template = new RestTemplate(); MultiValueMap headers = new LinkedMultiValueMap<>(); - headers.add("Accept", MediaType.APPLICATION_JSON_VALUE); + headers.add("Accept", APPLICATION_JSON_VALUE); headers.add("Authorization", "bearer " + token); - headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE); + headers.add("Content-Type", APPLICATION_JSON_VALUE); if (StringUtils.hasText(zoneId)) { headers.add(IdentityZoneSwitchingFilter.HEADER, zoneId); } @@ -407,9 +423,9 @@ public static ScimGroup createGroup(String token, RestTemplate template = new RestTemplate(); template.setErrorHandler(fiveHundredErrorHandler); MultiValueMap headers = new LinkedMultiValueMap<>(); - headers.add("Accept", MediaType.APPLICATION_JSON_VALUE); + headers.add("Accept", APPLICATION_JSON_VALUE); headers.add("Authorization", "bearer " + token); - headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE); + headers.add("Content-Type", APPLICATION_JSON_VALUE); if (StringUtils.hasText(zoneId)) { headers.add(IdentityZoneSwitchingFilter.HEADER, zoneId); } @@ -429,10 +445,10 @@ public static ScimGroup updateGroup(String token, ScimGroup group) { RestTemplate template = new RestTemplate(); MultiValueMap headers = new LinkedMultiValueMap<>(); - headers.add("Accept", MediaType.APPLICATION_JSON_VALUE); + headers.add("Accept", APPLICATION_JSON_VALUE); headers.add("Authorization", "bearer " + token); headers.add("If-Match", "*"); - headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE); + headers.add("Content-Type", APPLICATION_JSON_VALUE); if (StringUtils.hasText(zoneId)) { headers.add(IdentityZoneSwitchingFilter.HEADER, zoneId); } @@ -469,9 +485,9 @@ public static ScimGroupExternalMember mapExternalGroup(String token, RestTemplate template = new RestTemplate(); MultiValueMap headers = new LinkedMultiValueMap<>(); - headers.add("Accept", MediaType.APPLICATION_JSON_VALUE); + headers.add("Accept", APPLICATION_JSON_VALUE); headers.add("Authorization", "bearer " + token); - headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE); + headers.add("Content-Type", APPLICATION_JSON_VALUE); if (StringUtils.hasText(zoneId)) { headers.add(IdentityZoneSwitchingFilter.HEADER, zoneId); } @@ -561,9 +577,9 @@ public static BaseClientDetails createClientAsZoneAdmin(String zoneAdminToken, RestTemplate template = new RestTemplate(); MultiValueMap headers = new LinkedMultiValueMap<>(); - headers.add("Accept", MediaType.APPLICATION_JSON_VALUE); + headers.add("Accept", APPLICATION_JSON_VALUE); headers.add("Authorization", "bearer "+zoneAdminToken); - headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE); + headers.add("Content-Type", APPLICATION_JSON_VALUE); headers.add(IdentityZoneSwitchingFilter.HEADER, zoneId); HttpEntity getHeaders = new HttpEntity(JsonUtils.writeValueAsBytes(client), headers); ResponseEntity clientCreate = template.exchange( @@ -596,9 +612,9 @@ protected boolean hasError(HttpStatus statusCode) { } }); MultiValueMap headers = new LinkedMultiValueMap<>(); - headers.add("Accept", MediaType.APPLICATION_JSON_VALUE); + headers.add("Accept", APPLICATION_JSON_VALUE); headers.add("Authorization", "bearer "+ adminToken); - headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE); + headers.add("Content-Type", APPLICATION_JSON_VALUE); if (StringUtils.hasText(switchToZoneId)) { headers.add(IdentityZoneSwitchingFilter.HEADER, switchToZoneId); } @@ -662,9 +678,9 @@ public static List getProviders(String zoneAdminToken, String zoneId) { RestTemplate client = new RestTemplate(); MultiValueMap headers = new LinkedMultiValueMap<>(); - headers.add("Accept", MediaType.APPLICATION_JSON_VALUE); + headers.add("Accept", APPLICATION_JSON_VALUE); headers.add("Authorization", "bearer " + zoneAdminToken); - headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE); + headers.add("Content-Type", APPLICATION_JSON_VALUE); headers.add(IdentityZoneSwitchingFilter.HEADER, zoneId); HttpEntity getHeaders = new HttpEntity(headers); ResponseEntity providerGet = client.exchange( @@ -859,9 +875,9 @@ public static IdentityProvider createOrUpdateProvider(String accessToken, IdentityProvider provider) { RestTemplate client = new RestTemplate(); MultiValueMap headers = new LinkedMultiValueMap<>(); - headers.add("Accept", MediaType.APPLICATION_JSON_VALUE); + headers.add("Accept", APPLICATION_JSON_VALUE); headers.add("Authorization", "bearer "+accessToken); - headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE); + headers.add("Content-Type", APPLICATION_JSON_VALUE); headers.add(IdentityZoneSwitchingFilter.HEADER, provider.getIdentityZoneId()); List existing = getProviders(accessToken, url, provider.getIdentityZoneId()); if (existing!=null) { diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/LoginSamlAuthenticationProviderTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/LoginSamlAuthenticationProviderTests.java index e6be5ab0355..dd41e573dab 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/LoginSamlAuthenticationProviderTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/LoginSamlAuthenticationProviderTests.java @@ -35,6 +35,7 @@ import org.cloudfoundry.identity.uaa.user.JdbcUaaUserDatabase; import org.cloudfoundry.identity.uaa.user.UaaAuthority; import org.cloudfoundry.identity.uaa.user.UaaUser; +import org.cloudfoundry.identity.uaa.user.UserInfo; import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.joda.time.DateTime; import org.junit.Before; @@ -91,6 +92,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -543,6 +545,39 @@ public void shadowAccount_createdWith_MappedUserAttributes() throws Exception { assertEquals("1234567890", user.getPhoneNumber()); } + @Test + public void custom_user_attributes_stored_if_configured() throws Exception { + Map attributeMappings = new HashMap<>(); + attributeMappings.put("given_name", "firstName"); + attributeMappings.put("family_name", "lastName"); + attributeMappings.put("email", "emailAddress"); + attributeMappings.put("phone_number", "phone"); + attributeMappings.put(USER_ATTRIBUTE_PREFIX+"secondary_email","emailAddress"); + providerDefinition.setAttributeMappings(attributeMappings); + provider.setConfig(providerDefinition); + provider = providerProvisioning.update(provider); + + UaaAuthentication authentication = getAuthentication(); + UaaUser user = userDatabase.retrieveUserByName("marissa-saml", OriginKeys.SAML); + assertEquals("Marissa", user.getGivenName()); + assertEquals("Bloggs", user.getFamilyName()); + assertEquals("marissa.bloggs@test.com", user.getEmail()); + assertEquals("1234567890", user.getPhoneNumber()); + assertEquals("marissa.bloggs@test.com", authentication.getUserAttributes().getFirst("secondary_email")); + + UserInfo userInfo = userDatabase.getUserInfo(user.getId()); + assertNull(userInfo); + + providerDefinition.setStoreCustomAttributes(true); + provider.setConfig(providerDefinition); + provider = providerProvisioning.update(provider); + authentication = getAuthentication(); + assertEquals("marissa.bloggs@test.com", authentication.getUserAttributes().getFirst("secondary_email")); + userInfo = userDatabase.getUserInfo(user.getId()); + assertNotNull(userInfo); + assertEquals("marissa.bloggs@test.com", userInfo.getFirst("secondary_email")); + } + @Test public void shadowAccountNotCreated_givenShadowAccountCreationDisabled() throws Exception { Map attributeMappings = new HashMap<>();