diff --git a/common/src/test/java/org/cloudfoundry/identity/uaa/authentication/login/LoginInfoEndpointTest.java b/common/src/test/java/org/cloudfoundry/identity/uaa/authentication/login/LoginInfoEndpointTest.java index 3c5d358a23f..2e55320042f 100644 --- a/common/src/test/java/org/cloudfoundry/identity/uaa/authentication/login/LoginInfoEndpointTest.java +++ b/common/src/test/java/org/cloudfoundry/identity/uaa/authentication/login/LoginInfoEndpointTest.java @@ -37,6 +37,13 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyCollection; +import static org.mockito.Matchers.anyList; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -116,7 +123,7 @@ public void testFilterIdpsForDefaultZone() throws Exception { // mock IdentityProviderConfigurator List idps = getIdps(); IdentityProviderConfigurator mockIDPConfigurator = mock(IdentityProviderConfigurator.class); - when(mockIDPConfigurator.getIdentityProviderDefinitionsForZone(IdentityZoneHolder.get())).thenReturn(idps); + when(mockIDPConfigurator.getIdentityProviderDefinitions((List) isNull(), eq(IdentityZone.getUaa()), eq(false))).thenReturn(idps); LoginInfoEndpoint endpoint = getEndpoint(); endpoint.setIdpDefinitions(mockIDPConfigurator); @@ -141,7 +148,7 @@ public void testFilterIdpsWithNoSavedRequest() throws Exception { // mock IdentityProviderConfigurator List idps = getIdps(); IdentityProviderConfigurator mockIDPConfigurator = mock(IdentityProviderConfigurator.class); - when(mockIDPConfigurator.getIdentityProviderDefinitionsForZone(IdentityZoneHolder.get())).thenReturn(idps); + when(mockIDPConfigurator.getIdentityProviderDefinitions((List) isNull(), eq(IdentityZone.getUaa()),eq(false))).thenReturn(idps); LoginInfoEndpoint endpoint = getEndpoint(); endpoint.setIdpDefinitions(mockIDPConfigurator); @@ -166,18 +173,19 @@ public void testFilterIDPsForAuthcodeClientInDefaultZone() throws Exception { // mock session and saved request MockHttpServletRequest request = getMockHttpServletRequest(); - List allowedProviders = Arrays.asList("my-client-awesome-idp"); + List allowedProviders = Arrays.asList("my-client-awesome-idp1", "my-client-awesome-idp2"); // mock Client service BaseClientDetails clientDetails = new BaseClientDetails(); clientDetails.setClientId("client-id"); - clientDetails.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, Arrays.asList("my-client-awesome-idp")); + clientDetails.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, new LinkedList<>(allowedProviders)); ClientDetailsService clientDetailsService = mock(ClientDetailsService.class); when(clientDetailsService.loadClientByClientId("client-id")).thenReturn(clientDetails); // mock IdentityProviderConfigurator List clientIDPs = new LinkedList<>(); - clientIDPs.add(createIdentityProviderDefinition("my-client-awesome-idp", "uaa")); + clientIDPs.add(createIdentityProviderDefinition("my-client-awesome-idp1", "uaa")); + clientIDPs.add(createIdentityProviderDefinition("my-client-awesome-idp2", "uaa")); IdentityProviderConfigurator mockIDPConfigurator = mock(IdentityProviderConfigurator.class); when(mockIDPConfigurator.getIdentityProviderDefinitions(allowedProviders, IdentityZoneHolder.get(), false)).thenReturn(clientIDPs); @@ -188,10 +196,49 @@ public void testFilterIDPsForAuthcodeClientInDefaultZone() throws Exception { endpoint.loginForHtml(model, null, request); List idpDefinitions = (List) model.asMap().get("idpDefinitions"); - assertEquals(1, idpDefinitions.size()); + assertEquals(2, idpDefinitions.size()); IdentityProviderDefinition clientIdp = idpDefinitions.iterator().next(); - assertEquals("my-client-awesome-idp", clientIdp.getIdpEntityAlias()); + assertEquals("my-client-awesome-idp1", clientIdp.getIdpEntityAlias()); + assertEquals(true, clientIdp.isShowSamlLink()); + } + + @Test + public void testFilterIDPsForAuthcodeClientInOtherZone() throws Exception { + // mock session and saved request + MockHttpServletRequest request = getMockHttpServletRequest(); + + IdentityZone zone = MultitenancyFixture.identityZone("other-zone", "other-zone"); + IdentityZoneHolder.set(zone); + + List allowedProviders = Arrays.asList("my-client-awesome-idp1", "my-client-awesome-idp2"); + + // mock Client service + BaseClientDetails clientDetails = new BaseClientDetails(); + clientDetails.setClientId("client-id"); + clientDetails.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, new LinkedList<>(allowedProviders)); + ClientDetailsService clientDetailsService = mock(ClientDetailsService.class); + when(clientDetailsService.loadClientByClientId("client-id")).thenReturn(clientDetails); + + // mock IdentityProviderConfigurator + List clientIDPs = new LinkedList<>(); + clientIDPs.add(createIdentityProviderDefinition("my-client-awesome-idp1", "uaa")); + clientIDPs.add(createIdentityProviderDefinition("my-client-awesome-idp2", "uaa")); + IdentityProviderConfigurator mockIDPConfigurator = mock(IdentityProviderConfigurator.class); + when(mockIDPConfigurator.getIdentityProviderDefinitions(eq(allowedProviders), eq(zone), eq(true))).thenReturn(clientIDPs); + + + LoginInfoEndpoint endpoint = getEndpoint(); + endpoint.setClientDetailsService(clientDetailsService); + endpoint.setIdpDefinitions(mockIDPConfigurator); + Model model = new ExtendedModelMap(); + endpoint.loginForHtml(model, null, request); + + List idpDefinitions = (List) model.asMap().get("idpDefinitions"); + assertEquals(2, idpDefinitions.size()); + + IdentityProviderDefinition clientIdp = idpDefinitions.iterator().next(); + assertEquals("my-client-awesome-idp1", clientIdp.getIdpEntityAlias()); assertEquals(true, clientIdp.isShowSamlLink()); } @@ -243,10 +290,8 @@ private LoginInfoEndpoint getEndpoint() { private List getIdps() { List idps = new LinkedList<>(); - idps.add(createIdentityProviderDefinition("awesome-idp", "uaa")); idps.add(createIdentityProviderDefinition("my-client-awesome-idp", "uaa")); - return idps; } 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 0f3231f2e7d..cc94793985b 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 @@ -32,9 +32,11 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.openqa.selenium.By; +import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.OutputType; import org.openqa.selenium.TakesScreenshot; import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebDriverException; import org.openqa.selenium.WebElement; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -52,6 +54,7 @@ import java.io.File; import java.io.IOException; import java.net.Inet4Address; +import java.net.URLEncoder; import java.net.UnknownHostException; import java.util.Arrays; import java.util.List; @@ -62,6 +65,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static org.springframework.http.MediaType.TEXT_HTML_VALUE; @@ -98,23 +102,8 @@ public void clearWebDriverOfCookies() throws Exception { webDriver.manage().deleteAllCookies(); webDriver.get(baseUrl.replace("localhost", "testzone2.localhost") + "/logout.do"); webDriver.manage().deleteAllCookies(); - } - - @Test - @Ignore //this requires some SAML providers to be preconfigured - public void testSamlVariations() throws Exception { - assumeTrue("Expected testzone1/2.localhost to resolve to 127.0.0.1", doesSupportZoneDNS()); - webDriver.get(baseUrl + "/login"); - Assert.assertEquals("Cloud Foundry", webDriver.getTitle()); - webDriver.findElement(By.name("username")); - webDriver.findElement(By.name("password")); - webDriver.findElement(By.xpath("//a[text()='Okta Preview 1']")); - webDriver.findElement(By.xpath("//a[text()='Okta Preview 2']")); - webDriver.findElement(By.xpath("//a[text()='Log in with OpenAM']")); - webDriver.findElement(By.xpath("//a[text()='Log in with vCenter SSO']")); - webDriver.findElement(By.xpath("//a[text()='Log in with Simple SAML PHP']")); - webDriver.findElement(By.xpath("//input[@value='Sign in']")); - Assert.assertEquals(3, webDriver.findElements(By.xpath("//input")).size()); + webDriver.get("http://simplesamlphp.cfapps.io/module.php/core/authenticate.php?as=example-userpass&logout"); + webDriver.get("http://simplesamlphp2.cfapps.io/module.php/core/authenticate.php?as=example-userpass&logout"); } @Test @@ -156,11 +145,12 @@ public void testSimpleSamlPhpLogin() throws Exception { testSimpleSamlLogin("/login", "Where to?"); } public void testSimpleSamlLogin(String firstUrl, String lookfor) throws Exception { - IdentityProvider provider = createIdentityProvider(); + IdentityProvider provider = createIdentityProvider("simplesamlphp"); + IdentityProvider provider2 = createIdentityProvider("simplesamlphp2"); //tells us that we are on travis assumeTrue("Expected testzone1/2.localhost to resolve to 127.0.0.1", doesSupportZoneDNS()); - webDriver.get("http://simplesamlphp.cfapps.io/module.php/core/authenticate.php?as=example-userpass&logout"); + webDriver.get(baseUrl + firstUrl); Assert.assertEquals("Cloud Foundry", webDriver.getTitle()); webDriver.findElement(By.xpath("//a[text()='"+provider.getConfigValue(IdentityProviderDefinition.class).getLinkText()+"']")).click(); @@ -173,7 +163,7 @@ public void testSimpleSamlLogin(String firstUrl, String lookfor) throws Exceptio assertThat(webDriver.findElement(By.cssSelector("h1")).getText(), Matchers.containsString(lookfor)); } - protected IdentityProvider createIdentityProvider() throws Exception { + protected IdentityProvider createIdentityProvider(String originKey) throws Exception { RestTemplate identityClient = IntegrationTestUtils.getClientCredentialsTempate( IntegrationTestUtils.getClientCredentialsResource(baseUrl, new String[0], "identity", "identitysecret") ); @@ -192,7 +182,7 @@ protected IdentityProvider createIdentityProvider() throws Exception { email, "secret"); - IdentityProviderDefinition identityProviderDefinition = createSimplePHPSamlIDP(Origin.UAA); + IdentityProviderDefinition identityProviderDefinition = createSimplePHPSamlIDP(originKey, Origin.UAA); IdentityProvider provider = new IdentityProvider(); provider.setIdentityZoneId(Origin.UAA); provider.setType(Origin.SAML); @@ -208,7 +198,6 @@ protected IdentityProvider createIdentityProvider() throws Exception { @Test public void testSimpleSamlPhpLoginInTestZone1Works() throws Exception { assumeTrue("Expected testzone1/2.localhost to resolve to 127.0.0.1", doesSupportZoneDNS()); - webDriver.get("http://simplesamlphp.cfapps.io/module.php/core/authenticate.php?as=example-userpass&logout"); String zoneId = "testzone1"; RestTemplate identityClient = IntegrationTestUtils.getClientCredentialsTempate( @@ -231,7 +220,7 @@ public void testSimpleSamlPhpLoginInTestZone1Works() throws Exception { email, "secret"); - IdentityProviderDefinition identityProviderDefinition = createTestZone1IDP(); + IdentityProviderDefinition identityProviderDefinition = createTestZone1IDP("simplesamlphp"); IdentityProvider provider = new IdentityProvider(); provider.setIdentityZoneId(zoneId); provider.setType(Origin.SAML); @@ -239,7 +228,22 @@ public void testSimpleSamlPhpLoginInTestZone1Works() throws Exception { provider.setConfig(JsonUtils.writeValueAsString(identityProviderDefinition)); provider.setOriginKey(identityProviderDefinition.getIdpEntityAlias()); provider.setName("simplesamlphp for testzone1"); + + provider = IntegrationTestUtils.createOrUpdateProvider(zoneAdminToken,baseUrl,provider); + + //we have to create two providers to avoid automatic redirect + IdentityProviderDefinition identityProviderDefinition1 = identityProviderDefinition.clone(); + identityProviderDefinition1.setIdpEntityAlias(identityProviderDefinition.getIdpEntityAlias()+"-1"); + IdentityProvider provider1 = new IdentityProvider(); + provider1.setIdentityZoneId(zoneId); + provider1.setType(Origin.SAML); + provider1.setActive(true); + provider1.setConfig(JsonUtils.writeValueAsString(identityProviderDefinition1)); + provider1.setOriginKey(identityProviderDefinition1.getIdpEntityAlias()); + provider1.setName("simplesamlphp 1 for testzone1"); + provider1 = IntegrationTestUtils.createOrUpdateProvider(zoneAdminToken,baseUrl,provider1); + assertNotNull(provider.getId()); String testZone1Url = baseUrl.replace("localhost", zoneId+".localhost"); @@ -247,7 +251,14 @@ public void testSimpleSamlPhpLoginInTestZone1Works() throws Exception { webDriver.get(testZone1Url + "/logout.do"); webDriver.get(testZone1Url + "/login"); Assert.assertEquals("Cloud Foundry", webDriver.getTitle()); - WebElement element = webDriver.findElement(By.xpath("//a[text()='" + identityProviderDefinition.getLinkText() + "']")); + + List elements = webDriver.findElements(By.xpath("//a[text()='"+identityProviderDefinition.getLinkText()+"']")); + assertNotNull(elements); + assertEquals(2, elements.size()); + + WebElement element = webDriver.findElement(By.xpath("//a[text()='" + identityProviderDefinition1.getLinkText() + "']")); + assertNotNull(element); + element = webDriver.findElement(By.xpath("//a[text()='" + identityProviderDefinition.getLinkText() + "']")); String loginUrl = element.getAttribute("href"); element.click(); webDriver.findElement(By.xpath("//h2[contains(text(), 'Enter your username and password')]")); @@ -267,10 +278,11 @@ public void testSimpleSamlPhpLoginInTestZone1Works() throws Exception { assertNotNull(provider.getId()); webDriver.get(testZone1Url + "/login"); Assert.assertEquals("Cloud Foundry", webDriver.getTitle()); - List elements = webDriver.findElements(By.xpath("//a[text()='"+identityProviderDefinition.getLinkText()+"']")); + elements = webDriver.findElements(By.xpath("//a[text()='"+identityProviderDefinition.getLinkText()+"']")); assertNotNull(elements); - assertEquals(0, elements.size()); + assertEquals(1, elements.size()); + //enable the provider provider.setActive(true); provider = IntegrationTestUtils.createOrUpdateProvider(zoneAdminToken,baseUrl,provider); assertNotNull(provider.getId()); @@ -278,14 +290,18 @@ public void testSimpleSamlPhpLoginInTestZone1Works() throws Exception { Assert.assertEquals("Cloud Foundry", webDriver.getTitle()); elements = webDriver.findElements(By.xpath("//a[text()='"+identityProviderDefinition.getLinkText()+"']")); assertNotNull(elements); - assertEquals(1, elements.size()); + assertEquals(2, elements.size()); } @Test public void testLoginPageShowsIDPsForAuthcodeClient() throws Exception { - IdentityProvider provider = createIdentityProvider(); - List idps = Arrays.asList(provider.getConfigValue(IdentityProviderDefinition.class).getIdpEntityAlias()); + IdentityProvider provider = createIdentityProvider("simplesamlphp"); + IdentityProvider provider2 = createIdentityProvider("simplesamlphp2"); + List idps = Arrays.asList( + provider.getConfigValue(IdentityProviderDefinition.class).getIdpEntityAlias(), + provider2.getConfigValue(IdentityProviderDefinition.class).getIdpEntityAlias() + ); String adminAccessToken = testClient.getOAuthAccessToken("admin", "adminsecret", "client_credentials", "clients.read clients.write clients.secret"); @@ -297,32 +313,60 @@ public void testLoginPageShowsIDPsForAuthcodeClient() throws Exception { testClient.createClient(adminAccessToken, clientDetails); webDriver.get(baseUrl + "/oauth/authorize?client_id=" + clientId + "&redirect_uri=http%3A%2F%2Flocalhost%3A8888%2Flogin&response_type=code&state=8tp0tR"); - assertEquals(1, webDriver.findElements(By.className("saml-login-link")).size()); - webDriver.findElement(By.xpath("//a[text()='"+provider.getConfigValue(IdentityProviderDefinition.class).getLinkText()+"']")); + webDriver.findElement(By.xpath("//a[text()='" + provider.getConfigValue(IdentityProviderDefinition.class).getLinkText() + "']")); + webDriver.findElement(By.xpath("//a[text()='" + provider2.getConfigValue(IdentityProviderDefinition.class).getLinkText()+"']")); } @Test - public void testLoginClientIDPAuthorization() throws Exception { - IdentityProvider provider = createIdentityProvider(); - List idps = Arrays.asList(provider.getConfigValue(IdentityProviderDefinition.class).getIdpEntityAlias()); + public void testLoginSamlOnlyProviderNoUsernamePassword() throws Exception { + IdentityProvider provider = createIdentityProvider("simplesamlphp"); + IdentityProvider provider2 = createIdentityProvider("simplesamlphp2"); + List idps = Arrays.asList(provider.getOriginKey(), provider2.getOriginKey()); webDriver.get(baseUrl + "/logout.do"); String adminAccessToken = testClient.getOAuthAccessToken("admin", "adminsecret", "client_credentials", "clients.read clients.write clients.secret"); String clientId = UUID.randomUUID().toString(); - BaseClientDetails clientDetails = new BaseClientDetails(clientId, null, "openid", "authorization_code", "uaa.none", "http://localhost:8080/login"); + BaseClientDetails clientDetails = new BaseClientDetails(clientId, null, "openid", "authorization_code", "uaa.none", "http://localhost:8080/uaa/login"); clientDetails.setClientSecret("secret"); clientDetails.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, idps); - testClient.createClient(adminAccessToken, clientDetails); + webDriver.get(baseUrl + "/oauth/authorize?client_id=" + clientId + "&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fuaa%3Alogin&response_type=code&state=8tp0tR"); + try { + webDriver.findElement(By.name("username")); + fail("Element username should not be present"); + } catch (NoSuchElementException x) {} + try { + webDriver.findElement(By.name("password")); + fail("Element username should not be present"); + } catch (NoSuchElementException x) {} + webDriver.get(baseUrl + "/logout.do"); + } - webDriver.get(baseUrl + "/oauth/authorize?client_id=" + clientId + "&redirect_uri=http%3A%2F%2Flocalhost%3A8888%2Flogin&response_type=code&state=8tp0tR"); + @Test + public void testSamlLoginClientIDPAuthorizationAutomaticRedirect() throws Exception { + IdentityProvider provider = createIdentityProvider("simplesamlphp"); + assertEquals(provider.getOriginKey(), provider.getConfigValue(IdentityProviderDefinition.class).getIdpEntityAlias()); + List idps = Arrays.asList(provider.getOriginKey()); + webDriver.get(baseUrl + "/logout.do"); + String adminAccessToken = testClient.getOAuthAccessToken("admin", "adminsecret", "client_credentials", "clients.read clients.write clients.secret"); + + String clientId = UUID.randomUUID().toString(); + BaseClientDetails clientDetails = new BaseClientDetails(clientId, null, "openid", "authorization_code", "uaa.none", baseUrl); + clientDetails.setClientSecret("secret"); + clientDetails.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, idps); + clientDetails.addAdditionalInformation(ClientConstants.AUTO_APPROVE, true); + testClient.createClient(adminAccessToken, clientDetails); + + webDriver.get(baseUrl + "/oauth/authorize?client_id=" + clientId + "&redirect_uri=" + URLEncoder.encode(baseUrl) + "&response_type=code&state=8tp0tR"); + //we should now be in the Simple SAML PHP site + webDriver.findElement(By.xpath("//h2[contains(text(), 'Enter your username and password')]")); webDriver.findElement(By.name("username")).clear(); webDriver.findElement(By.name("username")).sendKeys(testAccounts.getUserName()); webDriver.findElement(By.name("password")).sendKeys(testAccounts.getPassword()); - webDriver.findElement(By.xpath("//input[@value='Sign in']")).click(); + webDriver.findElement(By.xpath("//input[@value='Login']")).click(); - assertThat(webDriver.findElement(By.cssSelector("p")).getText(), Matchers.containsString("The application is not authorized for your account.")); + assertThat(webDriver.findElement(By.cssSelector("h1")).getText(), Matchers.containsString("Where to?")); webDriver.get(baseUrl + "/logout.do"); } @@ -360,12 +404,15 @@ protected boolean doesSupportZoneDNS() { } } - public IdentityProviderDefinition createTestZone1IDP() { - return createSimplePHPSamlIDP("testzone1"); + public IdentityProviderDefinition createTestZone1IDP(String alias) { + return createSimplePHPSamlIDP(alias, "testzone1"); } - public IdentityProviderDefinition createSimplePHPSamlIDP(String zoneId) { + public IdentityProviderDefinition createSimplePHPSamlIDP(String alias, String zoneId) { + if (!("simplesamlphp".equals(alias) || "simplesamlphp2".equals(alias))) { + throw new IllegalArgumentException("Only valid origins are: simplesamlphp,simplesamlphp2"); + } String idpMetaData = "\n" + - "\n" + + "\n" + " \n" + " \n" + " begl1WVCsXSn7iHixtWPP8d/X+k=BmbKqA3A0oSLcn5jImz/l5WbpVXj+8JIpT/ENWjOjSd/gcAsZm1QvYg+RxYPBk+iV2bBxD+/yAE/w0wibsHrl0u9eDhoMRUJBUSmeyuN1lYzBuoVa08PdAGtb5cGm4DMQT5Rzakb1P0hhEPPEDDHgTTxop89LUu6xx97t2Q03Khy8mXEmBmNt2NlFxJPNt0FwHqLKOHRKBOE/+BpswlBocjOQKFsI9tG3TyjFC68mM2jo0fpUQCgj5ZfhzolvS7z7c6V201d9Tqig0/mMFFJLTN8WuZPavw22AJlMjsDY9my+4R9HKhK5U53DhcTeECs9fb4gd7p5BJy4vVp7tqqOg==\n" + @@ -385,9 +432,9 @@ public IdentityProviderDefinition createSimplePHPSamlIDP(String zoneId) { " \n" + " \n" + " \n" + - " \n" + + " \n" + " urn:oasis:names:tc:SAML:2.0:nameid-format:transient\n" + - " \n" + + " \n" + " \n" + " \n" + " Filip\n" + @@ -402,8 +449,8 @@ public IdentityProviderDefinition createSimplePHPSamlIDP(String zoneId) { def.setAssertionConsumerIndex(0); def.setMetadataTrustCheck(false); def.setShowSamlLink(true); - def.setIdpEntityAlias("simplesamlphp"); - def.setLinkText("Login with Simple SAML PHP"); + def.setIdpEntityAlias(alias); + def.setLinkText("Login with Simple SAML PHP("+alias+")"); return def; } diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/LoginMockMvcTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/LoginMockMvcTests.java index 0aed2b4bc6e..bccb78b266a 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/LoginMockMvcTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/LoginMockMvcTests.java @@ -13,28 +13,13 @@ package org.cloudfoundry.identity.uaa.login; -import java.util.Arrays; -import java.util.List; - -import static org.hamcrest.Matchers.hasEntry; -import static org.hamcrest.Matchers.hasKey; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.nullValue; -import static org.springframework.http.MediaType.APPLICATION_JSON; -import static org.springframework.http.MediaType.TEXT_HTML; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.xpath; - import com.googlecode.flyway.core.Flyway; import org.cloudfoundry.identity.uaa.TestClassNullifier; import org.cloudfoundry.identity.uaa.authentication.Origin; import org.cloudfoundry.identity.uaa.authentication.UaaPrincipal; import org.cloudfoundry.identity.uaa.authentication.login.LoginInfoEndpoint; import org.cloudfoundry.identity.uaa.authentication.login.Prompt; +import org.cloudfoundry.identity.uaa.client.ClientConstants; import org.cloudfoundry.identity.uaa.login.saml.IdentityProviderDefinition; import org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils; import org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.IdentityZoneCreationResult; @@ -53,6 +38,7 @@ import org.junit.BeforeClass; import org.junit.Test; import org.springframework.mock.env.MockEnvironment; +import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpSession; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContext; @@ -61,6 +47,9 @@ import org.springframework.security.oauth2.common.util.RandomValueStringGenerator; import org.springframework.security.oauth2.provider.client.BaseClientDetails; import org.springframework.security.web.FilterChainProxy; +import org.springframework.security.web.PortResolverImpl; +import org.springframework.security.web.savedrequest.DefaultSavedRequest; +import org.springframework.security.web.savedrequest.SavedRequest; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -68,6 +57,29 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.support.XmlWebApplicationContext; +import javax.servlet.http.Cookie; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.http.MediaType.TEXT_HTML; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.xpath; + public class LoginMockMvcTests extends TestClassNullifier { private static MockEnvironment mockEnvironment = new MockEnvironment(); @@ -353,6 +365,59 @@ public void testSamlLoginLinksShowActiveProviders() throws Exception { .andExpect(xpath("//a[text()='" + inactiveIdentityProviderDefinition.getLinkText() + "']").doesNotExist()); } + @Test + public void testSamlRedirectWhenTheOnlyProvider() throws Exception { + final String zoneAdminClientId = "admin"; + BaseClientDetails zoneAdminClient = new BaseClientDetails(zoneAdminClientId, null, "openid", "client_credentials,authorization_code", "clients.admin,scim.read,scim.write","http://test.redirect.com"); + zoneAdminClient.setClientSecret("admin-secret"); + + IdentityZoneCreationResult identityZoneCreationResult = mockMvcUtils.createOtherIdentityZoneAndReturnResult("puppy-" + new RandomValueStringGenerator().generate(), mockMvc, webApplicationContext, zoneAdminClient); + IdentityZone identityZone = identityZoneCreationResult.getIdentityZone(); + String zoneAdminToken = identityZoneCreationResult.getZoneAdminToken(); + + IdentityProviderDefinition activeIdentityProviderDefinition = new IdentityProviderDefinition("http://example.com/saml/metadata", "active-saml", null, 0, false, true, "Active SAML Provider", null, identityZone.getId()); + IdentityProvider activeIdentityProvider = new IdentityProvider(); + activeIdentityProvider.setType(Origin.SAML); + activeIdentityProvider.setName("Active SAML Provider"); + activeIdentityProvider.setActive(true); + activeIdentityProvider.setConfig(JsonUtils.writeValueAsString(activeIdentityProviderDefinition)); + activeIdentityProvider.setOriginKey("active-saml"); + activeIdentityProvider = mockMvcUtils.createIdpUsingWebRequest(mockMvc, identityZone.getId(), zoneAdminToken, activeIdentityProvider, status().isCreated()); + + zoneAdminClient.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, Collections.singletonList(activeIdentityProvider.getOriginKey())); + mockMvcUtils.updateClient(mockMvc, zoneAdminToken, zoneAdminClient, identityZone); + + MockHttpSession session = new MockHttpSession(); + SavedRequest savedRequest = new DefaultSavedRequest(new MockHttpServletRequest(), new PortResolverImpl()) { + @Override + public String getRedirectUrl() { + return "http://test/redirect/oauth/authorize"; + } + @Override + public String[] getParameterValues(String name) { + if ("client_id".equals(name)) { + return new String[] {"admin"}; + } + return new String[0]; + } + @Override public List getCookies() { return null; } + @Override public String getMethod() { return null; } + @Override public List getHeaderValues(String name) { return null; } + @Override + public Collection getHeaderNames() { return null; } + @Override public List getLocales() { return null; } + @Override public Map getParameterMap() { return null; } + }; + session.setAttribute("SPRING_SECURITY_SAVED_REQUEST", savedRequest); + + mockMvc.perform(get("/login").accept(TEXT_HTML).with(new SetServerNameRequestPostProcessor(identityZone.getSubdomain() + ".localhost")) + .session(session) + .with(new SetServerNameRequestPostProcessor(identityZone.getSubdomain() + ".localhost"))) + .andDo(print()) + .andExpect(status().isFound()) + .andExpect(redirectedUrl("saml/discovery?returnIDParam=idp&entityID=cloudfoundry-saml-login&idp=active-saml&isPassive=true")); + } + @Test public void testDeactivatedProviderIsRemovedFromSamlLoginLinks() throws Exception { BaseClientDetails zoneAdminClient = new BaseClientDetails("admin", null, null, "client_credentials", "clients.admin,scim.read,scim.write");