/******************************************************************************* * 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.login; import org.cloudfoundry.identity.uaa.authentication.UaaAuthentication; import org.cloudfoundry.identity.uaa.authentication.UaaPrincipal; import org.cloudfoundry.identity.uaa.codestore.ExpiringCode; import org.cloudfoundry.identity.uaa.codestore.ExpiringCodeStore; import org.cloudfoundry.identity.uaa.codestore.JdbcExpiringCodeStore; import org.cloudfoundry.identity.uaa.constants.OriginKeys; import org.cloudfoundry.identity.uaa.home.HomeController; import org.cloudfoundry.identity.uaa.mock.InjectedMockContextTest; import org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils; import org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.IdentityZoneCreationResult; import org.cloudfoundry.identity.uaa.oauth.client.ClientConstants; import org.cloudfoundry.identity.uaa.provider.AbstractIdentityProviderDefinition; import org.cloudfoundry.identity.uaa.provider.AbstractXOAuthIdentityProviderDefinition; import org.cloudfoundry.identity.uaa.provider.IdentityProvider; import org.cloudfoundry.identity.uaa.provider.IdentityProviderProvisioning; import org.cloudfoundry.identity.uaa.provider.JdbcIdentityProviderProvisioning; import org.cloudfoundry.identity.uaa.provider.LdapIdentityProviderDefinition; import org.cloudfoundry.identity.uaa.provider.LockoutPolicy; import org.cloudfoundry.identity.uaa.provider.OIDCIdentityProviderDefinition; import org.cloudfoundry.identity.uaa.provider.SamlIdentityProviderDefinition; import org.cloudfoundry.identity.uaa.provider.UaaIdentityProviderDefinition; import org.cloudfoundry.identity.uaa.provider.saml.BootstrapSamlIdentityProviderConfiguratorTests; import org.cloudfoundry.identity.uaa.scim.ScimUser; import org.cloudfoundry.identity.uaa.scim.ScimUserProvisioning; import org.cloudfoundry.identity.uaa.scim.jdbc.JdbcScimUserProvisioning; import org.cloudfoundry.identity.uaa.security.web.CookieBasedCsrfTokenRepository; import org.cloudfoundry.identity.uaa.security.web.CorsFilter; import org.cloudfoundry.identity.uaa.user.UaaAuthority; import org.cloudfoundry.identity.uaa.util.JsonUtils; import org.cloudfoundry.identity.uaa.util.SetServerNameRequestPostProcessor; import org.cloudfoundry.identity.uaa.zone.BrandingInformation; import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.IdentityZoneConfiguration; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.cloudfoundry.identity.uaa.zone.IdentityZoneProvisioning; import org.cloudfoundry.identity.uaa.zone.Links; import org.cloudfoundry.identity.uaa.zone.MultitenancyFixture; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.mock.env.MockEnvironment; import org.springframework.mock.env.MockPropertySource; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpSession; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextImpl; import org.springframework.security.crypto.codec.Base64; import org.springframework.security.oauth2.common.util.RandomValueStringGenerator; import org.springframework.security.oauth2.provider.client.BaseClientDetails; import org.springframework.security.web.PortResolverImpl; import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; import org.springframework.security.web.savedrequest.DefaultSavedRequest; import org.springframework.security.web.savedrequest.SavedRequest; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; import org.springframework.web.context.support.XmlWebApplicationContext; import javax.servlet.http.Cookie; import javax.servlet.http.HttpSession; import java.lang.reflect.Field; import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; import java.sql.Timestamp; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; import static java.util.Arrays.asList; import static java.util.Collections.EMPTY_LIST; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static org.cloudfoundry.identity.uaa.constants.OriginKeys.LDAP; import static org.cloudfoundry.identity.uaa.constants.OriginKeys.UAA; import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.CookieCsrfPostProcessor.cookieCsrf; import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.createOtherIdentityZone; import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.createUserInZone; import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.getMarissaSecurityContext; import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.getUaaSecurityContext; import static org.cloudfoundry.identity.uaa.web.UaaSavedRequestAwareAuthenticationSuccessHandler.SAVED_REQUEST_SESSION_ATTRIBUTE; import static org.cloudfoundry.identity.uaa.zone.IdentityZone.getUaa; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.isEmptyOrNullString; import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; 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.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.http.MediaType.TEXT_HTML; import static org.springframework.security.oauth2.common.util.OAuth2Utils.CLIENT_ID; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.securityContext; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; 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.redirectedUrlPattern; 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 InjectedMockContextTest { private MockEnvironment mockEnvironment; private MockPropertySource propertySource; private Properties originalProperties = new Properties(); Field f = ReflectionUtils.findField(MockEnvironment.class, "propertySource"); private RandomValueStringGenerator generator = new RandomValueStringGenerator(); private String adminToken; private XmlWebApplicationContext webApplicationContext; private IdentityZoneConfiguration originalConfiguration; private IdentityZoneConfiguration identityZoneConfiguration; private Links globalLinks; @Before public void setUpContext() throws Exception { globalLinks = getWebApplicationContext().getBean("globalLinks", Links.class); SecurityContextHolder.clearContext(); webApplicationContext = getWebApplicationContext(); mockEnvironment = (MockEnvironment) webApplicationContext.getEnvironment(); f.setAccessible(true); propertySource = (MockPropertySource)ReflectionUtils.getField(f, mockEnvironment); for (String s : propertySource.getPropertyNames()) { originalProperties.put(s, propertySource.getProperty(s)); } adminToken = MockMvcUtils.getClientCredentialsOAuthAccessToken(getMockMvc(), "admin", "adminsecret", null, null); originalConfiguration = getWebApplicationContext().getBean(IdentityZoneProvisioning.class).retrieve(getUaa().getId()).getConfig(); identityZoneConfiguration = getWebApplicationContext().getBean(IdentityZoneProvisioning.class).retrieve(getUaa().getId()).getConfig(); } @After public void resetGenerator() throws Exception { getWebApplicationContext().getBean(JdbcExpiringCodeStore.class).setGenerator(new RandomValueStringGenerator(24)); getWebApplicationContext().getBean(LoginInfoEndpoint.class).setGlobalLinks(globalLinks); } @After public void tearDown() throws Exception { //restore all properties setSelfServiceLinksEnabled(true); setDisableInternalUserManagement(false); setZoneConfiguration(originalConfiguration); mockEnvironment.getPropertySources().remove(MockPropertySource.MOCK_PROPERTIES_PROPERTY_SOURCE_NAME); MockPropertySource originalPropertySource = new MockPropertySource(originalProperties); ReflectionUtils.setField(f, mockEnvironment, new MockPropertySource(originalProperties)); mockEnvironment.getPropertySources().addLast(originalPropertySource); SecurityContextHolder.clearContext(); IdentityZoneHolder.clear(); } @Test public void testLogin() throws Exception { getMockMvc().perform(get("/login")) .andExpect(status().isOk()) .andExpect(view().name("login")) .andExpect(model().attribute("links", hasEntry("forgotPasswordLink", "/forgot_password"))) .andExpect(model().attribute("links", hasEntry("createAccountLink", "/create_account"))) .andExpect(model().attributeExists("prompts")) .andExpect(content().string(containsString("/create_account"))); } public IdentityZone createZoneLinksZone() throws Exception { String subdomain = new RandomValueStringGenerator(24).generate().toLowerCase(); IdentityZone zone = MockMvcUtils.createOtherIdentityZone(subdomain, getMockMvc(), getWebApplicationContext()); zone.getConfig().getLinks().setSelfService(new Links.SelfService().setPasswd(null).setSignup(null)); return MockMvcUtils.updateIdentityZone(zone, getWebApplicationContext()); } @Test public void self_service_zone_variable_links() throws Exception { IdentityZone zone = createZoneLinksZone(); getMockMvc().perform( get("/login") .header("Host", zone.getSubdomain()+".localhost") ) .andExpect(status().isOk()) .andExpect(view().name("login")) .andExpect(model().attribute("links", hasEntry("forgotPasswordLink", "/forgot_password"))) .andExpect(model().attribute("links", hasEntry("createAccountLink", "/create_account"))) .andExpect(content().string(containsString("/create_account"))); getWebApplicationContext().getBean(LoginInfoEndpoint.class).setGlobalLinks( new Links().setSelfService( new Links.SelfService() .setPasswd("/passwd?id={zone.id}") .setSignup("/signup?subdomain={zone.subdomain}") ) ); getMockMvc().perform( get("/login") .header("Host", zone.getSubdomain()+".localhost") ) .andExpect(status().isOk()) .andExpect(view().name("login")) .andExpect(model().attribute("links", hasEntry("forgotPasswordLink", "/passwd?id="+zone.getId()))) .andExpect(model().attribute("links", hasEntry("createAccountLink", "/signup?subdomain="+zone.getSubdomain()))) .andExpect(content().string(containsString("/passwd?id="+zone.getId()))) .andExpect(content().string(containsString("/signup?subdomain="+zone.getSubdomain()))); zone.getConfig().getLinks().setSelfService( new Links.SelfService() .setPasswd("/local_passwd?id={zone.id}") .setSignup("/local_signup?subdomain={zone.subdomain}") ); zone = MockMvcUtils.updateIdentityZone(zone, getWebApplicationContext()); getMockMvc().perform( get("/login") .header("Host", zone.getSubdomain()+".localhost") ) .andExpect(status().isOk()) .andExpect(view().name("login")) .andExpect(model().attribute("links", hasEntry("forgotPasswordLink", "/local_passwd?id="+zone.getId()))) .andExpect(model().attribute("links", hasEntry("createAccountLink", "/local_signup?subdomain="+zone.getSubdomain()))) .andExpect(content().string(containsString("/local_passwd?id="+zone.getId()))) .andExpect(content().string(containsString("/local_signup?subdomain="+zone.getSubdomain()))); } @Test public void global_zone_variable_home_redirect() throws Exception { IdentityZone zone = createZoneLinksZone(); ScimUser marissa = new ScimUser(null, "marissa", "", ""); marissa.setPassword("secret"); marissa.setPrimaryEmail("marissa@test.org"); marissa = createUserInZone(getMockMvc(), adminToken, marissa, "", zone.getId()); getMockMvc().perform( get("/") .with(securityContext(getUaaSecurityContext(marissa.getUserName(), getWebApplicationContext(), zone))) .header("Host", zone.getSubdomain()+".localhost") ) .andDo(print()) .andExpect(status().isOk()); getWebApplicationContext().getBean(HomeController.class).setGlobalLinks( new Links().setHomeRedirect("http://{zone.subdomain}.redirect.to/z/{zone.id}") ); getMockMvc().perform( get("/") .with(securityContext(getUaaSecurityContext(marissa.getUserName(), getWebApplicationContext(), zone))) .header("Host", zone.getSubdomain()+".localhost") ) .andExpect(status().is3xxRedirection()) .andExpect(redirectedUrl("http://"+zone.getSubdomain()+".redirect.to/z/"+zone.getId())); zone.getConfig().getLinks().setHomeRedirect("http://configured.{zone.subdomain}.redirect.to/z/{zone.id}"); zone = MockMvcUtils.updateIdentityZone(zone, getWebApplicationContext()); getMockMvc().perform( get("/") .with(securityContext(getUaaSecurityContext(marissa.getUserName(), getWebApplicationContext(), zone))) .header("Host", zone.getSubdomain()+".localhost") ) .andExpect(status().is3xxRedirection()) .andExpect(redirectedUrl("http://configured."+zone.getSubdomain()+".redirect.to/z/"+zone.getId())); } @Test public void testLogin_Csrf_MaxAge() throws Exception { getMockMvc() .perform( get("/login")) .andExpect(status().isOk()) .andExpect(cookie().maxAge(CookieBasedCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME, CookieBasedCsrfTokenRepository.DEFAULT_COOKIE_MAX_AGE)); } @Test public void testLogin_Csrf_Reset_On_Refresh() throws Exception { MvcResult mvcResult = getMockMvc() .perform( get("/login")) .andReturn(); Cookie csrf1 = mvcResult.getResponse().getCookie(CookieBasedCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME); mvcResult = getMockMvc() .perform( get("/login") .cookie(csrf1)) .andReturn(); Cookie csrf2 = mvcResult.getResponse().getCookie(CookieBasedCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME); assertNotNull(csrf2); assertNotEquals(csrf1.getValue(), csrf2.getValue()); } protected void setDisableInternalAuth(boolean disable) throws Exception { MockMvcUtils.setDisableInternalAuth(getWebApplicationContext(), getUaa().getId(), disable); } protected void setDisableInternalUserManagement(boolean disabled) throws Exception { MockMvcUtils.setDisableInternalUserManagement(getWebApplicationContext(), getUaa().getId(), disabled); } protected void setSelfServiceLinksEnabled(boolean enabled) throws Exception { MockMvcUtils.setSelfServiceLinksEnabled(getWebApplicationContext(), getUaa().getId(), enabled); } protected void setZoneConfiguration(IdentityZoneConfiguration configuration) throws Exception { MockMvcUtils.setZoneConfiguration(getWebApplicationContext(), getUaa().getId(), configuration); } protected void setPrompts(List<Prompt> prompts) throws Exception { MockMvcUtils.setPrompts(getWebApplicationContext(), getUaa().getId(), prompts); } protected List<Prompt> getPrompts() throws Exception { return MockMvcUtils.getPrompts(getWebApplicationContext(), getUaa().getId()); } protected Links.Logout getLogout() throws Exception { return MockMvcUtils.getLogout(getWebApplicationContext(), getUaa().getId()); } protected void setLogout(Links.Logout logout) throws Exception { MockMvcUtils.setLogout(getWebApplicationContext(), getUaa().getId(), logout); } @Test public void test_cookie_csrf() throws Exception { MockHttpSession session = new MockHttpSession(); MockHttpServletRequestBuilder invalidPost = post("/login.do") .session(session) .param("username", "marissa") .param("password", "koala"); getMockMvc().perform(invalidPost) .andDo(print()) .andExpect(status().isForbidden()) .andExpect(forwardedUrl("/login?error=invalid_login_request")); session = new MockHttpSession(); String csrfValue = "12345"; Cookie cookie = new Cookie(CookieBasedCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME, csrfValue); getMockMvc().perform( invalidPost .cookie(cookie) .param(CookieBasedCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME, "other-value") ) .andDo(print()) .andExpect(status().isForbidden()) .andExpect(forwardedUrl("/login?error=invalid_login_request")); ScimUser marissa = getWebApplicationContext().getBean(JdbcScimUserProvisioning.class).query("username eq 'marissa'").get(0); MockHttpServletRequestBuilder validPost = post("/uaa/login.do") .session(session) .contextPath("/uaa") .param("username", "marissa") .param("password", "koala") .cookie(cookie) .param(CookieBasedCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME, csrfValue); getMockMvc().perform(validPost) .andDo(print()) .andExpect(status().isFound()) .andExpect(redirectedUrl("/uaa/")) .andExpect(currentUserCookie(marissa.getId())); } private static ResultMatcher currentUserCookie(String userId) { return result -> { cookie().value("Current-User", URLEncoder.encode("{\"userId\":\"" + userId + "\"}", "UTF-8")).match(result); cookie().maxAge("Current-User", 365*24*60*60); cookie().path("Current-User", "/uaa").match(result); }; } @Test public void test_case_insensitive_login() throws Exception { String username = "mixed-CASE-USER-"+generator.generate()+"@testdomain.com"; ScimUser user = createUser(username, "", adminToken); assertEquals(username, user.getUserName()); MockHttpServletRequestBuilder loginPost = post("/authenticate") .accept(MediaType.APPLICATION_JSON_VALUE) .param("username", user.getUserName()) .param("password", user.getPassword()); getMockMvc().perform(loginPost) .andExpect(status().isOk()) .andExpect(content().string(containsString("\"username\":\"" + user.getUserName()))) .andExpect(content().string(containsString("\"email\":\"" + user.getPrimaryEmail()))); loginPost = post("/authenticate") .accept(MediaType.APPLICATION_JSON_VALUE) .param("username", user.getUserName().toUpperCase()) .param("password", user.getPassword()); getMockMvc().perform(loginPost) .andExpect(status().isOk()) .andExpect(content().string(containsString("\"username\":\"" + user.getUserName()))) .andExpect(content().string(containsString("\"email\":\"" + user.getPrimaryEmail()))); } @Test public void test_previous_login_time_upon_authentication() throws Exception { ScimUser user = createUser("", adminToken); MockHttpSession session = new MockHttpSession(); long beforeAuthTime = System.currentTimeMillis(); getMockMvc().perform(post("/uaa/login.do") .session(session) .with(cookieCsrf()) .contextPath("/uaa") .param("username", user.getUserName()) .param("password", user.getPassword())); long afterAuthTime = System.currentTimeMillis(); SecurityContext securityContext = (SecurityContext) session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY); assertNull(((UaaAuthentication) securityContext.getAuthentication()).getLastLoginSuccessTime()); session = new MockHttpSession(); getMockMvc().perform(post("/uaa/login.do") .session(session) .with(cookieCsrf()) .contextPath("/uaa") .param("username", user.getUserName()) .param("password", user.getPassword())); securityContext = (SecurityContext) session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY); Long lastLoginTime = ((UaaAuthentication) securityContext.getAuthentication()).getLastLoginSuccessTime(); assertThat(lastLoginTime, greaterThanOrEqualTo(beforeAuthTime)); assertThat(lastLoginTime, lessThanOrEqualTo(afterAuthTime)); } @Test public void testLogin_Post_When_DisableInternalUserManagement_Is_True() throws Exception { ScimUser user = createUser("", adminToken); setDisableInternalAuth(true); try { getMockMvc().perform(post("/login.do") .with(cookieCsrf()) .param("username", user.getUserName()) .param("password", user.getPassword())) .andExpect(redirectedUrl("/login?error=login_failure")); } finally { setDisableInternalAuth(false); } getMockMvc().perform(post("/uaa/login.do") .with(cookieCsrf()) .contextPath("/uaa") .param("username", user.getUserName()) .param("password", user.getPassword())) .andDo(print()) .andExpect(redirectedUrl("/uaa/")) .andExpect(currentUserCookie(user.getId())); } @Test public void testLogin_When_DisableInternalUserManagement_Is_True() throws Exception { setDisableInternalUserManagement(true); getMockMvc().perform(get("/login")) .andExpect(status().isOk()) .andExpect(view().name("login")) .andExpect(model().attributeExists("prompts")) .andExpect(content().string(not(containsString("/create_account")))); } @Test public void testDefaultLogo() throws Exception { mockEnvironment.setProperty("assetBaseUrl", "//cdn.example.com/resources"); getMockMvc().perform(get("/login")) .andExpect(content().string(containsString("url(//cdn.example.com/resources/images/product-logo.png)"))); } @Test public void testCustomLogo() throws Exception { setZoneFavIconAndProductLogo(null, "/bASe/64+"); getMockMvc().perform(get("/login")) .andExpect(content().string(allOf(containsString("url(data:image/png;base64,/bASe/64+)"), not(containsString("url(/uaa/resources/oss/images/product-logo.png)"))))); } @Test public void testCustomFavIcon() throws Exception { setZoneFavIconAndProductLogo("/sM4lL==", null); getMockMvc().perform(get("/login")) .andExpect(content().string(allOf(containsString("<link href='data:image/png;base64,/sM4lL==' rel='shortcut icon' />"), not(containsString("square-logo.png"))))); } @Test public void testCustomFavIcon_With_LineBreaks() throws Exception { setZoneFavIconAndProductLogo("/sM4\n\nlL==", "/sM4\n\nlL=="); getMockMvc().perform(get("/login")) .andExpect(content().string(allOf(containsString("<link href='data:image/png;base64,/sM4\n\nlL==' rel='shortcut icon' />"), not(containsString("square-logo.png"))))) .andExpect(content().string(allOf(containsString("<style>.header-image {background-image: url(data:image/png;base64,/sM4lL==);}</style>"), not(containsString("product-logo.png"))))); } private void setZoneFavIconAndProductLogo(String favIcon, String productLogo) throws Exception { BrandingInformation branding = new BrandingInformation(); branding.setSquareLogo(favIcon); branding.setProductLogo(productLogo); identityZoneConfiguration.setBranding(branding); setZoneConfiguration(identityZoneConfiguration); } private static final String defaultCopyrightTemplate = "Copyright "+"\u00a9"+" %s"; private static final String cfCopyrightText = String.format(defaultCopyrightTemplate, "CloudFoundry.org Foundation, Inc."); private static final String CF_LAST_LOGIN = "Last Login"; @Test public void testDefaultFooter() throws Exception { getMockMvc().perform(get("/login")) .andExpect(content().string(containsString(cfCopyrightText))) .andExpect(content().string(not(containsString(CF_LAST_LOGIN)))); } @Test public void testCustomizedFooter() throws Exception { String customFooterText = "This text should be in the footer."; BrandingInformation branding = new BrandingInformation(); branding.setFooterLegalText(customFooterText); identityZoneConfiguration.setBranding(branding); setZoneConfiguration(identityZoneConfiguration); getMockMvc().perform(get("/login")) .andExpect(content().string(allOf(containsString(customFooterText), not(containsString(cfCopyrightText))))) .andExpect(content().string(not(containsString(CF_LAST_LOGIN)))); } @Test public void testCustomCompanyName() throws Exception { String companyName = "Big Company"; BrandingInformation branding = new BrandingInformation(); branding.setCompanyName(companyName); identityZoneConfiguration.setBranding(branding); setZoneConfiguration(identityZoneConfiguration); String expectedFooterText = String.format(defaultCopyrightTemplate, companyName); getMockMvc().perform(get("/login")) .andExpect(content().string(allOf(containsString(expectedFooterText)))); } @Test public void testCustomCompanyNameInZone() throws Exception { String companyName = "Big Company"; BrandingInformation branding = new BrandingInformation(); branding.setCompanyName(companyName); identityZoneConfiguration.setBranding(branding); setZoneConfiguration(identityZoneConfiguration); branding = new BrandingInformation(); String zoneCompanyName = "Zone Company"; branding.setCompanyName(zoneCompanyName); IdentityZoneConfiguration config = new IdentityZoneConfiguration(); config.setBranding(branding); IdentityZone identityZone = setupZone(config); String expectedFooterText = String.format(defaultCopyrightTemplate, zoneCompanyName); getMockMvc().perform(get("/login").accept(TEXT_HTML).with(new SetServerNameRequestPostProcessor(identityZone.getSubdomain() + ".localhost"))) .andExpect(status().isOk()) .andExpect(content().string(allOf(containsString(expectedFooterText)))); } @Test public void testFooterLinks() throws Exception { Map<String, String> footerLinks = new HashMap<>(); footerLinks.put("Terms of Use", "/terms.html"); footerLinks.put("Privacy", "/privacy"); // Insanity BrandingInformation branding = new BrandingInformation(); branding.setFooterLinks(footerLinks); identityZoneConfiguration.setBranding(branding); setZoneConfiguration(identityZoneConfiguration); getMockMvc().perform(get("/login")).andExpect(content().string(containsString("<a href=\"/privacy\">Privacy</a> — <a href=\"/terms.html\">Terms of Use</a>"))); } @Test public void testForgotPasswordPageDoesNotHaveCsrf() throws Exception { getMockMvc().perform(get("/forgot_password")) .andExpect(status().isOk()) .andExpect(view().name("forgot_password")) .andExpect(content().string(containsString("action=\"/forgot_password.do\""))) .andExpect(content().string(not(containsString("name=\"_csrf\"")))); } @Test public void testForgotPasswordSubmitDoesNotValidateCsrf() throws Exception { getMockMvc().perform( post("/forgot_password.do") .param("email", "marissa@test.org") .with(csrf().useInvalidToken())) .andExpect(status().isFound()) .andExpect(redirectedUrl("email_sent?code=reset_password")); } @Test public void testChangePasswordPageDoesHaveCsrf() throws Exception { getMockMvc().perform( get("/change_password") .with(securityContext(MockMvcUtils.utils().getMarissaSecurityContext(getWebApplicationContext()))) ) .andExpect(status().isOk()) .andExpect(view().name("change_password")) .andExpect(content().string(containsString("action=\"/change_password.do\""))) .andExpect(content().string(containsString("name=\"_csrf\""))); } @Test public void testChangePasswordSubmitDoesValidateCsrf() throws Exception { ScimUser user = createUser("", adminToken); getMockMvc().perform( post("/change_password.do") .with(securityContext(MockMvcUtils.utils().getUaaSecurityContext(user.getUserName(), getWebApplicationContext()))) .param("current_password", user.getPassword()) .param("new_password", "newSecr3t") .param("confirm_password", "newSecr3t") .with(csrf().useInvalidToken())) .andExpect(status().isFound()) .andExpect(redirectedUrl("http://localhost/invalid_request")); getMockMvc().perform( post("/change_password.do") .with(securityContext(MockMvcUtils.utils().getUaaSecurityContext(user.getUserName(), getWebApplicationContext()))) .param("current_password", user.getPassword()) .param("new_password", "newSecr3t") .param("confirm_password", "newSecr3t") .with(csrf())) .andExpect(status().isFound()) .andExpect(redirectedUrl("profile")); } private ScimUser createUser(String subdomain, String accessToken) throws Exception { String username = generator.generate()+"@testdomain.com"; return createUser(username, subdomain, accessToken); } private ScimUser createUser(String username, String subdomain, String accessToken) throws Exception { ScimUser user = new ScimUser(null, username, "Test", "User"); user.setPrimaryEmail(username); user.setPassword("Secr3t"); user = createUserInZone(getMockMvc(), accessToken, user, subdomain); user.setPassword("Secr3t"); return user; } @Test public void testLogOut() throws Exception { getMockMvc().perform(get("/uaa/logout.do").contextPath("/uaa")) .andExpect(status().isFound()) .andExpect(redirectedUrl("/uaa/login")) .andExpect(emptyCurrentUserCookie()); } @Test public void testLogOutIgnoreRedirectParameter() throws Exception { getMockMvc().perform(get("/uaa/logout.do").param("redirect", "https://www.google.com").contextPath("/uaa")) .andExpect(status().isFound()) .andExpect(redirectedUrl("/uaa/login")) .andExpect(emptyCurrentUserCookie()); } @Test public void testLogOutEnableRedirectParameter() throws Exception { Links.Logout original = getLogout(); Links.Logout logout = getLogout(); logout.setDisableRedirectParameter(false); logout.setWhitelist(Arrays.asList("https://www.google.com")); setLogout(logout); try { getMockMvc().perform(get("/uaa/logout.do").param("redirect", "https://www.google.com").contextPath("/uaa")) .andExpect(status().isFound()) .andExpect(redirectedUrl("https://www.google.com")) .andExpect(emptyCurrentUserCookie()); } finally { setLogout(original); } } @Test public void testLogOutAllowInternalRedirect() throws Exception { Links.Logout original = getLogout(); Links.Logout logout = getLogout(); setLogout(logout); try { getMockMvc().perform(get("/uaa/logout.do").param("redirect", "http://localhost/uaa/internal-location").contextPath("/uaa")) .andExpect(status().isFound()) .andExpect(redirectedUrl("http://localhost/uaa/internal-location")) .andExpect(emptyCurrentUserCookie()); } finally { setLogout(original); } } @Test public void testLogOutWhitelistedRedirectParameter() throws Exception { Links.Logout original = getLogout(); Links.Logout logout = getLogout(); logout.setDisableRedirectParameter(false); logout.setWhitelist(asList("https://www.google.com")); setLogout(logout); try { getMockMvc().perform(get("/uaa/logout.do").param("redirect", "https://www.google.com").contextPath("/uaa")) .andExpect(status().isFound()) .andExpect(redirectedUrl("https://www.google.com")) .andExpect(emptyCurrentUserCookie()); } finally { setLogout(original); } } @Test public void testLogOutNotWhitelistedRedirectParameter() throws Exception { Links.Logout original = getLogout(); Links.Logout logout = getLogout(); logout.setDisableRedirectParameter(false); logout.setWhitelist(asList("https://www.yahoo.com")); setLogout(logout); try { getMockMvc().perform(get("/uaa/logout.do").param("redirect", "https://www.google.com").contextPath("/uaa")) .andExpect(status().isFound()) .andExpect(redirectedUrl("/uaa/login")) .andExpect(emptyCurrentUserCookie()); } finally { setLogout(original); } } @Test public void testLogOutNullWhitelistedRedirectParameter() throws Exception { Links.Logout original = getLogout(); Links.Logout logout = getLogout(); logout.setDisableRedirectParameter(false); logout.setWhitelist(Arrays.asList("http*://www.google.com")); setLogout(logout); try { getMockMvc().perform(get("/uaa/logout.do").param("redirect", "https://www.google.com").contextPath("/uaa")) .andExpect(status().isFound()) .andExpect(redirectedUrl("https://www.google.com")) .andExpect(emptyCurrentUserCookie()); } finally { setLogout(original); } } @Test public void testLogOutEmptyWhitelistedRedirectParameter() throws Exception { Links.Logout original = getLogout(); Links.Logout logout = getLogout(); logout.setDisableRedirectParameter(false); logout.setWhitelist(EMPTY_LIST); setLogout(logout); try { getMockMvc().perform(get("/uaa/logout.do").param("redirect", "https://www.google.com").contextPath("/uaa")) .andExpect(status().isFound()) .andExpect(redirectedUrl("/uaa/login")) .andExpect(emptyCurrentUserCookie()); } finally { setLogout(original); } } @Test public void testLogoutRedirectIsEnabledInZone() throws Exception { String zoneId = generator.generate(); IdentityZone zone = MultitenancyFixture.identityZone(zoneId, zoneId); zone.setConfig(new IdentityZoneConfiguration()); IdentityZoneProvisioning provisioning = getWebApplicationContext().getBean(IdentityZoneProvisioning.class); zone = provisioning.create(zone); assertFalse(zone.getConfig().getLinks().getLogout().isDisableRedirectParameter()); } @Test public void testLogOutChangeUrlValue() throws Exception { Links.Logout original = getLogout(); assertFalse(original.isDisableRedirectParameter()); Links.Logout logout = getLogout(); logout.setRedirectUrl("https://www.google.com"); setLogout(logout); try { getMockMvc().perform(get("/uaa/logout.do").contextPath("/uaa")) .andExpect(status().isFound()) .andExpect(redirectedUrl("https://www.google.com")) .andExpect(emptyCurrentUserCookie()); } finally { setLogout(original); } } @Test public void testLogOutWithClientRedirect() throws Exception { Links.Logout original = getLogout(); Links.Logout logout = getLogout(); logout.setDisableRedirectParameter(false); logout.setWhitelist(EMPTY_LIST); setLogout(logout); try { String clientId = generator.generate(); String accessToken = MockMvcUtils.getClientOAuthAccessToken(getMockMvc(), "admin", "adminsecret", ""); BaseClientDetails client = new BaseClientDetails(clientId, "", "", "client_credentials", "uaa.none", "http://*.wildcard.testing,http://testing.com"); client.setClientSecret(clientId); MockMvcUtils.createClient(getMockMvc(), accessToken, client); getMockMvc().perform( get("/uaa/logout.do") .param(CLIENT_ID, clientId) .param("redirect", "http://testing.com") .contextPath("/uaa") ) .andExpect(status().isFound()) .andExpect(redirectedUrl("http://testing.com")) .andExpect(emptyCurrentUserCookie()); getMockMvc().perform( get("/uaa/logout.do") .param(CLIENT_ID, clientId) .param("redirect", "http://www.wildcard.testing") .contextPath("/uaa") ) .andExpect(status().isFound()) .andExpect(redirectedUrl("http://www.wildcard.testing")) .andExpect(emptyCurrentUserCookie()); getMockMvc().perform( get("/uaa/logout.do") .param(CLIENT_ID, "non-existent-client") .param("redirect", "http://www.wildcard.testing") .contextPath("/uaa") ) .andExpect(status().isFound()) .andExpect(redirectedUrl("/uaa/login")) .andExpect(emptyCurrentUserCookie()); } finally { setLogout(original); } } @Test public void testLogOut_Config_For_Zone() throws Exception { String zoneId = new RandomValueStringGenerator().generate(); IdentityZoneProvisioning zoneProvisioning = getWebApplicationContext().getBean(IdentityZoneProvisioning.class); IdentityZone zone = MultitenancyFixture.identityZone(zoneId, zoneId); zone.setName(zoneId).setConfig(new IdentityZoneConfiguration()); zone.getConfig().getLinks().getLogout() .setRedirectUrl("http://test.redirect.com") .setDisableRedirectParameter(true) .setRedirectParameterName("redirect"); zone = zoneProvisioning.create(zone); //default zone getMockMvc().perform(get("/uaa/logout.do").contextPath("/uaa")) .andExpect(status().isFound()) .andExpect(redirectedUrl("/uaa/login")) .andExpect(emptyCurrentUserCookie()); //other zone getMockMvc().perform(get("/uaa/logout.do") .contextPath("/uaa") .header("Host", zoneId+".localhost")) .andExpect(status().isFound()) .andExpect(redirectedUrl("http://test.redirect.com")) .andExpect(emptyCurrentUserCookie()); getMockMvc().perform(get("/uaa/logout.do") .contextPath("/uaa") .header("Host", zoneId+".localhost") .param("redirect", "http://google.com") ) .andExpect(status().isFound()) .andExpect(redirectedUrl("http://test.redirect.com")) .andExpect(emptyCurrentUserCookie()); zone.getConfig().getLinks().getLogout().setDisableRedirectParameter(false); zone = zoneProvisioning.update(zone); getMockMvc().perform(get("/uaa/logout.do") .contextPath("/uaa") .header("Host", zoneId+".localhost") .param("redirect", "http://google.com") ) .andExpect(status().isFound()) .andExpect(redirectedUrl("http://test.redirect.com")) .andExpect(emptyCurrentUserCookie()); zone.getConfig().getLinks().getLogout().setDisableRedirectParameter(false); zone.getConfig().getLinks().getLogout().setWhitelist(asList("http://google.com")); zone = zoneProvisioning.update(zone); getMockMvc().perform(get("/uaa/logout.do") .contextPath("/uaa") .header("Host", zoneId+".localhost") .param("redirect", "http://google.com") ) .andExpect(status().isFound()) .andExpect(redirectedUrl("http://google.com")) .andExpect(emptyCurrentUserCookie()); zone.getConfig().getLinks().getLogout().setWhitelist(asList("http://yahoo.com")); zone = zoneProvisioning.update(zone); getMockMvc().perform(get("/uaa/logout.do") .contextPath("/uaa") .header("Host", zoneId+".localhost") .param("redirect", "http://google.com") ) .andExpect(status().isFound()) .andExpect(redirectedUrl("http://test.redirect.com")) .andExpect(emptyCurrentUserCookie()); getMockMvc().perform(get("/uaa/logout.do") .contextPath("/uaa") .header("Host", zoneId+".localhost") .param("redirect", "http://yahoo.com") ) .andExpect(status().isFound()) .andExpect(redirectedUrl("http://yahoo.com")) .andExpect(emptyCurrentUserCookie()); } @Test public void testLoginWithAnalytics() throws Exception { mockEnvironment.setProperty("analytics.code", "secret_code"); mockEnvironment.setProperty("analytics.domain", "example.com"); getMockMvc().perform(get("/login").accept(TEXT_HTML)) .andExpect(status().isOk()) .andExpect(xpath("//body/script[contains(text(),'example.com')]").exists()); } @Test public void testDefaultAndExternalizedBranding() throws Exception { getMockMvc().perform(MockMvcRequestBuilders.get("/login")) .andExpect(xpath("//head/link[@rel='shortcut icon']/@href").string("/resources/oss/images/square-logo.png")) .andExpect(xpath("//head/link[@href='/resources/oss/stylesheets/application.css']").exists()) .andExpect(xpath("//head/style[text()[contains(.,'/resources/oss/images/product-logo.png')]]").exists()); mockEnvironment.setProperty("assetBaseUrl", "//cdn.example.com/pivotal"); getMockMvc().perform(MockMvcRequestBuilders.get("/login")) .andExpect(xpath("//head/link[@rel='shortcut icon']/@href").string("//cdn.example.com/pivotal/images/square-logo.png")) .andExpect(xpath("//head/link[@href='//cdn.example.com/pivotal/stylesheets/application.css']").exists()) .andExpect(xpath("//head/style[text()[contains(.,'//cdn.example.com/pivotal/images/product-logo.png')]]").exists()); } @Test public void testAccessConfirmationPage() throws Exception { ScimUserProvisioning userProvisioning = getWebApplicationContext().getBean(JdbcScimUserProvisioning.class); ScimUser marissa = userProvisioning.query("username eq \"marissa\" and origin eq \"uaa\"").get(0); UaaPrincipal uaaPrincipal = new UaaPrincipal(marissa.getId(), marissa.getUserName(), marissa.getPrimaryEmail(), marissa.getOrigin(), marissa.getExternalId(), IdentityZoneHolder.get().getId()); UsernamePasswordAuthenticationToken principal = new UsernamePasswordAuthenticationToken(uaaPrincipal, null, asList(UaaAuthority.fromAuthorities("uaa.user"))); MockHttpSession session = new MockHttpSession(); SecurityContext securityContext = new SecurityContextImpl(); securityContext.setAuthentication(principal); session.putValue("SPRING_SECURITY_CONTEXT", securityContext); MockHttpServletRequestBuilder get = get("/oauth/authorize") .accept(TEXT_HTML) .param("response_type", "code") .param("client_id", "app") .param("state", "somestate") .param("redirect_uri", "http://localhost:8080/app/") .session(session) .principal(principal); getMockMvc().perform(get) .andExpect(status().isOk()) .andExpect(forwardedUrl("/oauth/confirm_access")); } @Test public void testSignupsAndResetPasswordEnabled() throws Exception { setSelfServiceLinksEnabled(true); getMockMvc().perform(MockMvcRequestBuilders.get("/login")) .andExpect(xpath("//a[text()='Create account']").exists()) .andExpect(xpath("//a[text()='Reset password']").exists()); } @Test public void testSignupsAndResetPasswordDisabledWithNoLinksConfigured() throws Exception { setSelfServiceLinksEnabled(false); getMockMvc().perform(MockMvcRequestBuilders.get("/login")) .andExpect(xpath("//a[text()='Create account']").doesNotExist()) .andExpect(xpath("//a[text()='Reset password']").doesNotExist()); } @Test public void testSignupsAndResetPasswordDisabledWithSomeLinksConfigured() throws Exception { identityZoneConfiguration.getLinks().getSelfService().setSignup("http://example.com/signup"); identityZoneConfiguration.getLinks().getSelfService().setPasswd("http://example.com/reset_passwd"); identityZoneConfiguration.getLinks().getSelfService().setSelfServiceLinksEnabled(false); setZoneConfiguration(identityZoneConfiguration); getMockMvc().perform(MockMvcRequestBuilders.get("/login")) .andExpect(xpath("//a[text()='Create account']").doesNotExist()) .andExpect(xpath("//a[text()='Reset password']").doesNotExist()); } @Test public void testSignupsAndResetPasswordEnabledWithCustomLinks() throws Exception { identityZoneConfiguration.getLinks().getSelfService().setSignup("http://example.com/signup"); identityZoneConfiguration.getLinks().getSelfService().setPasswd("http://example.com/reset_passwd"); identityZoneConfiguration.getLinks().getSelfService().setSelfServiceLinksEnabled(true); setZoneConfiguration(identityZoneConfiguration); getMockMvc().perform(MockMvcRequestBuilders.get("/login")) .andExpect(xpath("//a[text()='Create account']/@href").string("http://example.com/signup")) .andExpect(xpath("//a[text()='Reset password']/@href").string("http://example.com/reset_passwd")); } @Test public void testLoginWithExplicitPrompts() throws Exception { List<Prompt> original = getPrompts(); try { Prompt first = new Prompt("how", "text", "How did I get here?"); Prompt second = new Prompt("where", "password", "Where does that highway go to?"); setPrompts(asList(first, second)); getMockMvc().perform(get("/login").accept(TEXT_HTML)) .andExpect(status().isOk()) .andExpect(view().name("login")) .andExpect(model().attribute("prompts", hasKey("how"))) .andExpect(model().attribute("prompts", hasKey("where"))) .andExpect(model().attribute("prompts", not(hasKey("password")))); } finally { setPrompts(original); } } @Test public void testLoginWithExplicitJsonPrompts() throws Exception { List<Prompt> original = getPrompts(); try { Prompt first = new Prompt("how", "text", "How did I get here?"); Prompt second = new Prompt("where", "password", "Where does that highway go to?"); setPrompts(asList(first, second)); getMockMvc().perform(get("/login") .accept(APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(view().name("login")) .andExpect(model().attribute("prompts", hasKey("how"))) .andExpect(model().attribute("prompts", hasKey("where"))) .andExpect(model().attribute("prompts", not(hasKey("password")))); } finally { setPrompts(original); } } @Test public void testLoginWithRemoteUaaPrompts() throws Exception { getMockMvc().perform(get("/login") .accept(TEXT_HTML)) .andExpect(status().isOk()) .andExpect(view().name("login")) .andExpect(model().attribute("prompts", hasKey("username"))) .andExpect(model().attribute("prompts", not(hasKey("passcode")))) .andExpect(model().attribute("prompts", hasKey("password"))); } @Test public void testLoginWithRemoteUaaJsonPrompts() throws Exception { getMockMvc().perform(get("/login") .accept(APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(view().name("login")) .andExpect(model().attribute("prompts", hasKey("username"))) .andExpect(model().attribute("prompts", hasKey("password"))); } @Test public void testInfoWithRemoteUaaJsonPrompts() throws Exception { getMockMvc().perform(get("/info") .accept(APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(view().name("login")) .andExpect(model().attribute("prompts", hasKey("username"))) .andExpect(model().attribute("prompts", hasKey("password"))); } @Test public void testInfoWithRemoteUaaHtmlPrompts() throws Exception { getMockMvc().perform(get("/info") .accept(TEXT_HTML)) .andExpect(status().isOk()) .andExpect(view().name("login")) .andExpect(model().attribute("prompts", hasKey("username"))) .andExpect(model().attribute("prompts", hasKey("password"))); } @Test public void testDefaultAndCustomSignupLink() throws Exception { getMockMvc().perform(get("/login").accept(TEXT_HTML)) .andExpect(status().isOk()) .andExpect(model().attribute("links", hasEntry("createAccountLink", "/create_account"))); identityZoneConfiguration.getLinks().getSelfService().setSignup("http://www.example.com/signup"); setZoneConfiguration(identityZoneConfiguration); getMockMvc().perform(get("/login").accept(TEXT_HTML)) .andExpect(status().isOk()) .andExpect(model().attribute("links", hasEntry("createAccountLink", "http://www.example.com/signup"))); } @Test public void testLocalSignupDisabled() throws Exception { setSelfServiceLinksEnabled(false); getMockMvc().perform(get("/login").accept(TEXT_HTML)) .andExpect(status().isOk()) .andExpect(model().attribute("createAccountLink", nullValue())); } @Test public void testCustomSignupLinkWithLocalSignupDisabled() throws Exception { setSelfServiceLinksEnabled(false); getMockMvc().perform(get("/login").accept(TEXT_HTML)) .andExpect(status().isOk()) .andExpect(model().attribute("createAccountLink", nullValue())); } @Test public void testSamlLoginLinksShowActiveProviders() throws Exception { String activeAlias = "login-saml-"+generator.generate(); String inactiveAlias = "login-saml-"+generator.generate(); BaseClientDetails zoneAdminClient = new BaseClientDetails("admin", null, null, "client_credentials", "clients.admin,scim.read,scim.write"); zoneAdminClient.setClientSecret("admin-secret"); IdentityZoneCreationResult identityZoneCreationResult = MockMvcUtils.createOtherIdentityZoneAndReturnResult("puppy-" + new RandomValueStringGenerator().generate(), getMockMvc(), getWebApplicationContext(), zoneAdminClient); IdentityZone identityZone = identityZoneCreationResult.getIdentityZone(); String zoneAdminToken = identityZoneCreationResult.getZoneAdminToken(); String metadata = String.format(MockMvcUtils.IDP_META_DATA, new RandomValueStringGenerator().generate()); SamlIdentityProviderDefinition activeSamlIdentityProviderDefinition = new SamlIdentityProviderDefinition() .setMetaDataLocation(metadata) .setIdpEntityAlias(activeAlias) .setLinkText("Active SAML Provider") .setShowSamlLink(true) .setZoneId(identityZone.getId()); IdentityProvider activeIdentityProvider = new IdentityProvider(); activeIdentityProvider.setType(OriginKeys.SAML); activeIdentityProvider.setName("Active SAML Provider"); activeIdentityProvider.setConfig(activeSamlIdentityProviderDefinition); activeIdentityProvider.setActive(true); activeIdentityProvider.setOriginKey(activeAlias); MockMvcUtils.createIdpUsingWebRequest(getMockMvc(), identityZone.getId(), zoneAdminToken, activeIdentityProvider, status().isCreated()); metadata = String.format(MockMvcUtils.IDP_META_DATA, new RandomValueStringGenerator().generate()); SamlIdentityProviderDefinition inactiveSamlIdentityProviderDefinition = new SamlIdentityProviderDefinition() .setMetaDataLocation(metadata) .setIdpEntityAlias(inactiveAlias) .setLinkText("You should not see me") .setZoneId(identityZone.getId()); IdentityProvider inactiveIdentityProvider = new IdentityProvider(); inactiveIdentityProvider.setType(OriginKeys.SAML); inactiveIdentityProvider.setName("Inactive SAML Provider"); inactiveIdentityProvider.setConfig(inactiveSamlIdentityProviderDefinition); inactiveIdentityProvider.setActive(false); inactiveIdentityProvider.setOriginKey(inactiveAlias); MockMvcUtils.createIdpUsingWebRequest(getMockMvc(), identityZone.getId(), zoneAdminToken, inactiveIdentityProvider, status().isCreated()); getMockMvc().perform(get("/login").accept(TEXT_HTML).with(new SetServerNameRequestPostProcessor(identityZone.getSubdomain() + ".localhost"))) .andExpect(status().isOk()) .andExpect(xpath("//a[text()='" + activeSamlIdentityProviderDefinition.getLinkText() + "']").exists()) .andExpect(xpath("//a[text()='" + inactiveSamlIdentityProviderDefinition.getLinkText() + "']").doesNotExist()); } @Test public void testSamlRedirectWhenTheOnlyProvider() throws Exception { String alias = "login-saml-"+generator.generate(); 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(), getMockMvc(), getWebApplicationContext(), zoneAdminClient); IdentityZone identityZone = identityZoneCreationResult.getIdentityZone(); String zoneAdminToken = identityZoneCreationResult.getZoneAdminToken(); String metadata = String.format(MockMvcUtils.IDP_META_DATA, new RandomValueStringGenerator().generate()); SamlIdentityProviderDefinition activeSamlIdentityProviderDefinition = new SamlIdentityProviderDefinition() .setMetaDataLocation(metadata) .setIdpEntityAlias(alias) .setLinkText("Active SAML Provider") .setZoneId(identityZone.getId()); IdentityProvider activeIdentityProvider = new IdentityProvider(); activeIdentityProvider.setType(OriginKeys.SAML); activeIdentityProvider.setName("Active SAML Provider"); activeIdentityProvider.setActive(true); activeIdentityProvider.setConfig(activeSamlIdentityProviderDefinition); activeIdentityProvider.setOriginKey(alias); activeIdentityProvider = MockMvcUtils.createIdpUsingWebRequest(getMockMvc(), identityZone.getId(), zoneAdminToken, activeIdentityProvider, status().isCreated()); zoneAdminClient.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, Collections.singletonList(activeIdentityProvider.getOriginKey())); MockMvcUtils.updateClient(getMockMvc(), zoneAdminToken, zoneAdminClient, identityZone); MockHttpSession session = new MockHttpSession(); SavedRequest savedRequest = new MockMvcUtils.MockSavedRequest(); session.setAttribute(SAVED_REQUEST_SESSION_ATTRIBUTE, savedRequest); getMockMvc().perform(get("/login") .accept(TEXT_HTML) .session(session) .with(new SetServerNameRequestPostProcessor(identityZone.getSubdomain() + ".localhost"))) .andExpect(status().isFound()) .andExpect(redirectedUrl("/saml/discovery?returnIDParam=idp&entityID=" + identityZone.getSubdomain() + ".cloudfoundry-saml-login&idp=" + alias + "&isPassive=true")); getMockMvc().perform(get("/login") .accept(APPLICATION_JSON) .session(session) .with(new SetServerNameRequestPostProcessor(identityZone.getSubdomain() + ".localhost"))) .andExpect(status().isOk()); IdentityProviderProvisioning provisioning = getWebApplicationContext().getBean(JdbcIdentityProviderProvisioning.class); IdentityProvider uaaProvider = provisioning.retrieveByOrigin(UAA, identityZone.getId()); try { IdentityZoneHolder.set(identityZone); uaaProvider.setActive(false); provisioning.update(uaaProvider); getMockMvc().perform(get("/login") .accept(APPLICATION_JSON) .session(session) .with(new SetServerNameRequestPostProcessor(identityZone.getSubdomain() + ".localhost"))) .andExpect(status().isOk()); }finally { IdentityZoneHolder.set(identityZone); uaaProvider.setActive(true); provisioning.update(uaaProvider); IdentityZoneHolder.clear(); } } @Test public void samlRedirect_onlyOneProvider_noClientContext() throws Exception { String alias = "login-saml-"+generator.generate(); 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(), getMockMvc(), getWebApplicationContext(), zoneAdminClient); IdentityZone identityZone = identityZoneCreationResult.getIdentityZone(); String zoneAdminToken = identityZoneCreationResult.getZoneAdminToken(); String metadata = String.format(MockMvcUtils.IDP_META_DATA, new RandomValueStringGenerator().generate()); SamlIdentityProviderDefinition activeSamlIdentityProviderDefinition = new SamlIdentityProviderDefinition() .setMetaDataLocation(metadata) .setIdpEntityAlias(alias) .setLinkText("Active SAML Provider") .setZoneId(identityZone.getId()); IdentityProvider activeIdentityProvider = new IdentityProvider(); activeIdentityProvider.setType(OriginKeys.SAML); activeIdentityProvider.setName("Active SAML Provider"); activeIdentityProvider.setActive(true); activeIdentityProvider.setConfig(activeSamlIdentityProviderDefinition); activeIdentityProvider.setOriginKey(alias); MockMvcUtils.createIdpUsingWebRequest(getMockMvc(), identityZone.getId(), zoneAdminToken, activeIdentityProvider, status().isCreated()); IdentityZoneHolder.set(identityZone); IdentityProviderProvisioning identityProviderProvisioning = getWebApplicationContext().getBean(JdbcIdentityProviderProvisioning.class); IdentityProvider uaaIdentityProvider = identityProviderProvisioning.retrieveByOrigin(UAA, identityZone.getId()); uaaIdentityProvider.setActive(false); identityProviderProvisioning.update(uaaIdentityProvider); getMockMvc().perform(get("/login").accept(TEXT_HTML).with(new SetServerNameRequestPostProcessor(identityZone.getSubdomain() + ".localhost")) .with(new SetServerNameRequestPostProcessor(identityZone.getSubdomain() + ".localhost"))) .andExpect(status().isFound()) .andExpect(redirectedUrl("/saml/discovery?returnIDParam=idp&entityID=" + identityZone.getSubdomain() + ".cloudfoundry-saml-login&idp="+alias+"&isPassive=true")); IdentityZoneHolder.clear(); } @Test public void xOAuthRedirect_onlyOneProvider_noClientContext_and_ResponseType_Set() 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(), getMockMvc(), getWebApplicationContext(), zoneAdminClient); IdentityZone identityZone = identityZoneCreationResult.getIdentityZone(); String zoneAdminToken = identityZoneCreationResult.getZoneAdminToken(); OIDCIdentityProviderDefinition definition = new OIDCIdentityProviderDefinition(); definition.setAuthUrl(new URL("http://auth.url")); definition.setTokenUrl(new URL("http://token.url")); definition.setTokenKey("key"); definition.setRelyingPartyId("uaa"); definition.setRelyingPartySecret("secret"); definition.setShowLinkText(false); definition.setScopes(asList("openid", "roles")); definition.setResponseType("code id_token"); String oauthAlias = "login-oauth-" + generator.generate(); IdentityProvider<OIDCIdentityProviderDefinition> oauthIdentityProvider = MultitenancyFixture.identityProvider(oauthAlias, "uaa"); oauthIdentityProvider.setConfig(definition); oauthIdentityProvider.setActive(true); MockMvcUtils.createIdpUsingWebRequest(getMockMvc(), identityZone.getId(), zoneAdminToken, oauthIdentityProvider, status().isCreated()); IdentityZoneHolder.set(identityZone); IdentityProviderProvisioning identityProviderProvisioning = getWebApplicationContext().getBean(JdbcIdentityProviderProvisioning.class); IdentityProvider uaaIdentityProvider = identityProviderProvisioning.retrieveByOrigin(UAA, identityZone.getId()); uaaIdentityProvider.setActive(false); identityProviderProvisioning.update(uaaIdentityProvider); getMockMvc().perform(get("/login").accept(TEXT_HTML) .servletPath("/login") .with(new SetServerNameRequestPostProcessor(identityZone.getSubdomain() + ".localhost"))) .andExpect(status().isFound()) .andExpect( header() .string("Location", startsWith("http://auth.url?client_id=uaa&response_type=code+id_token&redirect_uri=http%3A%2F%2F" + identityZone.getSubdomain() + ".localhost%2Flogin%2Fcallback%2F" + oauthAlias + "&scope=openid+roles&nonce=") ) ); IdentityZoneHolder.clear(); } @Test public void testLoginHintRedirect() 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"); MockMvcUtils.IdentityZoneCreationResult identityZoneCreationResult = MockMvcUtils.createOtherIdentityZoneAndReturnResult("puppy-" + new RandomValueStringGenerator().generate(), getMockMvc(), getWebApplicationContext(), zoneAdminClient); IdentityZone identityZone = identityZoneCreationResult.getIdentityZone(); String zoneAdminToken = identityZoneCreationResult.getZoneAdminToken(); OIDCIdentityProviderDefinition definition = new OIDCIdentityProviderDefinition(); definition.setAuthUrl(new URL("http://auth.url")); definition.setTokenUrl(new URL("http://token.url")); definition.setTokenKey("key"); definition.setRelyingPartyId("uaa"); definition.setRelyingPartySecret("secret"); definition.setShowLinkText(false); definition.setScopes(asList("openid", "roles")); String oauthAlias = "login-oauth-" + generator.generate(); IdentityProvider<OIDCIdentityProviderDefinition> oauthIdentityProvider = MultitenancyFixture.identityProvider(oauthAlias, "uaa"); oauthIdentityProvider.setConfig(definition); oauthIdentityProvider.setActive(true); oauthIdentityProvider.getConfig().setEmailDomain(singletonList("example.com")); MockMvcUtils.createIdpUsingWebRequest(getMockMvc(), identityZone.getId(), zoneAdminToken, oauthIdentityProvider, status().isCreated()); IdentityZoneHolder.set(identityZone); MockHttpSession session = new MockHttpSession(); SavedRequest savedRequest = mock(DefaultSavedRequest.class); when(savedRequest.getParameterValues("login_hint")).thenReturn(new String[] { "example.com" }); session.putValue(SAVED_REQUEST_SESSION_ATTRIBUTE, savedRequest); getMockMvc().perform(get("/login") .accept(TEXT_HTML) .session(session) .servletPath("/login") .with(new SetServerNameRequestPostProcessor(identityZone.getSubdomain() + ".localhost")) ) .andExpect(status().isFound()) .andExpect( header() .string("Location", startsWith("http://auth.url?client_id=uaa&response_type=code&redirect_uri=http%3A%2F%2F" + identityZone.getSubdomain() + ".localhost%2Flogin%2Fcallback%2F" + oauthAlias + "&scope=openid+roles&nonce=") ) ); IdentityZoneHolder.clear(); } @Test public void noRedirect_ifProvidersOfDifferentTypesPresent() throws Exception { String alias = "login-saml-"+generator.generate(); 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(), getMockMvc(), getWebApplicationContext(), zoneAdminClient); IdentityZone identityZone = identityZoneCreationResult.getIdentityZone(); String zoneAdminToken = identityZoneCreationResult.getZoneAdminToken(); String metadata = String.format(MockMvcUtils.IDP_META_DATA, new RandomValueStringGenerator().generate()); SamlIdentityProviderDefinition activeSamlIdentityProviderDefinition = new SamlIdentityProviderDefinition() .setMetaDataLocation(metadata) .setIdpEntityAlias(alias) .setLinkText("Active SAML Provider") .setZoneId(identityZone.getId()); IdentityProvider activeIdentityProvider = new IdentityProvider(); activeIdentityProvider.setType(OriginKeys.SAML); activeIdentityProvider.setName("Active SAML Provider"); activeIdentityProvider.setActive(true); activeIdentityProvider.setConfig(activeSamlIdentityProviderDefinition); activeIdentityProvider.setOriginKey(alias); activeIdentityProvider = MockMvcUtils.createIdpUsingWebRequest(getMockMvc(), identityZone.getId(), zoneAdminToken, activeIdentityProvider, status().isCreated()); OIDCIdentityProviderDefinition definition = new OIDCIdentityProviderDefinition(); definition.setAuthUrl(new URL("http://auth.url")); definition.setTokenUrl(new URL("http://token.url")); definition.setTokenKey("key"); definition.setRelyingPartyId("UAA"); definition.setRelyingPartySecret("secret"); definition.setShowLinkText(false); String oauthAlias = "login-oauth-" + generator.generate(); IdentityProvider<OIDCIdentityProviderDefinition> oauthIdentityProvider = MultitenancyFixture.identityProvider(oauthAlias, "uaa"); oauthIdentityProvider.setConfig(definition); oauthIdentityProvider.setActive(true); MockMvcUtils.createIdpUsingWebRequest(getMockMvc(), identityZone.getId(), zoneAdminToken, oauthIdentityProvider, status().isCreated()); IdentityZoneHolder.set(identityZone); IdentityProviderProvisioning identityProviderProvisioning = getWebApplicationContext().getBean(JdbcIdentityProviderProvisioning.class); IdentityProvider uaaIdentityProvider = identityProviderProvisioning.retrieveByOrigin(UAA, identityZone.getId()); uaaIdentityProvider.setActive(false); identityProviderProvisioning.update(uaaIdentityProvider); getMockMvc().perform(get("/login").accept(TEXT_HTML).with(new SetServerNameRequestPostProcessor(identityZone.getSubdomain() + ".localhost")) .with(new SetServerNameRequestPostProcessor(identityZone.getSubdomain() + ".localhost"))) .andExpect(status().isOk()) .andExpect(view().name("login")); IdentityZoneHolder.clear(); } @Test public void testNoCreateAccountLinksWhenUAAisNotAllowedProvider() throws Exception { String alias2 = "login-saml-"+generator.generate(); String alias3 = "login-saml-"+generator.generate(); 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(), getMockMvc(), getWebApplicationContext(), zoneAdminClient); IdentityZone identityZone = identityZoneCreationResult.getIdentityZone(); String zoneAdminToken = identityZoneCreationResult.getZoneAdminToken(); SamlIdentityProviderDefinition activeSamlIdentityProviderDefinition3 = new SamlIdentityProviderDefinition() .setMetaDataLocation(String.format(BootstrapSamlIdentityProviderConfiguratorTests.xmlWithoutID, "http://example3.com/saml/metadata")) .setIdpEntityAlias(alias3) .setLinkText("Active3 SAML Provider") .setZoneId(identityZone.getId()); IdentityProvider activeIdentityProvider3 = new IdentityProvider(); activeIdentityProvider3.setType(OriginKeys.SAML); activeIdentityProvider3.setName("Active 3 SAML Provider"); activeIdentityProvider3.setActive(true); activeIdentityProvider3.setConfig(activeSamlIdentityProviderDefinition3); activeIdentityProvider3.setOriginKey(alias3); activeIdentityProvider3 = MockMvcUtils.createIdpUsingWebRequest(getMockMvc(), identityZone.getId(), zoneAdminToken, activeIdentityProvider3, status().isCreated()); SamlIdentityProviderDefinition activeSamlIdentityProviderDefinition2 = new SamlIdentityProviderDefinition() .setMetaDataLocation(String.format(BootstrapSamlIdentityProviderConfiguratorTests.xmlWithoutID, "http://example2.com/saml/metadata")) .setIdpEntityAlias(alias2) .setLinkText("Active2 SAML Provider") .setZoneId(identityZone.getId()); IdentityProvider activeIdentityProvider2 = new IdentityProvider(); activeIdentityProvider2.setType(OriginKeys.SAML); activeIdentityProvider2.setName("Active 2 SAML Provider"); activeIdentityProvider2.setActive(true); activeIdentityProvider2.setConfig(activeSamlIdentityProviderDefinition2); activeIdentityProvider2.setOriginKey(alias2); activeIdentityProvider2 = MockMvcUtils.createIdpUsingWebRequest(getMockMvc(), identityZone.getId(), zoneAdminToken, activeIdentityProvider2, status().isCreated()); zoneAdminClient.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, asList(activeIdentityProvider3.getOriginKey(), activeIdentityProvider2.getOriginKey())); MockMvcUtils.updateClient(getMockMvc(), 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<Cookie> getCookies() { return null; } @Override public String getMethod() { return null; } @Override public List<String> getHeaderValues(String name) { return null; } @Override public Collection<String> getHeaderNames() { return null; } @Override public List<Locale> getLocales() { return null; } @Override public Map<String, String[]> getParameterMap() { return null; } }; session.setAttribute(SAVED_REQUEST_SESSION_ATTRIBUTE, savedRequest); getMockMvc().perform(get("/login").accept(TEXT_HTML).with(new SetServerNameRequestPostProcessor(identityZone.getSubdomain() + ".localhost")) .session(session) .with(new SetServerNameRequestPostProcessor(identityZone.getSubdomain() + ".localhost"))) .andExpect(status().isOk()) .andExpect(xpath("//a[text()='Create account']").doesNotExist()) .andExpect(xpath("//a[text()='Reset password']").doesNotExist()); } @Test public void testDeactivatedProviderIsRemovedFromSamlLoginLinks() throws Exception { String alias = "login-saml-"+generator.generate(); BaseClientDetails zoneAdminClient = new BaseClientDetails("admin", null, null, "client_credentials", "clients.admin,scim.read,scim.write"); zoneAdminClient.setClientSecret("admin-secret"); IdentityZoneCreationResult identityZoneCreationResult = MockMvcUtils.createOtherIdentityZoneAndReturnResult("puppy-" + new RandomValueStringGenerator().generate(), getMockMvc(), getWebApplicationContext(), zoneAdminClient); IdentityZone identityZone = identityZoneCreationResult.getIdentityZone(); String zoneAdminToken = identityZoneCreationResult.getZoneAdminToken(); String metadata = String.format(MockMvcUtils.IDP_META_DATA, new RandomValueStringGenerator().generate()); SamlIdentityProviderDefinition samlIdentityProviderDefinition = new SamlIdentityProviderDefinition() .setMetaDataLocation(metadata) .setIdpEntityAlias(alias) .setLinkText("SAML Provider") .setShowSamlLink(true) .setZoneId(identityZone.getId()); IdentityProvider identityProvider = new IdentityProvider(); identityProvider.setType(OriginKeys.SAML); identityProvider.setName("SAML Provider"); identityProvider.setActive(true); identityProvider.setConfig(samlIdentityProviderDefinition); identityProvider.setOriginKey(alias); identityProvider = MockMvcUtils.createIdpUsingWebRequest(getMockMvc(), identityZone.getId(), zoneAdminToken, identityProvider, status().isCreated()); getMockMvc().perform(get("/login").accept(TEXT_HTML).with(new SetServerNameRequestPostProcessor(identityZone.getSubdomain() + ".localhost"))) .andExpect(status().isOk()) .andExpect(xpath("//a[text()='" + samlIdentityProviderDefinition.getLinkText() + "']").exists()); identityProvider.setActive(false); MockMvcUtils.createIdpUsingWebRequest(getMockMvc(), identityZone.getId(), zoneAdminToken, identityProvider, status().isOk(), true); getMockMvc().perform(get("/login").accept(TEXT_HTML).with(new SetServerNameRequestPostProcessor(identityZone.getSubdomain() + ".localhost"))) .andExpect(status().isOk()) .andExpect(xpath("//a[text()='" + samlIdentityProviderDefinition.getLinkText() + "']").doesNotExist()); } @Test public void testChangeEmailWithoutAuthenticationReturnsRedirect() throws Exception { getMockMvc().perform(get("/change_email").accept(TEXT_HTML)) .andExpect(status().isFound()) .andExpect(redirectedUrl("http://localhost/login")); } @Test public void testChangeEmailPageHasCsrf() throws Exception { SecurityContext marissaContext = getMarissaSecurityContext(getWebApplicationContext()); MockHttpServletRequestBuilder get = get("/change_email") .accept(TEXT_HTML) .with(securityContext(marissaContext)); getMockMvc().perform(get) .andExpect(status().isOk()) .andExpect(content().string(containsString("_csrf"))); } @Test public void testChangeEmailSubmitWithMissingCsrf() throws Exception { SecurityContext marissaContext = getMarissaSecurityContext(getWebApplicationContext()); MockHttpServletRequestBuilder get = get("/change_email") .accept(TEXT_HTML) .with(securityContext(marissaContext)); MockHttpSession session = (MockHttpSession) getMockMvc().perform(get) .andExpect(status().isOk()) .andExpect(content().string(containsString("_csrf"))) .andReturn().getRequest().getSession(); assertNotNull(session.getAttribute(HttpSessionCsrfTokenRepository.class.getName().concat(".CSRF_TOKEN"))); MockHttpServletRequestBuilder changeEmail = post("/change_email.do") .accept(TEXT_HTML) .session(session) .with(securityContext(marissaContext)) .param("newEmail", "test@test.org") .param("client_id", ""); getMockMvc().perform(changeEmail) .andExpect(status().isFound()) .andExpect(redirectedUrl("http://localhost/invalid_request")); } @Test public void testChangeEmailSubmitWithInvalidCsrf() throws Exception { SecurityContext marissaContext = getMarissaSecurityContext(getWebApplicationContext()); MockHttpServletRequestBuilder get = get("/change_email") .accept(TEXT_HTML) .with(securityContext(marissaContext)); MockHttpSession session = (MockHttpSession) getMockMvc().perform(get) .andExpect(status().isOk()) .andExpect(content().string(containsString("_csrf"))) .andReturn().getRequest().getSession(); assertNotNull(session.getAttribute(HttpSessionCsrfTokenRepository.class.getName().concat(".CSRF_TOKEN"))); MockHttpServletRequestBuilder changeEmail = post("/change_email.do") .accept(TEXT_HTML) .session(session) .with(securityContext(marissaContext)) .param("newEmail", "test@test.org") .param("client_id", "") .param("_csrf", "invalid csrf token"); getMockMvc().perform(changeEmail) .andExpect(status().isFound()) .andExpect(redirectedUrl("http://localhost/invalid_request")); } @Test public void testChangeEmailSubmitWithSpringSecurityForcedCsrf() throws Exception { SecurityContext marissaContext = getMarissaSecurityContext(getWebApplicationContext()); //example shows to to test a request that is secured by csrf and you wish to bypass it MockHttpServletRequestBuilder changeEmail = post("/change_email.do") .accept(TEXT_HTML) .with(securityContext(marissaContext)) .with(csrf()) .param("newEmail", "test@test.org") .param("client_id", ""); HttpSession session = getMockMvc().perform(changeEmail) .andExpect(status().isFound()) .andExpect(redirectedUrl("email_sent?code=email_change")) .andReturn().getRequest().getSession(false); System.out.println("session = " + session); } @Test public void testChangeEmailSubmitWithCorrectCsrf() throws Exception { SecurityContext marissaContext = getMarissaSecurityContext(getWebApplicationContext()); MockHttpServletRequestBuilder get = get("/change_email") .accept(TEXT_HTML) .with(securityContext(marissaContext)); MvcResult result = getMockMvc().perform(get) .andExpect(status().isOk()) .andExpect(content().string(containsString("_csrf"))) .andReturn(); MockHttpSession session = (MockHttpSession)result.getRequest().getSession(); CsrfToken csrfToken = (CsrfToken)session.getAttribute(HttpSessionCsrfTokenRepository.class.getName().concat(".CSRF_TOKEN")); MockHttpServletRequestBuilder changeEmail = post("/change_email.do") .accept(TEXT_HTML) .with(securityContext(marissaContext)) .session(session) .param("newEmail", "test@test.org") .param("client_id", "") .param("_csrf", csrfToken.getToken()); getMockMvc().perform(changeEmail) .andExpect(status().isFound()) .andExpect(redirectedUrl("email_sent?code=email_change")); } @Test public void testChangeEmailDoNotLoggedIn() throws Exception { SecurityContext marissaContext = getMarissaSecurityContext(getWebApplicationContext()); MockHttpServletRequestBuilder changeEmail = post("/change_email.do") .accept(TEXT_HTML) .with(csrf()); getMockMvc().perform(changeEmail) .andExpect(status().isFound()) .andExpect(redirectedUrl("http://localhost/login")); changeEmail = post("/change_email.do") .accept(TEXT_HTML) .with(csrf().useInvalidToken()); getMockMvc().perform(changeEmail) .andExpect(status().isFound()) .andExpect(redirectedUrl("http://localhost/invalid_request")); changeEmail = post("/change_email.do") .accept(TEXT_HTML) .with(csrf().useInvalidToken()) .with(securityContext(marissaContext)); getMockMvc().perform(changeEmail) .andExpect(status().isFound()) .andExpect(redirectedUrl("http://localhost/invalid_request")); } @Test public void testChangeEmailNoCsrfReturns403AndInvalidRequest() throws Exception { SecurityContext marissaContext = getMarissaSecurityContext(getWebApplicationContext()); MockHttpServletRequestBuilder get = get("/change_email") .accept(TEXT_HTML) .with(securityContext(marissaContext)); getMockMvc().perform(get) .andExpect(status().isOk()) .andExpect(content().string(containsString("_csrf"))) .andReturn(); MockHttpServletRequestBuilder changeEmail = post("/change_email.do") .accept(TEXT_HTML) .with(securityContext(marissaContext)) .param("newEmail", "test@test.org") .param("client_id", ""); getMockMvc().perform(changeEmail) .andExpect(status().isFound()) .andExpect(redirectedUrl("http://localhost/invalid_request")); } @Test public void testCsrfForInvitationAcceptPost() throws Exception { SecurityContext marissaContext = getMarissaSecurityContext(getWebApplicationContext()); AnonymousAuthenticationToken inviteToken = new AnonymousAuthenticationToken("invited-test", marissaContext.getAuthentication().getPrincipal(), asList(UaaAuthority.UAA_INVITED)); MockHttpSession inviteSession = new MockHttpSession(); SecurityContext inviteContext = new SecurityContextImpl(); inviteContext.setAuthentication(inviteToken); inviteSession.setAttribute("SPRING_SECURITY_CONTEXT", inviteContext); ExpiringCode code = getWebApplicationContext().getBean(ExpiringCodeStore.class).generateCode("{ \"origin\" : \"uaa\"}", new Timestamp(System.currentTimeMillis() + 1000 * 60), null); //logged in with valid CSRF MockHttpServletRequestBuilder post = post("/invitations/accept.do") .session(inviteSession) .with(csrf()) .param("code",code.getCode()) .param("client_id", "random") .param("password", "password") .param("password_confirmation", "yield_unprocessable_entity"); getMockMvc().perform(post) .andExpect(status().isFound()) .andExpect(redirectedUrlPattern("accept?error_message_code=form_error&code=*")) ; //logged in, invalid CSRF post = post("/invitations/accept.do") .session(inviteSession) .with(csrf().useInvalidToken()) .param("client_id", "random") .param("password", "password") .param("password_confirmation", "yield_unprocessable_entity"); getMockMvc().perform(post) .andExpect(status().isFound()) .andExpect(redirectedUrl("http://localhost/invalid_request")); //not logged in, no CSRF post = post("/invitations/accept.do") .param("client_id", "random") .param("password", "password") .param("password_confirmation", "yield_unprocessable_entity"); getMockMvc().perform(post) .andExpect(status().isFound()) .andExpect(redirectedUrl("http://localhost/invalid_request")); //not logged in, valid CSRF(can't happen) post = post("/invitations/accept.do") .with(csrf()) .param("client_id", "random") .param("password", "password") .param("code", "notvalidated") .param("password_confirmation", "yield_unprocessable_entity"); getMockMvc().perform(post) .andExpect(status().isFound()) .andExpect(redirectedUrl("http://localhost/login")); } /** * Positive test case that exercises the CORS logic for dealing with the "X-Requested-With" header. * * @throws Exception */ @Test public void testLogOutCorsPreflight() throws Exception { CorsFilter corsFilter = getWebApplicationContext().getBean(CorsFilter.class); corsFilter.setCorsXhrAllowedOrigins(asList("^localhost$", "^*\\.localhost$")); corsFilter.setCorsXhrAllowedUris(asList("^/logout\\.do$")); corsFilter.initialize(); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.add("Access-Control-Request-Headers", "X-Requested-With"); httpHeaders.add("Access-Control-Request-Method", "GET"); httpHeaders.add("Origin", "localhost"); getMockMvc().perform(options("/logout.do").headers(httpHeaders)).andExpect(status().isOk()); } /** * Positive test case that exercises the CORS logic for dealing with the "X-Requested-With" header. * * @throws Exception */ @Test public void testLogOutCorsPreflightForIdentityZone() throws Exception { CorsFilter corsFilter = getWebApplicationContext().getBean(CorsFilter.class); corsFilter.setCorsXhrAllowedOrigins(asList("^localhost$", "^*\\.localhost$")); corsFilter.setCorsXhrAllowedUris(asList("^/logout.do$")); corsFilter.initialize(); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.add("Access-Control-Request-Headers", "X-Requested-With"); httpHeaders.add("Access-Control-Request-Method", "GET"); httpHeaders.add("Origin", "testzone1.localhost"); getMockMvc().perform(options("/logout.do").headers(httpHeaders)).andExpect(status().isOk()); } /** * This should avoid the logic for X-Requested-With header entirely. * * @throws Exception on test failure */ @Test public void testLogOutCorsPreflightWithStandardHeader() throws Exception { CorsFilter corsFilter = getWebApplicationContext().getBean(CorsFilter.class); corsFilter.setCorsXhrAllowedOrigins(asList("^localhost$")); corsFilter.setCorsXhrAllowedUris(asList("^/logout\\.do$")); corsFilter.initialize(); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.add("Access-Control-Request-Headers", "Accept"); httpHeaders.add("Access-Control-Request-Method", "GET"); httpHeaders.add("Origin", "localhost"); getMockMvc().perform(options("/logout.do").headers(httpHeaders)).andExpect(status().isOk()); } /** * The endpoint is not white-listed to allow CORS requests with the "X-Requested-With" header so the * CorsFilter returns a 403. * * @throws Exception on test failure */ @Test public void testLogOutCorsPreflightWithUnallowedEndpoint() throws Exception { CorsFilter corsFilter = getWebApplicationContext().getBean(CorsFilter.class); corsFilter.setCorsXhrAllowedOrigins(asList("^localhost$")); corsFilter.setCorsXhrAllowedUris(asList("^/logout\\.do$")); corsFilter.initialize(); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.add("Access-Control-Request-Headers", "X-Requested-With"); httpHeaders.add("Access-Control-Request-Method", "GET"); httpHeaders.add("Origin", "localhost"); getMockMvc().perform(options("/logout.dont").headers(httpHeaders)).andExpect(status().isForbidden()); } /** * The access control request method is not a GET therefore CORS requests with the "X-Requested-With" * header are not allowed and the CorsFilter returns a 405. * * @throws Exception on test failure */ @Test public void testLogOutCorsPreflightWithUnallowedMethod() throws Exception { CorsFilter corsFilter = getWebApplicationContext().getBean(CorsFilter.class); corsFilter.setCorsXhrAllowedOrigins(asList("^localhost$")); corsFilter.setCorsXhrAllowedUris(asList("^/logout\\.do$")); corsFilter.initialize(); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.add("Access-Control-Request-Headers", "X-Requested-With"); httpHeaders.add("Access-Control-Request-Method", "POST"); httpHeaders.add("Origin", "localhost"); getMockMvc().perform(options("/logout.do").headers(httpHeaders)).andExpect(status().isMethodNotAllowed()); } /** * The request origin is not white-listed to allow CORS requests with the "X-Requested-With" header so the * CorsFilter returns a 403. * * @throws Exception on test failure */ @Test public void testLogOutCorsPreflightWithUnallowedOrigin() throws Exception { CorsFilter corsFilter = getWebApplicationContext().getBean(CorsFilter.class); corsFilter.setCorsXhrAllowedOrigins(asList("^localhost$")); corsFilter.setCorsXhrAllowedUris(asList("^/logout\\.do$")); corsFilter.initialize(); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.add("Access-Control-Request-Headers", "X-Requested-With"); httpHeaders.add("Access-Control-Request-Method", "GET"); httpHeaders.add("Origin", "fuzzybunnies.com"); getMockMvc().perform(options("/logout.do").headers(httpHeaders)).andExpect(status().isForbidden()); } @Test public void login_LockoutPolicySucceeds_ForDefaultZone() throws Exception { ScimUser userToLockout = createUser("", adminToken); attemptFailedLogin(5, userToLockout.getUserName(), ""); getMockMvc().perform(post("/uaa/login.do") .contextPath("/uaa") .with(cookieCsrf()) .param("username", userToLockout.getUserName()) .param("password", userToLockout.getPassword())) .andExpect(redirectedUrl("/uaa/login?error=account_locked")) .andExpect(emptyCurrentUserCookie()); } @Test public void login_LockoutPolicySucceeds_WhenPolicyIsUpdatedByApi() throws Exception { String subdomain = generator.generate(); IdentityZone zone = createOtherIdentityZone(subdomain, getMockMvc(), getWebApplicationContext()); changeLockoutPolicyForIdpInZone(zone); String zoneAdminToken = testClient.getClientCredentialsOAuthAccessToken("admin", "admin-secret", "scim.write,idps.write", zone.getSubdomain()); ScimUser userToLockout = createUser(subdomain, zoneAdminToken); attemptFailedLogin(2, userToLockout.getUserName(), subdomain); getMockMvc().perform(post("/uaa/login.do") .contextPath("/uaa") .with(new SetServerNameRequestPostProcessor(subdomain + ".localhost")) .with(cookieCsrf()) .param("username", userToLockout.getUserName()) .param("password", userToLockout.getPassword())) .andExpect(redirectedUrl("/uaa/login?error=account_locked")) .andExpect(emptyCurrentUserCookie()); } @Test public void autologin_with_validCode_RedirectsToSavedRequest_ifPresent() throws Exception { MockHttpSession session = MockMvcUtils.getSavedRequestSession(); MockMvcUtils.PredictableGenerator generator = new MockMvcUtils.PredictableGenerator(); JdbcExpiringCodeStore store = getWebApplicationContext().getBean(JdbcExpiringCodeStore.class); store.setGenerator(generator); AutologinRequest request = new AutologinRequest(); request.setUsername("marissa"); request.setPassword("koala"); getMockMvc().perform(post("/autologin") .header("Authorization", "Basic " + new String(Base64.encode("admin:adminsecret".getBytes()))) .contentType(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(request))) .andExpect(status().isOk()); getMockMvc().perform(get("/autologin") .session(session) .param("code", "test" + generator.counter.get()) .param("client_id", "admin")) .andExpect(redirectedUrl("http://test/redirect/oauth/authorize")); } @Test public void autologin_with_validCode_RedirectsToHome() throws Exception { MockMvcUtils.PredictableGenerator generator = new MockMvcUtils.PredictableGenerator(); JdbcExpiringCodeStore store = getWebApplicationContext().getBean(JdbcExpiringCodeStore.class); store.setGenerator(generator); AutologinRequest request = new AutologinRequest(); request.setUsername("marissa"); request.setPassword("koala"); getMockMvc().perform(post("/autologin") .header("Authorization", "Basic " + new String(Base64.encode("admin:adminsecret".getBytes()))) .contentType(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(request))) .andExpect(status().isOk()); getMockMvc().perform(get("/autologin") .param("code", "test" + generator.counter.get()) .param("client_id", "admin")) .andExpect(redirectedUrl("home")); } @Test public void idpDiscoveryPageDisplayed_IfFlagIsEnabled() throws Exception { IdentityZoneConfiguration config = new IdentityZoneConfiguration(); config.setIdpDiscoveryEnabled(true); IdentityZone zone = setupZone(config); getMockMvc().perform(get("/login") .header("Accept", TEXT_HTML) .with(new SetServerNameRequestPostProcessor(zone.getSubdomain() + ".localhost"))) .andExpect(status().isOk()) .andExpect(view().name("idp_discovery/email")) .andExpect(content().string(containsString("Sign in"))) .andExpect(xpath("//input[@name='email']").exists()) .andExpect(xpath("//div[@class='action']//a").string("Create account")) .andExpect(xpath("//input[@type='submit']/@value").string("Next")); } @Test public void idpDiscoveryPageNotDisplayed_IfFlagIsEnabledAndDiscoveryFailedPreviously() throws Exception { IdentityZoneConfiguration config = new IdentityZoneConfiguration(); config.setIdpDiscoveryEnabled(true); IdentityZone zone = setupZone(config); getMockMvc().perform(get("/login?discoveryPerformed=true") .header("Accept", TEXT_HTML) .with(new SetServerNameRequestPostProcessor(zone.getSubdomain() + ".localhost"))) .andExpect(status().isOk()) .andExpect(view().name("login")); } @Test public void idpDiscoveryClientNameDisplayed_WithUTF8Characters() throws Exception { String utf8String = "\u7433\u8D3A"; String clientName = "woohoo-"+utf8String; IdentityZoneConfiguration config = new IdentityZoneConfiguration(); config.setIdpDiscoveryEnabled(true); IdentityZone zone = setupZone(config); MockHttpSession session = new MockHttpSession(); String clientId = generator.generate(); BaseClientDetails client = new BaseClientDetails(clientId, "", "", "client_credentials", "uaa.none", "http://*.wildcard.testing,http://testing.com"); client.setClientSecret("secret"); client.addAdditionalInformation(ClientConstants.CLIENT_NAME, clientName); MockMvcUtils.createClient(getMockMvc(), adminToken, client, zone); SavedRequest savedRequest = getSavedRequest(client); session.setAttribute(SAVED_REQUEST_SESSION_ATTRIBUTE, savedRequest); getMockMvc().perform(get("/login") .session(session) .header("Accept", TEXT_HTML) .with(new SetServerNameRequestPostProcessor(zone.getSubdomain() + ".localhost"))) .andExpect(status().isOk()) .andExpect(view().name("idp_discovery/email")) .andExpect(content().string(containsString("Sign in to continue to "+clientName))) .andExpect(xpath("//input[@name='email']").exists()) .andExpect(xpath("//div[@class='action']//a").string("Create account")) .andExpect(xpath("//input[@type='submit']/@value").string("Next")); } @Test public void accountChooserEnabled_NoSaveAccounts() throws Exception { String clientName = "woohoo"; IdentityZoneConfiguration config = new IdentityZoneConfiguration(); config.setIdpDiscoveryEnabled(true); config.setAccountChooserEnabled(true); IdentityZone zone = setupZone(config); MockHttpSession session = new MockHttpSession(); String clientId = generator.generate(); BaseClientDetails client = new BaseClientDetails(clientId, "", "", "client_credentials", "uaa.none", "http://*.wildcard.testing,http://testing.com"); client.setClientSecret("secret"); client.addAdditionalInformation(ClientConstants.CLIENT_NAME, clientName); MockMvcUtils.createClient(getMockMvc(), adminToken, client, zone); SavedAccountOption savedAccount = new SavedAccountOption(); savedAccount.setEmail("test@example.org"); savedAccount.setOrigin("uaa"); savedAccount.setUserId("1234-5678"); savedAccount.setUsername("test@example.org"); getMockMvc().perform(get("/login") .session(session) .header("Accept", TEXT_HTML) .with(new SetServerNameRequestPostProcessor(zone.getSubdomain() + ".localhost"))) .andExpect(status().isOk()) .andExpect(view().name("idp_discovery/email")); } @Test public void accountChooserEnabled() throws Exception { String clientName = "woohoo"; IdentityZoneConfiguration config = new IdentityZoneConfiguration(); config.setIdpDiscoveryEnabled(true); config.setAccountChooserEnabled(true); IdentityZone zone = setupZone(config); MockHttpSession session = new MockHttpSession(); String clientId = generator.generate(); BaseClientDetails client = new BaseClientDetails(clientId, "", "", "client_credentials", "uaa.none", "http://*.wildcard.testing,http://testing.com"); client.setClientSecret("secret"); client.addAdditionalInformation(ClientConstants.CLIENT_NAME, clientName); MockMvcUtils.createClient(getMockMvc(), adminToken, client, zone); SavedAccountOption savedAccount = new SavedAccountOption(); savedAccount.setEmail("test@example.org"); savedAccount.setOrigin("uaa"); savedAccount.setUserId("1234-5678"); savedAccount.setUsername("test@example.org"); getMockMvc().perform(get("/login") .session(session) .cookie(new Cookie("Saved-Account-12345678", URLEncoder.encode(JsonUtils.writeValueAsString(savedAccount)))) .header("Accept", TEXT_HTML) .with(new SetServerNameRequestPostProcessor(zone.getSubdomain() + ".localhost"))) .andExpect(status().isOk()) .andExpect(view().name("idp_discovery/account_chooser")); } @Test public void emailPageIdpDiscoveryEnabled_SelfServiceLinksDisabled() throws Exception { IdentityZoneConfiguration config = new IdentityZoneConfiguration(); config.setIdpDiscoveryEnabled(true); config.setLinks(new Links().setSelfService(new Links.SelfService().setSelfServiceLinksEnabled(false))); IdentityZone zone = setupZone(config); setSelfServiceLinksEnabled(false); getMockMvc().perform(MockMvcRequestBuilders.get("/login") .with(new SetServerNameRequestPostProcessor(zone.getSubdomain() + ".localhost"))) .andExpect(xpath("//div[@class='action']//a").doesNotExist()); } @Test public void idpDiscoveryRedirectsToSamlExternalProvider_withClientContext() throws Exception { IdentityZone zone = MultitenancyFixture.identityZone("test-saml", "test-saml"); createOtherIdentityZone(zone.getSubdomain(), getMockMvc(), getWebApplicationContext()); String originKey = generator.generate(); MockHttpSession session = setUpClientAndProviderForIdpDiscovery(originKey, zone); getMockMvc().perform(post("/login/idp_discovery") .header("Accept", TEXT_HTML) .session(session) .param("email", "marissa@test.org") .with(new SetServerNameRequestPostProcessor(zone.getSubdomain() + ".localhost"))) .andExpect(redirectedUrl("/saml/discovery?returnIDParam=idp&entityID=" + zone.getSubdomain() + ".cloudfoundry-saml-login&idp=" + originKey + "&isPassive=true")); } @Test public void idpDiscoveryRedirectsToOIDCProvider() throws Exception { IdentityZone zone = MultitenancyFixture.identityZone("oidc-idp-discovery", "oidc-idp-discovery"); createOtherIdentityZone(zone.getSubdomain(), getMockMvc(), getWebApplicationContext()); String originKey = createOIDCProvider(zone, "id_token code"); getMockMvc().perform(post("/login/idp_discovery") .header("Accept", TEXT_HTML) .servletPath("/login/idp_discovery") .param("email", "marissa@test.org") .with(new SetServerNameRequestPostProcessor(zone.getSubdomain() + ".localhost"))) .andExpect( header() .string( "Location", startsWith("http://myauthurl.com?client_id=id&response_type=id_token+code&redirect_uri=http%3A%2F%2Foidc-idp-discovery.localhost%2Flogin%2Fcallback%2F" +originKey+"&nonce=") ) ); } @Test public void multiple_oidc_providers_use_response_type_in_url() throws Exception { IdentityZone zone = MultitenancyFixture.identityZone("oidc-idp-discovery-multi", "oidc-idp-discovery-multi"); createOtherIdentityZone(zone.getSubdomain(), getMockMvc(), getWebApplicationContext()); String originKey = createOIDCProvider(zone); String originKey2 = createOIDCProvider(zone,"code id_token"); getMockMvc().perform(get("/login") .header("Accept", TEXT_HTML) .servletPath("/login") .with(new SetServerNameRequestPostProcessor(zone.getSubdomain() + ".localhost"))) .andExpect(status().isOk()) .andExpect(content().string(containsString("http://myauthurl.com?client_id=id&response_type=code&redirect_uri=http%3A%2F%2Foidc-idp-discovery-multi.localhost%2Flogin%2Fcallback%2F" +originKey+"&nonce="))) .andExpect(content().string(containsString("http://myauthurl.com?client_id=id&response_type=code+id_token&redirect_uri=http%3A%2F%2Foidc-idp-discovery-multi.localhost%2Flogin%2Fcallback%2F" +originKey2+"&nonce="))); } public String createOIDCProvider(IdentityZone zone) throws Exception { return createOIDCProvider(zone, null); } public String createOIDCProvider(IdentityZone zone, String responseType) throws Exception { String originKey = generator.generate(); AbstractXOAuthIdentityProviderDefinition definition = new OIDCIdentityProviderDefinition(); definition.setEmailDomain(asList("test.org")); definition.setAuthUrl(new URL("http://myauthurl.com")); definition.setTokenKey("key"); definition.setTokenUrl(new URL("http://mytokenurl.com")); definition.setRelyingPartyId("id"); definition.setRelyingPartySecret("secret"); definition.setLinkText("my oidc provider"); if (StringUtils.hasText(responseType)) { definition.setResponseType(responseType); } IdentityProvider identityProvider = MultitenancyFixture.identityProvider(originKey, zone.getId()); identityProvider.setType(OriginKeys.OIDC10); identityProvider.setConfig(definition); MockMvcUtils.createIdpUsingWebRequest(getMockMvc(), zone.getId(), adminToken, identityProvider, status().isCreated()); return originKey; } @Test public void idpDiscoveryWithNoEmailDomainMatch_withClientContext() throws Exception { IdentityZone zone = MultitenancyFixture.identityZone("jon", "jon"); createOtherIdentityZone(zone.getSubdomain(), getMockMvc(), getWebApplicationContext()); IdentityZoneHolder.set(zone); IdentityProviderProvisioning identityProviderProvisioning = getWebApplicationContext().getBean(JdbcIdentityProviderProvisioning.class); IdentityProvider identityProvider = identityProviderProvisioning.retrieveByOrigin("uaa", zone.getId()); identityProvider.setConfig(new AbstractIdentityProviderDefinition().setEmailDomain(Collections.singletonList("totally-different.org"))); identityProviderProvisioning.update(identityProvider); String originKey = generator.generate(); MockHttpSession session = setUpClientAndProviderForIdpDiscovery(originKey, zone); getMockMvc().perform(post("/login/idp_discovery") .header("Accept", TEXT_HTML) .session(session) .param("email", "marissa@other.domain") .with(new SetServerNameRequestPostProcessor(zone.getSubdomain() + ".localhost"))) .andExpect(redirectedUrl("/login?discoveryPerformed=true")); } @Test public void idpDiscoveryWithMultipleEmailDomainMatches_withClientContext() throws Exception { IdentityZone zone = MultitenancyFixture.identityZone("madhura", "madhura"); createOtherIdentityZone(zone.getSubdomain(), getMockMvc(), getWebApplicationContext()); IdentityZoneHolder.set(zone); IdentityProviderProvisioning identityProviderProvisioning = getWebApplicationContext().getBean(JdbcIdentityProviderProvisioning.class); IdentityProvider identityProvider = identityProviderProvisioning.retrieveByOrigin("uaa", zone.getId()); identityProvider.setConfig(new AbstractIdentityProviderDefinition().setEmailDomain(Collections.singletonList("test.org"))); identityProviderProvisioning.update(identityProvider); String originKey = generator.generate(); MockHttpSession session = setUpClientAndProviderForIdpDiscovery(originKey, zone); getMockMvc().perform(post("/login/idp_discovery") .header("Accept", TEXT_HTML) .session(session) .param("email", "marissa@test.org") .with(new SetServerNameRequestPostProcessor(zone.getSubdomain() + ".localhost"))) .andExpect(redirectedUrl("/login?discoveryPerformed=true")); } @Test public void idpDiscoveryWithUaaFallBack_withClientContext() throws Exception { IdentityZone zone = MultitenancyFixture.identityZone("uaa-fall-back", "uaa-fall-back"); createOtherIdentityZone(zone.getSubdomain(), getMockMvc(), getWebApplicationContext()); String originKey = generator.generate(); MockHttpSession session = setUpClientAndProviderForIdpDiscovery(originKey, zone); getMockMvc().perform(post("/login/idp_discovery") .header("Accept", TEXT_HTML) .session(session) .param("email", "marissa@other.domain") .with(new SetServerNameRequestPostProcessor(zone.getSubdomain() + ".localhost"))) .andExpect(model().attributeExists("zone_name")) .andExpect(view().name("idp_discovery/password")); } @Test public void idpDiscoveryWithLdap_withClientContext() throws Exception{ IdentityZone zone = MultitenancyFixture.identityZone("puppy-ldap", "puppy-ldap"); createOtherIdentityZone(zone.getSubdomain(), getMockMvc(), getWebApplicationContext()); IdentityProvider identityProvider = MultitenancyFixture.identityProvider(LDAP, zone.getId()); identityProvider.setType(LDAP); identityProvider.setConfig(new LdapIdentityProviderDefinition().setEmailDomain(Collections.singletonList("testLdap.org"))); MockMvcUtils.createIdpUsingWebRequest(getMockMvc(), zone.getId(), adminToken, identityProvider, status().isCreated()); String originKey = generator.generate(); MockHttpSession session = setUpClientAndProviderForIdpDiscovery(originKey, zone); getMockMvc().perform(post("/login/idp_discovery") .header("Accept", TEXT_HTML) .session(session) .param("email", "marissa@testLdap.org") .with(new SetServerNameRequestPostProcessor(zone.getSubdomain() + ".localhost"))) .andExpect(redirectedUrl("/login?discoveryPerformed=true")); } @Test public void passwordPageDisplayed_ifUaaIsFallbackIDPForEmailDomain() throws Exception { getMockMvc().perform(post("/login/idp_discovery") .header("Accept", TEXT_HTML) .param("email", "marissa@koala.com")) .andExpect(status().isOk()) .andExpect(view().name("idp_discovery/password")) .andExpect(xpath("//input[@name='password']").exists()) .andExpect(xpath("//h4[@id='email']").string("marissa@koala.com")) .andExpect(xpath("//div[@class='action pull-right']//a").string("Reset password")) .andExpect(xpath("//input[@type='submit']/@value").string("Sign in")); } @Test public void passwordPageIdpDiscoveryEnabled_SelfServiceLinksDisabled() throws Exception { setSelfServiceLinksEnabled(false); getMockMvc().perform(post("/login/idp_discovery") .header("Accept", TEXT_HTML) .param("email", "marissa@koala.org")) .andExpect(status().isOk()) .andExpect(xpath("//div[@class='action pull-right']//a").doesNotExist()); } @Test public void userNamePresentInPasswordPage() throws Exception { getMockMvc().perform(post("/login/idp_discovery") .with(cookieCsrf()) .param("email", "test@email.com")) .andExpect(xpath("//input[@name='username']/@value").string("test@email.com")) .andExpect(xpath("//input[@name='X-Uaa-Csrf']").exists()); } @Test public void authorizeForClientWithIdpNotAllowed() throws Exception { String subdomain = "idp-not-allowed"; IdentityZone zone = MultitenancyFixture.identityZone(subdomain, subdomain); zone.getConfig().getLinks().getLogout().setDisableRedirectParameter(false); createOtherIdentityZone(zone.getSubdomain(), getMockMvc(), getWebApplicationContext()); // log in with some idp String zoneAdminToken = testClient.getClientCredentialsOAuthAccessToken("admin", "admin-secret", "scim.write,idps.write", zone.getSubdomain()); ScimUser user = createUser(subdomain, zoneAdminToken); MockHttpSession session = new MockHttpSession(); SetServerNameRequestPostProcessor inZone = new SetServerNameRequestPostProcessor(subdomain + ".localhost"); MockHttpServletRequestBuilder post = post("/uaa/login.do") .with(inZone) .with(cookieCsrf()) .contextPath("/uaa") .session(session) .param("username", user.getUserName()) .param("password", user.getPassword()); getMockMvc().perform(post) .andExpect(redirectedUrl("/uaa/")) .andExpect(currentUserCookie(user.getId())); // authorize for client that does not allow that idp String clientId = "different-provider-client"; BaseClientDetails client = new BaseClientDetails(clientId, "", "", "client_credentials", "uaa.none", "http://*.wildcard.testing,http://testing.com"); client.setClientSecret("secret"); client.setScope(singleton("uaa.user")); client.addAdditionalInformation(ClientConstants.CLIENT_NAME, "THE APPLICATION"); client.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, asList("a-different-provider")); HashSet<String> registeredRedirectUris = new HashSet<>(); registeredRedirectUris.add("http://idp-not-allowed.localhost/"); client.setRegisteredRedirectUri(registeredRedirectUris); MockMvcUtils.createClient(getMockMvc(), adminToken, client, zone); MockHttpServletRequestBuilder authorize = get("/oauth/authorize") .with(inZone) .session(session) .param("client_id", "different-provider-client") .param("response_type", "code") .param("client_secret", "secret") .param("garbage", "this-should-be-preserved"); String expectedUrl = "http://idp-not-allowed.localhost/oauth/authorize?client_id=different-provider-client&response_type=code&client_secret=secret&garbage=this-should-be-preserved"; String html = getMockMvc().perform(authorize) .andDo(print()) .andExpect(status().isUnauthorized()) .andReturn().getResponse().getContentAsString(); String extractPattern = "logout.do\\?redirect\\=(.*?)\">click here<"; Pattern pattern = Pattern.compile(extractPattern); Matcher matcher = pattern.matcher(html); assertTrue(matcher.find()); String group = matcher.group(1); assertEquals(expectedUrl, URLDecoder.decode(group, "UTF-8")); } private MockHttpSession setUpClientAndProviderForIdpDiscovery(String originKey, IdentityZone zone) throws Exception { String metadata = String.format(MockMvcUtils.IDP_META_DATA, new RandomValueStringGenerator().generate()); SamlIdentityProviderDefinition config = (SamlIdentityProviderDefinition) new SamlIdentityProviderDefinition() .setMetaDataLocation(metadata) .setIdpEntityAlias(originKey) .setLinkText("Active SAML Provider") .setZoneId(zone.getId()) .setEmailDomain(Collections.singletonList("test.org")); IdentityProvider identityProvider = MultitenancyFixture.identityProvider(originKey, zone.getId()); identityProvider.setType(OriginKeys.SAML); identityProvider.setConfig(config); MockMvcUtils.createIdpUsingWebRequest(getMockMvc(), zone.getId(), adminToken, identityProvider, status().isCreated()); String clientId = generator.generate(); BaseClientDetails client = new BaseClientDetails(clientId, "", "", "client_credentials", "uaa.none", "http://*.wildcard.testing,http://testing.com"); client.setClientSecret("secret"); client.addAdditionalInformation(ClientConstants.CLIENT_NAME, "woohoo"); client.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, asList(originKey, "other-provider", UAA, LDAP)); MockMvcUtils.createClient(getMockMvc(), adminToken, client, zone); SavedRequest savedRequest = getSavedRequest(client); MockHttpSession session = new MockHttpSession(); session.setAttribute(SAVED_REQUEST_SESSION_ATTRIBUTE, savedRequest); return session; } private void changeLockoutPolicyForIdpInZone(IdentityZone zone) throws Exception { IdentityProviderProvisioning identityProviderProvisioning = getWebApplicationContext().getBean(JdbcIdentityProviderProvisioning.class); IdentityProvider identityProvider = identityProviderProvisioning.retrieveByOrigin(UAA, zone.getId()); LockoutPolicy policy = new LockoutPolicy(); policy.setLockoutAfterFailures(2); policy.setLockoutPeriodSeconds(3600); policy.setCountFailuresWithin(900); UaaIdentityProviderDefinition configMap = new UaaIdentityProviderDefinition(null, policy); identityProvider.setConfig(configMap); String zoneAdminToken = testClient.getClientCredentialsOAuthAccessToken("admin", "admin-secret", "scim.write,idps.write", zone.getSubdomain()); getMockMvc().perform(put("/identity-providers/" + identityProvider.getId()) .with(new SetServerNameRequestPostProcessor(zone.getSubdomain() + ".localhost")) .content(JsonUtils.writeValueAsString(identityProvider)) .contentType(APPLICATION_JSON) .header("Authorization", "bearer " + zoneAdminToken)).andExpect(status().isOk()); } private void attemptFailedLogin(int numberOfAttempts, String username, String subdomain) throws Exception { String requestDomain = subdomain.equals("") ? "localhost" : subdomain + ".localhost"; MockHttpServletRequestBuilder post = post("/uaa/login.do") .with(new SetServerNameRequestPostProcessor(requestDomain)) .with(cookieCsrf()) .contextPath("/uaa") .param("username", username) .param("password", "wrong_password"); for (int i = 0; i < numberOfAttempts ; i++) { getMockMvc().perform(post) .andExpect(redirectedUrl("/uaa/login?error=login_failure")) .andExpect(emptyCurrentUserCookie()); } } private static ResultMatcher emptyCurrentUserCookie() { return result -> { cookie().value("Current-User", isEmptyOrNullString()).match(result); cookie().maxAge("Current-User", 0).match(result); cookie().path("Current-User", "/uaa").match(result); }; } private IdentityZone setupZone(IdentityZoneConfiguration config) throws Exception { String zoneId = generator.generate().toLowerCase(); IdentityZone zone = createOtherIdentityZone(zoneId, getMockMvc(), getWebApplicationContext()); zone.setConfig(config); getWebApplicationContext().getBean(IdentityZoneProvisioning.class).update(zone); return zone; } private SavedRequest getSavedRequest(BaseClientDetails client) throws Exception { return 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[] {client.getClientId()}; } return new String[0]; } @Override public List<Cookie> getCookies() { return null; } @Override public String getMethod() { return null; } @Override public List<String> getHeaderValues(String name) { return null; } @Override public Collection<String> getHeaderNames() { return null; } @Override public List<Locale> getLocales() { return null; } @Override public Map<String, String[]> getParameterMap() { return null; } }; } }