diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlAuthenticationFilterConfig.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlAuthenticationFilterConfig.java index b538cd12a2b..184c4af47ca 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlAuthenticationFilterConfig.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlAuthenticationFilterConfig.java @@ -30,6 +30,9 @@ @Configuration public class SamlAuthenticationFilterConfig { + /** + * Handles building and forwarding the SAML2 Authentication Request to the IDP. + */ @Autowired @Bean Filter saml2WebSsoAuthenticationRequestFilter(RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) { @@ -80,6 +83,9 @@ AuthenticationProvider samlAuthenticationProvider(IdentityZoneManager identityZo return samlResponseAuthenticationProvider; } + /** + * Handles the SAML2 Authentication Response and creates an Authentication object. + */ @Autowired @Bean Filter saml2WebSsoAuthenticationFilter(AuthenticationProvider samlAuthenticationProvider, diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml4AuthenticationProviderTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml4AuthenticationProviderTests.java index 318fac54cb6..f7e325bd782 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml4AuthenticationProviderTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml4AuthenticationProviderTests.java @@ -330,7 +330,7 @@ void authenticationContainsAmr() { } @Test - void test_external_groups_as_scopes() { + void externalGroupsAsScopes() { providerDefinition.setGroupMappingMode(SamlIdentityProviderDefinition.ExternalGroupMappingMode.AS_SCOPES); providerDefinition.addAttributeMapping(GROUP_ATTRIBUTE_NAME, Arrays.asList("2ndgroups", "groups")); provider.setConfig(providerDefinition); @@ -346,7 +346,7 @@ void test_external_groups_as_scopes() { } @Test - void test_group_mapping() { + void groupMapping() { providerDefinition.addAttributeMapping(GROUP_ATTRIBUTE_NAME, "groups"); provider.setConfig(providerDefinition); providerProvisioning.update(provider, identityZoneManager.getCurrentIdentityZone().getId()); @@ -359,7 +359,7 @@ void test_group_mapping() { } @Test - void test_non_string_attributes() { + void nonStringAttributes() { providerDefinition.addAttributeMapping(USER_ATTRIBUTE_PREFIX + "XSURI", "XSURI"); providerDefinition.addAttributeMapping(USER_ATTRIBUTE_PREFIX + "XSAny", "XSAny"); providerDefinition.addAttributeMapping(USER_ATTRIBUTE_PREFIX + "XSQName", "XSQName"); @@ -438,8 +438,8 @@ void addExternalGroupsToAuthenticationWithWildcardWhitelist() { } @Test - @Disabled("SAML test doesn't compile") - void update_invitedUser_whose_username_is_notEmail() throws Exception { + @Disabled("SAML test doesn't compile: Invitations. Requires different response data") + void updateInvitedUserWhoseUsernameIsNotEmail() throws Exception { ScimUser scimUser = getInvitedUser(); // SAMLCredential credential = getUserCredential("marissa-invited", "Marissa-invited", null, "marissa.invited@test.org", null); @@ -455,8 +455,8 @@ void update_invitedUser_whose_username_is_notEmail() throws Exception { } @Test - @Disabled("SAML test doesn't compile") - void invitedUser_authentication_whenAuthenticatedEmailDoesNotMatchInvitedEmail() + @Disabled("SAML test doesn't compile: Invitations. Requires different response data") + void invitedUserAuthenticationWhenAuthenticatedEmailDoesNotMatchInvitedEmail() throws Exception { Map attributeMappings = new HashMap<>(); attributeMappings.put("email", "emailAddress"); 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 f0a3b08f19b..3a11ee6800b 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 @@ -1,10 +1,10 @@ /******************************************************************************* * 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 @@ -203,7 +203,7 @@ void samlSPMetadata() { ResponseEntity response = request.getForEntity( baseUrl + "/saml/metadata", String.class); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); - String metadataXml = (String) response.getBody(); + String metadataXml = response.getBody(); // The SAML SP metadata should match the following UAA configs: // login.entityID @@ -264,7 +264,7 @@ void simpleSamlLoginWithAddShadowUserOnLoginFalse() throws Exception { // create a UAA user with the email address as the username. deleteUser(SAML_ORIGIN, testAccounts.getEmail()); - IdentityProvider provider = IntegrationTestUtils.createIdentityProvider(SAML_ORIGIN, false, baseUrl, serverRunning); + IdentityProvider provider = IntegrationTestUtils.createIdentityProvider(SAML_ORIGIN, false, baseUrl, serverRunning); String clientId = "app-addnew-false" + new RandomValueStringGenerator().generate(); String redirectUri = "http://nosuchhostname:0/nosuchendpoint"; createClientAndSpecifyProvider(clientId, provider, redirectUri); @@ -278,7 +278,7 @@ void simpleSamlLoginWithAddShadowUserOnLoginFalse() throws Exception { } @Test - @Disabled("SAML test fails") + @Disabled("SAML test fails: Requires zones") void incorrectResponseFromSamlIDP_showErrorFromSaml() { String zoneId = "testzone3"; String zoneUrl = baseUrl.replace("localhost", zoneId + ".localhost"); @@ -311,7 +311,7 @@ void incorrectResponseFromSamlIDP_showErrorFromSaml() { "secr3T"); SamlIdentityProviderDefinition samlIdentityProviderDefinition = createSimplePHPSamlIDP(SAML_ORIGIN, "testzone3"); - IdentityProvider provider = new IdentityProvider(); + IdentityProvider provider = new IdentityProvider<>(); provider.setIdentityZoneId(zoneId); provider.setType(OriginKeys.SAML); provider.setActive(true); @@ -343,10 +343,11 @@ void simpleSamlPhpLogin() throws Exception { } @Test - @Disabled("SAML test fails") + @Disabled("SAML test fails: requires LogoutRequest to be sent to the IDP") void simpleSamlPhpLoginDisplaysLastLogin() throws Exception { + createIdentityProvider(SAML_ORIGIN); + Long beforeTest = System.currentTimeMillis(); - IdentityProvider provider = createIdentityProvider(SAML_ORIGIN); LoginPage.go(webDriver, baseUrl) .clickSamlLink_goesToSamlLoginPage(SAML_ORIGIN) .login_goesToHomePage(testAccounts.getUserName(), testAccounts.getPassword()) @@ -362,9 +363,9 @@ void simpleSamlPhpLoginDisplaysLastLogin() throws Exception { } @Test - @Disabled("SAML test fails") + @Disabled("SAML test fails: Requires logout") void singleLogout() throws Exception { - IdentityProvider provider = createIdentityProvider(SAML_ORIGIN); + createIdentityProvider(SAML_ORIGIN); LoginPage.go(webDriver, baseUrl) .clickSamlLink_goesToSamlLoginPage(SAML_ORIGIN) @@ -374,7 +375,7 @@ void singleLogout() throws Exception { } @Test - @Disabled("SAML test fails") + @Disabled("SAML test fails: Requires zones and logout") void singleLogoutWithNoLogoutUrlOnIDPWithLogoutRedirect() { String zoneId = "testzone2"; String zoneUrl = baseUrl.replace("localhost", zoneId + ".localhost"); @@ -410,7 +411,7 @@ void singleLogoutWithNoLogoutUrlOnIDPWithLogoutRedirect() { email, "secr3T"); SamlIdentityProviderDefinition providerDefinition = createIDPWithNoSLOSConfigured(); - IdentityProvider provider = new IdentityProvider(); + IdentityProvider provider = new IdentityProvider<>(); provider.setIdentityZoneId(zoneId); provider.setType(OriginKeys.SAML); provider.setActive(true); @@ -435,10 +436,10 @@ void singleLogoutWithNoLogoutUrlOnIDPWithLogoutRedirect() { } @Test - @Disabled("SAML test fails") + @Disabled("SAML test fails: Requires logout") void singleLogoutWithNoLogoutUrlOnIDP() throws Exception { SamlIdentityProviderDefinition providerDefinition = createIDPWithNoSLOSConfigured(); - IdentityProvider provider = new IdentityProvider(); + IdentityProvider provider = new IdentityProvider<>(); provider.setIdentityZoneId(OriginKeys.UAA); provider.setType(OriginKeys.SAML); provider.setActive(true); @@ -447,8 +448,7 @@ void singleLogoutWithNoLogoutUrlOnIDP() throws Exception { provider.setName("simplesamlphp for uaa"); String zoneAdminToken = getZoneAdminToken(baseUrl, serverRunning); - - provider = IntegrationTestUtils.createOrUpdateProvider(zoneAdminToken, baseUrl, provider); + IntegrationTestUtils.createOrUpdateProvider(zoneAdminToken, baseUrl, provider); LoginPage.go(webDriver, baseUrl) .clickSamlLink_goesToSamlLoginPage("simplesamlphp") @@ -474,23 +474,6 @@ void faviconShouldNotSave() throws Exception { .login_goesToHomePage(MARISSA4_USERNAME, MARISSA4_PASSWORD); } - - private void testSimpleSamlLogin(String firstUrl, String lookfor) throws Exception { - testSimpleSamlLogin(firstUrl, lookfor, testAccounts.getUserName(), testAccounts.getPassword()); - } - - private void testSimpleSamlLogin(String firstUrl, String lookfor, String username, String password) throws Exception { - IdentityProvider provider = createIdentityProvider(SAML_ORIGIN); - - webDriver.get(baseUrl + firstUrl); - assertThat(webDriver.getTitle()).isEqualTo("Cloud Foundry"); - webDriver.findElement(By.xpath("//a[text()='" + provider.getConfig().getLinkText() + "']")).click(); - //takeScreenShot(); - assertThat(webDriver.getCurrentUrl()).contains("loginuserpass"); - sendCredentials(username, password); - assertThat(webDriver.findElement(By.cssSelector("h1")).getText()).contains(lookfor); - } - protected IdentityProvider createIdentityProvider(String originKey) throws Exception { return IntegrationTestUtils.createIdentityProvider(originKey, true, baseUrl, serverRunning); } @@ -498,7 +481,7 @@ protected IdentityProvider createIdentityProvide protected UaaClientDetails createClientAndSpecifyProvider(String clientId, IdentityProvider provider, String redirectUri) { - RestTemplate identityClient = IntegrationTestUtils.getClientCredentialsTemplate( + IntegrationTestUtils.getClientCredentialsTemplate( IntegrationTestUtils.getClientCredentialsResource(baseUrl, new String[0], "identity", "identitysecret") ); RestTemplate adminClient = IntegrationTestUtils.getClientCredentialsTemplate( @@ -529,7 +512,6 @@ protected UaaClientDetails createClientAndSpecifyProvider(String clientId, Ident } protected void deleteUser(String origin, String username) { - String zoneAdminToken = IntegrationTestUtils.getClientCredentialsToken(serverRunning, "admin", "adminsecret"); @@ -542,8 +524,8 @@ protected void deleteUser(String origin, String username) { } @Test - @Disabled("SAML test fails") - void saml_invitation_automatic_redirect_in_zone2() throws Exception { + @Disabled("SAML test fails: Requires zones") + void saml_invitation_automatic_redirect_in_zone2() { perform_SamlInvitation_Automatic_Redirect_In_Zone2(MARISSA2_USERNAME, MARISSA2_PASSWORD, true); perform_SamlInvitation_Automatic_Redirect_In_Zone2(MARISSA2_USERNAME, MARISSA2_PASSWORD, true); perform_SamlInvitation_Automatic_Redirect_In_Zone2(MARISSA2_USERNAME, MARISSA2_PASSWORD, true); @@ -643,7 +625,7 @@ public void perform_SamlInvitation_Automatic_Redirect_In_Zone2(String username, } @Test - @Disabled("SAML test fails") + @Disabled("SAML test fails: Requires zones") void relay_state_redirect_from_idp() { //ensure we are able to resolve DNS for hostname testzone1.localhost String zoneId = "testzone1"; @@ -706,7 +688,7 @@ void relay_state_redirect_from_idp() { } @Test - @Disabled("SAML test fails") + @Disabled("SAML test fails: Requires zones") void samlLoginClientIDPAuthorizationAutomaticRedirectInZone1() { //ensure we are able to resolve DNS for hostname testzone1.localhost String zoneId = "testzone1"; @@ -758,7 +740,7 @@ void samlLoginClientIDPAuthorizationAutomaticRedirectInZone1() { clientDetails.setClientSecret("secret"); clientDetails.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, idps); clientDetails.setAutoApproveScopes(Collections.singleton("true")); - clientDetails = IntegrationTestUtils.createClientAsZoneAdmin(zoneAdminToken, baseUrl, zoneId, clientDetails); + IntegrationTestUtils.createClientAsZoneAdmin(zoneAdminToken, baseUrl, zoneId, clientDetails); webDriver.get(zoneUrl + "/logout.do"); @@ -774,7 +756,7 @@ void samlLoginClientIDPAuthorizationAutomaticRedirectInZone1() { } @Test - @Disabled("SAML test fails") + @Disabled("SAML test fails: Requires zones and logout") void samlLoginMapGroupsInZone1() { //ensure we are able to resolve DNS for hostname testzone1.localhost String zoneId = "testzone1"; @@ -835,7 +817,6 @@ void samlLoginMapGroupsInZone1() { clientDetails = IntegrationTestUtils.createClientAsZoneAdmin(zoneAdminToken, baseUrl, zoneId, clientDetails); String adminTokenInZone = IntegrationTestUtils.getClientCredentialsToken(zoneUrl, clientDetails.getClientId(), "secret"); - ScimGroup uaaSamlUserGroup = new ScimGroup(null, "uaa.saml.user", zoneId); uaaSamlUserGroup = IntegrationTestUtils.createOrUpdateGroup(adminTokenInZone, null, zoneUrl, uaaSamlUserGroup); @@ -878,7 +859,7 @@ void samlLoginMapGroupsInZone1() { } @Test - @Disabled("SAML test fails") + @Disabled("SAML test fails: Requires zones and logout") void samlLoginCustomUserAttributesAndRolesInIDToken() throws Exception { final String COST_CENTER = "costCenter"; @@ -1029,9 +1010,8 @@ void samlLoginCustomUserAttributesAndRolesInIDToken() throws Exception { assertThat(userInfoRoles).containsExactlyInAnyOrder(expectedRoles); } - // TODO: work on this next @Test - @Disabled("SAML test fails") + @Disabled("SAML test fails: Requires zones and logout") void samlLoginEmailInIDTokenWhenUserIDIsNotEmail() { //ensure we are able to resolve DNS for hostname testzone1.localhost @@ -1069,8 +1049,7 @@ void samlLoginEmailInIDTokenWhenUserIDIsNotEmail() { SamlIdentityProviderDefinition samlIdentityProviderDefinition = createTestZoneIDP(SAML_ORIGIN, zoneId); samlIdentityProviderDefinition.addAttributeMapping(EMAIL_ATTRIBUTE_NAME, "emailAddress"); - IdentityProvider provider = new IdentityProvider(); - provider.setIdentityZoneId(zoneId); + IdentityProvider provider = new IdentityProvider<>(); provider.setType(OriginKeys.SAML); provider.setActive(true); provider.setConfig(samlIdentityProviderDefinition); @@ -1091,7 +1070,7 @@ void samlLoginEmailInIDTokenWhenUserIDIsNotEmail() { clientDetails = IntegrationTestUtils.createClientAsZoneAdmin(zoneAdminToken, baseUrl, zoneId, clientDetails); clientDetails.setClientSecret("secret"); - String adminTokenInZone = IntegrationTestUtils.getClientCredentialsToken(zoneUrl, clientDetails.getClientId(), "secret"); + IntegrationTestUtils.getClientCredentialsToken(zoneUrl, clientDetails.getClientId(), "secret"); webDriver.get(zoneUrl + "/logout.do"); @@ -1140,7 +1119,7 @@ void samlLoginEmailInIDTokenWhenUserIDIsNotEmail() { @Test - @Disabled("SAML test fails") + @Disabled("SAML test fails: Requires zones and logout") void simpleSamlPhpLoginInTestZone1Works() { String zoneId = "testzone1"; @@ -1159,7 +1138,6 @@ void simpleSamlPhpLoginInTestZone1Works() { String groupId = IntegrationTestUtils.findGroupId(adminClient, baseUrl, "zones." + zoneId + ".admin"); IntegrationTestUtils.addMemberToGroup(adminClient, baseUrl, user.getId(), groupId); - String zoneAdminToken = IntegrationTestUtils.getAccessTokenByAuthCode(serverRunning, UaaTestAccounts.standard(serverRunning), @@ -1177,7 +1155,6 @@ void simpleSamlPhpLoginInTestZone1Works() { provider.setOriginKey(samlIdentityProviderDefinition.getIdpEntityAlias()); provider.setName("simplesamlphp for testzone1"); - provider = IntegrationTestUtils.createOrUpdateProvider(zoneAdminToken, baseUrl, provider); //we have to create two providers to avoid automatic redirect @@ -1191,7 +1168,7 @@ void simpleSamlPhpLoginInTestZone1Works() { provider1.setConfig(samlIdentityProviderDefinition1); provider1.setOriginKey(samlIdentityProviderDefinition1.getIdpEntityAlias()); provider1.setName("simplesamlphp 1 for testzone1"); - provider1 = IntegrationTestUtils.createOrUpdateProvider(zoneAdminToken, baseUrl, provider1); + IntegrationTestUtils.createOrUpdateProvider(zoneAdminToken, baseUrl, provider1); assertThat(provider.getId()).isNotNull(); @@ -1287,7 +1264,7 @@ void loginSamlOnlyProviderNoUsernamePassword() throws Exception { } @Test - @Disabled("SAML test fails") + @Disabled("SAML test fails: Requires logout") void samlLoginClientIDPAuthorizationAutomaticRedirect() throws Exception { IdentityProvider provider = createIdentityProvider(SAML_ORIGIN); assertThat(provider.getConfig().getIdpEntityAlias()).isEqualTo(provider.getOriginKey()); @@ -1334,7 +1311,7 @@ void loginClientIDPAuthorizationAlreadyLoggedIn() { } @Test - @Disabled("SAML test fails") + @Disabled("SAML test fails: Requires logout") void springSamlEndpointsWithEmptyContext() throws IOException { CallEmpptyPageAndCheckHttpStatusCode("/saml/discovery", 200); CallEmpptyPageAndCheckHttpStatusCode("/saml/SingleLogout", 400); @@ -1394,19 +1371,6 @@ private SamlIdentityProviderDefinition createIDPWithNoSLOSConfigured() { return def; } - private void logout() { - webDriver.findElement(By.cssSelector(".dropdown-trigger")).click(); - webDriver.findElement(By.linkText("Sign Out")).click(); - } - - private void login(IdentityProvider provider) { - webDriver.get(baseUrl + "/login"); - assertThat(webDriver.getTitle()).isEqualTo("Cloud Foundry"); - webDriver.findElement(By.xpath("//a[text()='" + provider.getConfig().getLinkText() + "']")).click(); - webDriver.findElement(By.xpath(SIMPLESAMLPHP_LOGIN_PROMPT_XPATH_EXPR)); - sendCredentials(testAccounts.getUserName(), testAccounts.getPassword()); - } - private void sendCredentials(String username, String password, By loginButtonSelector) { webDriver.findElement(By.name("username")).clear(); webDriver.findElement(By.name("username")).sendKeys(username); 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 d9719e89c9e..0bb48fdf577 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 @@ -25,6 +25,7 @@ import org.cloudfoundry.identity.uaa.oauth.common.util.RandomValueStringGenerator; import org.cloudfoundry.identity.uaa.oauth.jwt.JwtClientAuthentication; import org.cloudfoundry.identity.uaa.provider.AbstractExternalOAuthIdentityProviderDefinition; +import org.cloudfoundry.identity.uaa.provider.AbstractIdentityProviderDefinition; import org.cloudfoundry.identity.uaa.provider.IdentityProvider; import org.cloudfoundry.identity.uaa.provider.OIDCIdentityProviderDefinition; import org.cloudfoundry.identity.uaa.provider.SamlIdentityProviderDefinition; @@ -992,9 +993,11 @@ public static SamlIdentityProviderDefinition createSimplePHPSamlIDP(String alias return def; } - public static IdentityProvider createOrUpdateProvider(String accessToken, - String url, - IdentityProvider provider) { + public static IdentityProvider + createOrUpdateProvider(String accessToken, + String url, + IdentityProvider provider) { + RestTemplate client = new RestTemplate(); MultiValueMap headers = new LinkedMultiValueMap<>(); headers.add("Accept", APPLICATION_JSON_VALUE);