diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/OIDCLoginIT.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/OIDCLoginIT.java index ffcc58e69f2..8c4aa5c5d98 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/OIDCLoginIT.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/OIDCLoginIT.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; @@ -41,21 +42,26 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.security.jwt.Jwt; import org.springframework.security.oauth2.client.test.TestAccounts; +import org.springframework.security.oauth2.common.util.RandomValueStringGenerator; +import org.springframework.security.oauth2.provider.client.BaseClientDetails; import org.springframework.test.context.ContextConfiguration; import org.springframework.web.client.RestOperations; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import static org.cloudfoundry.identity.uaa.constants.OriginKeys.OIDC10; 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.USER_NAME_ATTRIBUTE_NAME; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.startsWith; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; @@ -173,7 +179,7 @@ public void successfulLoginWithOIDC_and_SAML_Provider() throws Exception { This test creates an OIDC provider. That provider in turn has a SAML provider. The end user is authenticated using */ - createOIDCProviderWithRequestedScopes(); + String clientId = createOIDCProviderWithRequestedScopes(); webDriver.get(baseUrl + "/login"); webDriver.findElement(By.linkText("My OIDC Provider")).click(); Assert.assertThat(webDriver.getCurrentUrl(), Matchers.containsString("oidc10.identity.cf-app.com")); @@ -190,10 +196,11 @@ public void successfulLoginWithOIDC_and_SAML_Provider() throws Exception { Cookie cookie= webDriver.manage().getCookieNamed("JSESSIONID"); System.out.println("cookie = " + String.format("%s=%s",cookie.getName(), cookie.getValue())); + Map authCodeTokenResponse = IntegrationTestUtils.getAuthorizationCodeTokenMap(serverRunning, UaaTestAccounts.standard(serverRunning), - "login", - "loginsecret", + clientId, + "secret", null, null, "token id_token", @@ -212,6 +219,14 @@ public void successfulLoginWithOIDC_and_SAML_Provider() 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(baseUrl, authCodeTokenResponse.get("access_token")); + + Map> userAttributeMap = (Map>) userInfo.getAttributeValue(USER_ATTRIBUTES); + assertNotNull(userAttributeMap); + List clientIds = userAttributeMap.get("the_client_id"); + assertNotNull(clientIds); + assertEquals("identity", clientIds.get(0)); } @Test @@ -297,18 +312,20 @@ private IdentityProvider createAzureProvider() t return result; } - private void createOIDCProviderWithRequestedScopes() throws Exception { - createOIDCProviderWithRequestedScopes(null, "https://oidc10.identity.cf-app.com"); + private String createOIDCProviderWithRequestedScopes() throws Exception { + return createOIDCProviderWithRequestedScopes(null, "https://oidc10.identity.cf-app.com"); } - private void createOIDCProviderWithRequestedScopes(String issuer, final String urlBase) throws Exception { - createOIDCProviderWithRequestedScopes(issuer, urlBase, null); + private String createOIDCProviderWithRequestedScopes(String issuer, final String urlBase) throws Exception { + return createOIDCProviderWithRequestedScopes(issuer, urlBase, null); } - private void createOIDCProviderWithRequestedScopes(String issuer, String urlBase, String keyUrl) throws Exception { + private String createOIDCProviderWithRequestedScopes(String issuer, String urlBase, String keyUrl) throws Exception { IdentityProvider identityProvider = new IdentityProvider<>(); identityProvider.setName("my oidc provider"); identityProvider.setIdentityZoneId(OriginKeys.UAA); OIDCIdentityProviderDefinition config = new OIDCIdentityProviderDefinition(); config.addAttributeMapping(USER_NAME_ATTRIBUTE_NAME, "user_name"); + config.addAttributeMapping("user.attribute." + "the_client_id", "cid"); + config.setStoreCustomAttributes(true); config.setAuthUrl(new URL(urlBase + "/oauth/authorize")); config.setTokenUrl(new URL(urlBase + "/oauth/token")); config.setTokenKeyUrl(keyUrl==null ? new URL(urlBase + "/token_key") : new URL(keyUrl)); @@ -327,5 +344,13 @@ private void createOIDCProviderWithRequestedScopes(String issuer, String urlBase String clientCredentialsToken = IntegrationTestUtils.getClientCredentialsToken(baseUrl, "admin", "adminsecret"); IntegrationTestUtils.createOrUpdateProvider(clientCredentialsToken, baseUrl, identityProvider); originKey = "puppy"; + + BaseClientDetails clientDetails = new BaseClientDetails(new RandomValueStringGenerator().generate(), null, "openid,user_attributes", "authorization_code,client_credentials", "uaa.admin,scim.read,scim.write,uaa.resource"); + clientDetails.setClientSecret("secret"); + clientDetails.setAutoApproveScopes(Collections.singleton("true")); + clientDetails = IntegrationTestUtils.createClientAsZoneAdmin(clientCredentialsToken, baseUrl, null, clientDetails); + clientDetails.setClientSecret("secret"); + return clientDetails.getClientId(); + } } diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/providers/IdentityProviderEndpointsDocs.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/providers/IdentityProviderEndpointsDocs.java index a3e53f7a46a..55e2b7bae41 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/providers/IdentityProviderEndpointsDocs.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/providers/IdentityProviderEndpointsDocs.java @@ -98,6 +98,7 @@ public class IdentityProviderEndpointsDocs extends InjectedMockContextTest { private static final String PHONE_NUMBER_DESC = "Map `phone_number` to the attribute for phone number in the provider assertion."; private static final String GIVEN_NAME_DESC = "Map `given_name` to the attribute for given name in the provider assertion."; + private static final FieldDescriptor STORE_CUSTOM_ATTRIBUTES = fieldWithPath("config.storeCustomAttributes").optional(false).type(BOOLEAN).description("Set to true, to store custom user attributes to be fetched from the /userinfo endpoint"); private static final FieldDescriptor SKIP_SSL_VALIDATION = fieldWithPath("config.skipSslValidation").optional(false).type(BOOLEAN).description("Set to true, to skip SSL validation when fetching metadata."); private static final FieldDescriptor ATTRIBUTE_MAPPING = fieldWithPath("config.attributeMappings").optional(null).type(STRING).description("Map external attribute to UAA recognized mappings."); private static final FieldDescriptor ADD_SHADOW_USER = fieldWithPath("config.addShadowUserOnLogin").optional(true).description("Whether users should be allowed to authenticate from LDAP without having a user pre-populated in the users database"); @@ -130,7 +131,8 @@ public class IdentityProviderEndpointsDocs extends InjectedMockContextTest { PROVIDER_DESC, EMAIL_DOMAIN, ACTIVE, - ADD_SHADOW_USER + ADD_SHADOW_USER, + STORE_CUSTOM_ATTRIBUTES }; private static ApacheDsSSLContainer apacheDS; @@ -348,6 +350,7 @@ public void createSAMLIdentityProvider() throws Exception { fieldWithPath("type").required().description("`saml`"), fieldWithPath("originKey").required().description("A unique alias for the SAML provider"), SKIP_SSL_VALIDATION, + STORE_CUSTOM_ATTRIBUTES, fieldWithPath("config.metaDataLocation").required().type(STRING).description("SAML Metadata - either an XML string or a URL that will deliver XML content"), fieldWithPath("config.nameID").optional(null).type(STRING).description("The name ID to use for the username, default is \"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\"."), fieldWithPath("config.assertionConsumerIndex").optional(null).type(NUMBER).description("SAML assertion consumer index, default is 0"),