/*******************************************************************************
* 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.UaaAuthenticationDetails;
import org.cloudfoundry.identity.uaa.authentication.UaaPrincipal;
import org.cloudfoundry.identity.uaa.cache.UrlContentCache;
import org.cloudfoundry.identity.uaa.codestore.ExpiringCodeStore;
import org.cloudfoundry.identity.uaa.codestore.InMemoryExpiringCodeStore;
import org.cloudfoundry.identity.uaa.constants.OriginKeys;
import org.cloudfoundry.identity.uaa.oauth.client.ClientConstants;
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.RawXOAuthIdentityProviderDefinition;
import org.cloudfoundry.identity.uaa.provider.SamlIdentityProviderDefinition;
import org.cloudfoundry.identity.uaa.provider.UaaIdentityProviderDefinition;
import org.cloudfoundry.identity.uaa.provider.OIDCIdentityProviderDefinition;
import org.cloudfoundry.identity.uaa.provider.oauth.XOAuthProviderConfigurator;
import org.cloudfoundry.identity.uaa.provider.saml.LoginSamlAuthenticationToken;
import org.cloudfoundry.identity.uaa.provider.saml.SamlIdentityProviderConfigurator;
import org.cloudfoundry.identity.uaa.util.JsonUtils;
import org.cloudfoundry.identity.uaa.util.PredicateMatcher;
import org.cloudfoundry.identity.uaa.util.RestTemplateFactory;
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.Links;
import org.cloudfoundry.identity.uaa.zone.MultitenancyFixture;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.providers.ExpiringUsernameAuthenticationToken;
import org.springframework.security.web.savedrequest.DefaultSavedRequest;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.Model;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.cloudfoundry.identity.uaa.login.LoginInfoEndpoint.OAUTH_LINKS;
import static org.cloudfoundry.identity.uaa.login.LoginInfoEndpoint.SHOW_LOGIN_LINKS;
import static org.cloudfoundry.identity.uaa.util.UaaUrlUtils.addSubdomainToUrl;
import static org.cloudfoundry.identity.uaa.web.UaaSavedRequestAwareAuthenticationSuccessHandler.SAVED_REQUEST_SESSION_ATTRIBUTE;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyList;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class LoginInfoEndpointTests {
public static final String HTTP_LOCALHOST_8080_UAA = "http://localhost:8080/uaa";
private UaaPrincipal marissa;
private List<Prompt> prompts;
private ExtendedModelMap model = new ExtendedModelMap();
private SamlIdentityProviderConfigurator mockIDPConfigurator;
private List<SamlIdentityProviderDefinition> idps;
private IdentityProviderProvisioning identityProviderProvisioning;
private IdentityProvider uaaProvider;
private IdentityZoneConfiguration originalConfiguration;
private XOAuthProviderConfigurator configurator;
@Before
public void setUpPrincipal() {
IdentityZoneHolder.clear();
marissa = new UaaPrincipal("marissa-id","marissa","marissa@test.org","origin",null, IdentityZoneHolder.get().getId());
prompts = new LinkedList<>();
prompts.add(new Prompt("username", "text", "Email"));
prompts.add(new Prompt("password", "password", "Password"));
prompts.add(new Prompt("passcode", "text", "One Time Code ( Get one at "+HTTP_LOCALHOST_8080_UAA+"/passcode )"));
mockIDPConfigurator = mock(SamlIdentityProviderConfigurator.class);
identityProviderProvisioning = mock(IdentityProviderProvisioning.class);
uaaProvider = new IdentityProvider();
when(identityProviderProvisioning.retrieveByOrigin(eq(OriginKeys.UAA), anyString())).thenReturn(uaaProvider);
when(identityProviderProvisioning.retrieveByOrigin(eq(OriginKeys.LDAP), anyString())).thenReturn(new IdentityProvider());
idps = getIdps();
originalConfiguration = IdentityZoneHolder.get().getConfig();
IdentityZoneHolder.get().setConfig(new IdentityZoneConfiguration());
configurator = new XOAuthProviderConfigurator(identityProviderProvisioning, mock(UrlContentCache.class), mock(RestTemplateFactory.class));
}
@After
public void clearZoneHolder() {
IdentityZoneHolder.clear();
IdentityZoneHolder.get().setConfig(originalConfiguration);
}
@Test
public void testLoginReturnsSystemZone() throws Exception {
LoginInfoEndpoint endpoint = getEndpoint();
assertFalse(model.containsAttribute("zone_name"));
endpoint.loginForHtml(model, null, new MockHttpServletRequest());
assertEquals(OriginKeys.UAA, model.asMap().get("zone_name"));
}
@Test
public void testAlreadyLoggedInRedirectsToHome() throws Exception {
LoginInfoEndpoint endpoint = getEndpoint();
UaaAuthentication authentication = mock(UaaAuthentication.class);
when(authentication.isAuthenticated()).thenReturn(true);
String result = endpoint.loginForHtml(model, authentication, new MockHttpServletRequest());
assertEquals("redirect:/home", result);
}
@Test
public void testDeleteSavedAccount() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
LoginInfoEndpoint endpoint = getEndpoint();
String userId = "testUserId";
String result = endpoint.deleteSavedAccount(request, response, userId);
Cookie[] cookies = response.getCookies();
assertEquals(cookies.length, 1);
assertEquals(cookies[0].getName(), "Saved-Account-" + userId);
assertEquals(cookies[0].getMaxAge(), 0);
assertEquals("redirect:/login", result);
}
@Test
public void testSavedAccountsPopulatedOnModel() throws Exception {
LoginInfoEndpoint endpoint = getEndpoint();
assertThat(model, not(hasKey("savedAccounts")));
MockHttpServletRequest request = new MockHttpServletRequest();
SavedAccountOption savedAccount = new SavedAccountOption();
savedAccount.setUsername("bob");
savedAccount.setEmail("bob@example.com");
savedAccount.setUserId("xxxx");
savedAccount.setOrigin("uaa");
Cookie cookie1 = new Cookie("Saved-Account-xxxx", URLEncoder.encode(JsonUtils.writeValueAsString(savedAccount), UTF_8.name()));
savedAccount.setUsername("tim");
savedAccount.setEmail("tim@example.org");
savedAccount.setUserId("zzzz");
savedAccount.setOrigin("ldap");
Cookie cookie2 = new Cookie("Saved-Account-zzzz", URLEncoder.encode(JsonUtils.writeValueAsString(savedAccount), UTF_8.name()));
request.setCookies(cookie1, cookie2);
endpoint.loginForHtml(model, null, request);
assertThat(model, hasKey("savedAccounts"));
assertThat(model.get("savedAccounts"), instanceOf(List.class));
List<SavedAccountOption> savedAccounts = (List<SavedAccountOption>) model.get("savedAccounts");
assertThat(savedAccounts, hasSize(2));
SavedAccountOption savedAccount0 = savedAccounts.get(0);
assertThat(savedAccount0, notNullValue());
assertEquals("bob", savedAccount0.getUsername());
assertEquals("bob@example.com", savedAccount0.getEmail());
assertEquals("uaa", savedAccount0.getOrigin());
assertEquals("xxxx", savedAccount0.getUserId());
SavedAccountOption savedAccount1 = savedAccounts.get(1);
assertThat(savedAccount1, notNullValue());
assertEquals("tim", savedAccount1.getUsername());
assertEquals("tim@example.org", savedAccount1.getEmail());
assertEquals("ldap", savedAccount1.getOrigin());
assertEquals("zzzz", savedAccount1.getUserId());
}
@Test
public void testSavedAccountsEncodedAndUnEncoded() throws Exception {
LoginInfoEndpoint endpoint = getEndpoint();
assertThat(model, not(hasKey("savedAccounts")));
MockHttpServletRequest request = new MockHttpServletRequest();
SavedAccountOption savedAccount = new SavedAccountOption();
savedAccount.setUsername("bill");
savedAccount.setEmail("bill@example.com");
savedAccount.setUserId("xxxx");
savedAccount.setOrigin("uaa");
// write Cookie1 without URLencode into value, situation before this correction
Cookie cookie1 = new Cookie("Saved-Account-xxxx", JsonUtils.writeValueAsString(savedAccount));
savedAccount.setUsername("bill");
savedAccount.setEmail("bill@example.com");
savedAccount.setUserId("xxxx");
savedAccount.setOrigin("uaa");
// write Cookie2 with URLencode into value, situation after this correction
Cookie cookie2 = new Cookie("Saved-Account-zzzz", URLEncoder.encode(JsonUtils.writeValueAsString(savedAccount), UTF_8.name()));
request.setCookies(cookie1, cookie2);
endpoint.loginForHtml(model, null, request);
assertThat(model, hasKey("savedAccounts"));
assertThat(model.get("savedAccounts"), instanceOf(List.class));
List<SavedAccountOption> savedAccounts = (List<SavedAccountOption>) model.get("savedAccounts");
assertThat(savedAccounts, hasSize(2));
// evaluate that both cookies can be parsed out has have same values
SavedAccountOption savedAccount0 = savedAccounts.get(0);
assertThat(savedAccount0, notNullValue());
assertEquals("bill", savedAccount0.getUsername());
assertEquals("bill@example.com", savedAccount0.getEmail());
assertEquals("uaa", savedAccount0.getOrigin());
assertEquals("xxxx", savedAccount0.getUserId());
SavedAccountOption savedAccount1 = savedAccounts.get(1);
assertThat(savedAccount1, notNullValue());
assertEquals("bill", savedAccount1.getUsername());
assertEquals("bill@example.com", savedAccount1.getEmail());
assertEquals("uaa", savedAccount1.getOrigin());
assertEquals("xxxx", savedAccount1.getUserId());
}
@Test(expected=NullPointerException.class)
public void testSavedAccountsInvalidCookie() throws Exception {
LoginInfoEndpoint endpoint = getEndpoint();
assertThat(model, not(hasKey("savedAccounts")));
MockHttpServletRequest request = new MockHttpServletRequest();
SavedAccountOption savedAccount = new SavedAccountOption();
savedAccount.setUsername("bob");
savedAccount.setEmail("bob@example.com");
savedAccount.setUserId("xxxx");
savedAccount.setOrigin("uaa");
Cookie cookie1 = new Cookie("Saved-Account-xxxx", "%2");
request.setCookies(cookie1);
endpoint.loginForHtml(model, null, request);
}
@Test
public void testLoginReturnsOtherZone() throws Exception {
IdentityZone zone = new IdentityZone();
zone.setName("some_other_zone");
zone.setId("other-zone-id");
zone.setSubdomain(zone.getName());
IdentityZoneHolder.set(zone);
LoginInfoEndpoint endpoint = getEndpoint();
assertFalse(model.containsAttribute("zone_name"));
endpoint.loginForHtml(model, null, new MockHttpServletRequest());
assertEquals("some_other_zone", model.asMap().get("zone_name"));
}
@Test
public void customSelfserviceLinks_ApplyToAllZone_Html() throws Exception {
LoginInfoEndpoint endpoint = getEndpoint();
IdentityZone zone = new IdentityZone();
zone.setName("some_other_zone");
zone.setId("some_id");
zone.setSubdomain(zone.getName());
IdentityZoneConfiguration config = zone.getConfig();
IdentityZoneHolder.set(zone);
IdentityZoneHolder.get().getConfig().getLinks().getSelfService().setSignup("http://custom_signup_link");
IdentityZoneHolder.get().getConfig().getLinks().getSelfService().setPasswd("http://custom_passwd_link");
endpoint.loginForHtml(model, null, new MockHttpServletRequest());
validateSelfServiceLinks("http://custom_signup_link", "http://custom_passwd_link", model);
validateSelfServiceLinks("http://custom_signup_link", "http://custom_passwd_link", endpoint.getSelfServiceLinks());
//null config
zone.setConfig(null);
validateSelfServiceLinks("/create_account", "/forgot_password", endpoint.getSelfServiceLinks());
//null config with globals
endpoint.setGlobalLinks(new Links().setSelfService(new Links.SelfService().setSignup("/signup").setPasswd("/passwd")));
validateSelfServiceLinks("/signup", "/passwd", endpoint.getSelfServiceLinks());
//null links with globals
IdentityZoneConfiguration otherConfig = new IdentityZoneConfiguration(null);
otherConfig.getLinks().setSelfService(new Links.SelfService().setSignup(null).setPasswd(null));
validateSelfServiceLinks("/signup", "/passwd", endpoint.getSelfServiceLinks());
//null links with globals using variables
endpoint.setGlobalLinks(new Links().setSelfService(new Links.SelfService().setSignup("/signup?domain={zone.subdomain}").setPasswd("/passwd?id={zone.id}")));
validateSelfServiceLinks("/signup?domain="+zone.getSubdomain(), "/passwd?id="+zone.getId(), endpoint.getSelfServiceLinks());
//zone config overrides global
zone.setConfig(config);
validateSelfServiceLinks("http://custom_signup_link", "http://custom_passwd_link", endpoint.getSelfServiceLinks());
//zone config supports variables too
config.getLinks().getSelfService().setSignup("/local_signup?domain={zone.subdomain}");
config.getLinks().getSelfService().setPasswd("/local_passwd?id={zone.id}");
validateSelfServiceLinks("/local_signup?domain="+zone.getSubdomain(), "/local_passwd?id="+zone.getId(), endpoint.getSelfServiceLinks());
}
public void validateSelfServiceLinks(String signup, String passwd, Model model) {
Map<String, String> links = (Map<String, String>) model.asMap().get("links");
validateSelfServiceLinks(signup, passwd, links);
}
public void validateSelfServiceLinks(String signup, String passwd, Map<String,String> links) {
assertEquals(signup, links.get("createAccountLink"));
assertEquals(passwd, links.get("forgotPasswordLink"));
//json links
assertEquals(signup, links.get("register"));
assertEquals(passwd, links.get("passwd"));
}
@Test
public void use_login_url_if_present() throws Exception {
check_links_urls(IdentityZone.getUaa());
}
@Test
public void use_login_url_if_present_in_zone() throws Exception {
IdentityZone zone = MultitenancyFixture.identityZone("test","test");
check_links_urls(zone);
}
public void check_links_urls(IdentityZone zone) throws Exception {
IdentityZoneHolder.set(zone);
LoginInfoEndpoint endpoint = getEndpoint();
String baseUrl = "http://uaa.domain.com";
endpoint.setBaseUrl(baseUrl);
endpoint.infoForJson(model, null, new MockHttpServletRequest("GET", endpoint.getBaseUrl()));
assertEquals(addSubdomainToUrl(baseUrl), ((Map<String, String>) model.asMap().get("links")).get("uaa"));
assertEquals(addSubdomainToUrl(baseUrl.replace("uaa", "login")), ((Map<String, String>) model.asMap().get("links")).get("login"));
String loginBaseUrl = "http://external-login.domain.com";
endpoint.setExternalLoginUrl(loginBaseUrl);
endpoint.infoForJson(model, null, new MockHttpServletRequest("GET", endpoint.getBaseUrl()));
assertEquals(addSubdomainToUrl(baseUrl), ((Map<String, String>) model.asMap().get("links")).get("uaa"));
assertEquals(loginBaseUrl, ((Map<String, String>) model.asMap().get("links")).get("login"));
when(mockIDPConfigurator.getIdentityProviderDefinitions((List<String>) isNull(), eq(zone))).thenReturn(idps);
endpoint.setIdpDefinitions(mockIDPConfigurator);
endpoint.infoForJson(model, null, new MockHttpServletRequest("GET", endpoint.getBaseUrl()));
Map mapPrompts = (Map) model.get("prompts");
assertNotNull(mapPrompts.get("passcode"));
assertEquals("One Time Code ( Get one at "+addSubdomainToUrl(HTTP_LOCALHOST_8080_UAA) + "/passcode )", ((String[])mapPrompts.get("passcode"))[1]);
}
@Test
public void no_self_service_links_if_self_service_disabled() throws Exception {
IdentityZone zone = MultitenancyFixture.identityZone("zone","zone");
zone.setConfig(new IdentityZoneConfiguration());
zone.getConfig().getLinks().getSelfService().setSelfServiceLinksEnabled(false);
IdentityZoneHolder.set(zone);
LoginInfoEndpoint endpoint = getEndpoint();
endpoint.infoForJson(model, null, new MockHttpServletRequest("GET", endpoint.getBaseUrl()));
Map<String, Object> links = (Map<String, Object>) model.asMap().get("links");
assertNotNull(links);
assertNull(links.get("register"));
assertNull(links.get("passwd"));
}
@Test
public void no_ui_links_for_json() throws Exception {
LoginInfoEndpoint endpoint = getEndpoint();
endpoint.infoForJson(model, null, new MockHttpServletRequest("GET", endpoint.getBaseUrl()));
Map<String, Object> links = (Map<String, Object>) model.asMap().get("links");
assertNotNull(links);
assertNull(links.get("linkCreateAccountShow"));
assertNull(links.get("fieldUsernameShow"));
assertNull(links.get("forgotPasswordLink"));
assertNull(links.get("createAccountLink"));
assertEquals("http://someurl", links.get("login"));
assertEquals("http://someurl", links.get("uaa"));
assertEquals("/create_account", links.get("register"));
assertEquals("/forgot_password", links.get("passwd"));
}
@Test
public void saml_links_for_json() throws Exception {
LoginInfoEndpoint endpoint = getEndpoint();
endpoint.setIdpDefinitions(mockIDPConfigurator);
when(mockIDPConfigurator.getIdentityProviderDefinitions(anyObject(), anyObject())).thenReturn(idps);
endpoint.setIdpDefinitions(mockIDPConfigurator);
endpoint.infoForJson(model, null, new MockHttpServletRequest("GET", endpoint.getBaseUrl()));
Map<String, Object> links = (Map<String, Object>) model.asMap().get("links");
assertEquals("http://someurl", links.get("login"));
assertTrue(model.get(LoginInfoEndpoint.IDP_DEFINITIONS) instanceof Map);
Map<String,String> idpDefinitions = (Map<String,String>)model.get(LoginInfoEndpoint.IDP_DEFINITIONS);
for (SamlIdentityProviderDefinition def : idps) {
assertEquals(
"http://someurl/saml/discovery?returnIDParam=idp&entityID=" + endpoint.getZonifiedEntityId() + "&idp="+def.getIdpEntityAlias()+"&isPassive=true",
idpDefinitions.get(def.getIdpEntityAlias())
);
}
}
@Test
public void saml_links_for_html() throws Exception {
LoginInfoEndpoint endpoint = getEndpoint();
endpoint.setIdpDefinitions(mockIDPConfigurator);
endpoint.infoForHtml(model, null, new MockHttpServletRequest("GET", endpoint.getBaseUrl()));
Map<String, Object> links = (Map<String, Object>) model.asMap().get("links");
assertNotNull(links);
assertEquals("http://someurl", links.get("login"));
assertTrue(model.get(LoginInfoEndpoint.IDP_DEFINITIONS) instanceof Collection);
}
@Test
public void no_self_service_links_if_internal_user_management_disabled() throws Exception {
UaaIdentityProviderDefinition uaaIdentityProviderDefinition = new UaaIdentityProviderDefinition();
uaaIdentityProviderDefinition.setDisableInternalUserManagement(true);
uaaProvider.setConfig(uaaIdentityProviderDefinition);
LoginInfoEndpoint endpoint = getEndpoint();
endpoint.infoForJson(model, null, new MockHttpServletRequest("GET", endpoint.getBaseUrl()));
Map<String, Object> links = (Map<String, Object>) model.asMap().get("links");
assertNotNull(links);
assertNull(links.get("register"));
assertNull(links.get("passwd"));
assertNull(links.get("createAccountLink"));
assertNull(links.get("forgotPasswordLink"));
assertNull(model.asMap().get("createAccountLink"));
assertNull(model.asMap().get("forgotPasswordLink"));
}
@Test
public void no_usernamePasswordBoxes_if_internalAuth_and_ldap_disabled() throws Exception {
when(mockIDPConfigurator.getIdentityProviderDefinitions(anyList(), anyObject())).thenReturn(idps);
IdentityProvider ldapIdentityProvider = new IdentityProvider();
ldapIdentityProvider.setActive(false);
when(identityProviderProvisioning.retrieveByOrigin(OriginKeys.LDAP, "uaa")).thenReturn(ldapIdentityProvider);
IdentityProvider uaaIdentityProvider = new IdentityProvider();
uaaIdentityProvider.setActive(false);
when(identityProviderProvisioning.retrieveByOrigin(OriginKeys.UAA, "uaa")).thenReturn(uaaIdentityProvider);
LoginInfoEndpoint endpoint = getEndpoint();
endpoint.setIdpDefinitions(mockIDPConfigurator);
endpoint.infoForHtml(model, null, new MockHttpServletRequest("GET", endpoint.getBaseUrl()));
assertFalse((Boolean) model.get("fieldUsernameShow"));
}
@Test
public void testGeneratePasscodeForKnownUaaPrincipal() throws Exception {
Map<String,Object> model = new HashMap<>();
ExpiringCodeStore store = new InMemoryExpiringCodeStore();
LoginInfoEndpoint endpoint = getEndpoint();
endpoint.setExpiringCodeStore(store);
assertEquals("passcode", endpoint.generatePasscode(model, marissa));
UaaAuthentication uaaAuthentication = new UaaAuthentication(marissa, new ArrayList<GrantedAuthority>(),new UaaAuthenticationDetails(new MockHttpServletRequest()));
assertEquals("passcode", endpoint.generatePasscode(model, uaaAuthentication));
ExpiringUsernameAuthenticationToken expiringUsernameAuthenticationToken = new ExpiringUsernameAuthenticationToken(marissa,"");
LoginSamlAuthenticationToken samlAuthenticationToken = new LoginSamlAuthenticationToken(marissa, expiringUsernameAuthenticationToken);
assertEquals("passcode", endpoint.generatePasscode(model, samlAuthenticationToken));
//token with a UaaPrincipal should always work
assertEquals("passcode", endpoint.generatePasscode(model, expiringUsernameAuthenticationToken));
}
@Test(expected = LoginInfoEndpoint.UnknownPrincipalException.class)
public void testGeneratePasscodeForUnknownUaaPrincipal() throws Exception {
Map<String,Object> model = new HashMap<>();
LoginInfoEndpoint endpoint = getEndpoint();
ExpiringUsernameAuthenticationToken token = new ExpiringUsernameAuthenticationToken("princpal", "");
assertEquals("passcode", endpoint.generatePasscode(model, token));
}
@Test
public void test_PromptLogic() throws Exception {
LoginInfoEndpoint endpoint = getEndpoint();
endpoint.infoForHtml(model, null, new MockHttpServletRequest("GET", endpoint.getBaseUrl()));
assertNotNull("prompts attribute should be present", model.get("prompts"));
assertTrue("prompts should be a Map for Html content", model.get("prompts") instanceof Map);
Map mapPrompts = (Map)model.get("prompts");
assertEquals("there should be two prompts for html", 2, mapPrompts.size());
assertNotNull(mapPrompts.get("username"));
assertNotNull(mapPrompts.get("password"));
assertNull(mapPrompts.get("passcode"));
endpoint.infoForJson(model, null, new MockHttpServletRequest("GET", endpoint.getBaseUrl()));
assertNotNull("prompts attribute should be present", model.get("prompts"));
assertTrue("prompts should be a Map for JSON content", model.get("prompts") instanceof Map);
mapPrompts = (Map)model.get("prompts");
assertEquals("there should be two prompts for html", 2, mapPrompts.size());
assertNotNull(mapPrompts.get("username"));
assertNotNull(mapPrompts.get("password"));
assertNull(mapPrompts.get("passcode"));
//add a SAML IDP, should make the passcode prompt appear
when(mockIDPConfigurator.getIdentityProviderDefinitions((List<String>) isNull(), eq(IdentityZone.getUaa()))).thenReturn(idps);
endpoint.setIdpDefinitions(mockIDPConfigurator);
endpoint.infoForJson(model, null, new MockHttpServletRequest("GET", endpoint.getBaseUrl()));
assertNotNull("prompts attribute should be present", model.get("prompts"));
assertTrue("prompts should be a Map for JSON content", model.get("prompts") instanceof Map);
mapPrompts = (Map)model.get("prompts");
assertEquals("there should be three prompts for html", 3, mapPrompts.size());
assertNotNull(mapPrompts.get("username"));
assertNotNull(mapPrompts.get("password"));
assertNotNull(mapPrompts.get("passcode"));
when(mockIDPConfigurator.getIdentityProviderDefinitions((List<String>) isNull(), eq(IdentityZone.getUaa()))).thenReturn(idps);
IdentityProvider ldapIdentityProvider = new IdentityProvider();
ldapIdentityProvider.setActive(false);
when(identityProviderProvisioning.retrieveByOrigin(OriginKeys.LDAP, "uaa")).thenReturn(ldapIdentityProvider);
IdentityProvider uaaIdentityProvider = new IdentityProvider();
uaaIdentityProvider.setActive(false);
when(identityProviderProvisioning.retrieveByOrigin(OriginKeys.UAA, "uaa")).thenReturn(uaaIdentityProvider);
endpoint.infoForJson(model, null, new MockHttpServletRequest("GET", endpoint.getBaseUrl()));
assertNotNull("prompts attribute should be present", model.get("prompts"));
mapPrompts = (Map)model.get("prompts");
assertNull(mapPrompts.get("username"));
assertNull(mapPrompts.get("password"));
assertNotNull(mapPrompts.get("passcode"));
}
@Test
public void testFilterIdpsForDefaultZone() throws Exception {
// mock session and saved request
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpSession session = new MockHttpSession();
SavedRequest savedRequest = mock(SavedRequest.class);
when(savedRequest.getParameterValues("client_id")).thenReturn(new String[]{"client-id"});
when(savedRequest.getRedirectUrl()).thenReturn("http://localhost:8080/uaa");
session.setAttribute(SAVED_REQUEST_SESSION_ATTRIBUTE, savedRequest);
request.setSession(session);
// mock SamlIdentityProviderConfigurator
when(mockIDPConfigurator.getIdentityProviderDefinitions((List<String>) isNull(), eq(IdentityZone.getUaa()))).thenReturn(idps);
LoginInfoEndpoint endpoint = getEndpoint();
endpoint.setIdpDefinitions(mockIDPConfigurator);
endpoint.loginForHtml(model, null, request);
Collection<SamlIdentityProviderDefinition> idpDefinitions = (Collection<SamlIdentityProviderDefinition>) model.asMap().get("idpDefinitions");
assertEquals(2, idpDefinitions.size());
Iterator<SamlIdentityProviderDefinition> iterator = idpDefinitions.iterator();
SamlIdentityProviderDefinition clientIdp = iterator.next();
assertEquals("awesome-idp", clientIdp.getIdpEntityAlias());
assertEquals(true, clientIdp.isShowSamlLink());
clientIdp = iterator.next();
assertEquals("my-client-awesome-idp", clientIdp.getIdpEntityAlias());
assertEquals(true, clientIdp.isShowSamlLink());
assertEquals(true, model.asMap().get("fieldUsernameShow"));
assertEquals(true, model.asMap().get("linkCreateAccountShow"));
}
@Test
public void testFilterIdpsWithNoSavedRequest() throws Exception {
// mock SamlIdentityProviderConfigurator
when(mockIDPConfigurator.getIdentityProviderDefinitions((List<String>) isNull(), eq(IdentityZone.getUaa()))).thenReturn(idps);
LoginInfoEndpoint endpoint = getEndpoint();
endpoint.setIdpDefinitions(mockIDPConfigurator);
endpoint.loginForHtml(model, null, new MockHttpServletRequest());
Collection<SamlIdentityProviderDefinition> idpDefinitions = (Collection<SamlIdentityProviderDefinition>) model.asMap().get("idpDefinitions");
assertEquals(2, idpDefinitions.size());
Iterator<SamlIdentityProviderDefinition> iterator = idpDefinitions.iterator();
SamlIdentityProviderDefinition clientIdp = iterator.next();
assertEquals("awesome-idp", clientIdp.getIdpEntityAlias());
assertEquals(true, clientIdp.isShowSamlLink());
clientIdp = iterator.next();
assertEquals("my-client-awesome-idp", clientIdp.getIdpEntityAlias());
assertEquals(true, clientIdp.isShowSamlLink());
assertEquals(true, model.asMap().get("fieldUsernameShow"));
assertEquals(true, model.asMap().get("linkCreateAccountShow"));
}
@Test
public void testFilterIDPsForAuthcodeClientInDefaultZone() throws Exception {
// mock session and saved request
MockHttpServletRequest request = getMockHttpServletRequest();
List<String> allowedProviders = Arrays.asList("my-client-awesome-idp1", "my-client-awesome-idp2", OriginKeys.LDAP);
// mock Client service
BaseClientDetails clientDetails = new BaseClientDetails();
clientDetails.setClientId("client-id");
clientDetails.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, new LinkedList<>(allowedProviders));
ClientDetailsService clientDetailsService = mock(ClientDetailsService.class);
when(clientDetailsService.loadClientByClientId("client-id")).thenReturn(clientDetails);
// mock SamlIdentityProviderConfigurator
List<SamlIdentityProviderDefinition> clientIDPs = new LinkedList<>();
clientIDPs.add(createIdentityProviderDefinition("my-client-awesome-idp1", "uaa"));
clientIDPs.add(createIdentityProviderDefinition("my-client-awesome-idp2", "uaa"));
when(mockIDPConfigurator.getIdentityProviderDefinitions(eq(allowedProviders), eq(IdentityZone.getUaa()))).thenReturn(clientIDPs);
LoginInfoEndpoint endpoint = getEndpoint();
endpoint.setClientDetailsService(clientDetailsService);
endpoint.setIdpDefinitions(mockIDPConfigurator);
endpoint.loginForHtml(model, null, request);
Collection<SamlIdentityProviderDefinition> idpDefinitions = (Collection<SamlIdentityProviderDefinition>) model.asMap().get("idpDefinitions");
assertEquals(2, idpDefinitions.size());
assertThat(idpDefinitions, PredicateMatcher.<SamlIdentityProviderDefinition>has(c -> c.getIdpEntityAlias().equals("my-client-awesome-idp1")));
assertThat(idpDefinitions, PredicateMatcher.<SamlIdentityProviderDefinition>has(c -> c.isShowSamlLink()));
assertEquals(true, model.asMap().get("fieldUsernameShow"));
assertEquals(false, model.asMap().get("linkCreateAccountShow"));
}
@Test
public void testFilterIDPsForAuthcodeClientInOtherZone() throws Exception {
// mock session and saved request
MockHttpServletRequest request = getMockHttpServletRequest();
IdentityZone zone = MultitenancyFixture.identityZone("other-zone", "other-zone");
IdentityZoneHolder.set(zone);
List<String> allowedProviders = Arrays.asList("my-client-awesome-idp1", "my-client-awesome-idp2");
// mock Client service
BaseClientDetails clientDetails = new BaseClientDetails();
clientDetails.setClientId("client-id");
clientDetails.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, new LinkedList<>(allowedProviders));
ClientDetailsService clientDetailsService = mock(ClientDetailsService.class);
when(clientDetailsService.loadClientByClientId("client-id")).thenReturn(clientDetails);
// mock SamlIdentityProviderConfigurator
List<SamlIdentityProviderDefinition> clientIDPs = new LinkedList<>();
clientIDPs.add(createIdentityProviderDefinition("my-client-awesome-idp1", "uaa"));
clientIDPs.add(createIdentityProviderDefinition("my-client-awesome-idp2", "uaa"));
SamlIdentityProviderConfigurator mockIDPConfigurator = mock(SamlIdentityProviderConfigurator.class);
when(mockIDPConfigurator.getIdentityProviderDefinitions(eq(allowedProviders), eq(zone))).thenReturn(clientIDPs);
LoginInfoEndpoint endpoint = getEndpoint();
endpoint.setClientDetailsService(clientDetailsService);
endpoint.setIdpDefinitions(mockIDPConfigurator);
endpoint.loginForHtml(model, null, request);
Collection<SamlIdentityProviderDefinition> idpDefinitions = (Collection<SamlIdentityProviderDefinition>) model.asMap().get("idpDefinitions");
assertEquals(2, idpDefinitions.size());
assertThat(idpDefinitions, PredicateMatcher.<SamlIdentityProviderDefinition>has(c -> c.getIdpEntityAlias().equals("my-client-awesome-idp1")));
assertThat(idpDefinitions, PredicateMatcher.<SamlIdentityProviderDefinition>has(c -> c.isShowSamlLink()));
assertEquals(false, model.asMap().get("fieldUsernameShow"));
assertEquals(false, model.asMap().get("linkCreateAccountShow"));
}
@Test
public void testFilterIDPsForAuthcodeClientWithNoAllowedIDPsInOtherZone() throws Exception {
// mock session and saved request
MockHttpServletRequest request = getMockHttpServletRequest();
// mock Client service
BaseClientDetails clientDetails = new BaseClientDetails();
ClientDetailsService clientDetailsService = mock(ClientDetailsService.class);
when(clientDetailsService.loadClientByClientId("client-id")).thenReturn(clientDetails);
IdentityZone zone = MultitenancyFixture.identityZone("other-zone", "other-zone");
IdentityZoneHolder.set(zone);
LoginInfoEndpoint endpoint = getEndpoint();
endpoint.setClientDetailsService(clientDetailsService);
// mock SamlIdentityProviderConfigurator
SamlIdentityProviderConfigurator mockIDPConfigurator = mock(SamlIdentityProviderConfigurator.class);
endpoint.setIdpDefinitions(mockIDPConfigurator);
endpoint.loginForHtml(model, null, request);
verify(mockIDPConfigurator).getIdentityProviderDefinitions(null, zone);
}
@Test
public void allowedIdpsforClientOIDCProvider() throws MalformedURLException {
// mock session and saved request
MockHttpServletRequest request = getMockHttpServletRequest();
List<String> allowedProviders = Arrays.asList("my-OIDC-idp1", "my-OIDC-idp2", OriginKeys.LDAP);
// mock Client service
BaseClientDetails clientDetails = new BaseClientDetails();
clientDetails.setClientId("client-id");
clientDetails.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, new LinkedList<>(allowedProviders));
ClientDetailsService clientDetailsService = mock(ClientDetailsService.class);
when(clientDetailsService.loadClientByClientId("client-id")).thenReturn(clientDetails);
List<IdentityProvider> clientAllowedIdps = new LinkedList<>();
clientAllowedIdps.add(createOIDCIdentityProvider("my-OIDC-idp1"));
clientAllowedIdps.add(createOIDCIdentityProvider("my-OIDC-idp2"));
clientAllowedIdps.add(createOIDCIdentityProvider("my-OIDC-idp3"));
when(identityProviderProvisioning.retrieveAll(eq(true), anyString())).thenReturn(clientAllowedIdps);
LoginInfoEndpoint endpoint = getEndpoint();
endpoint.setClientDetailsService(clientDetailsService);
endpoint.loginForHtml(model, null, request);
Map<String, AbstractXOAuthIdentityProviderDefinition> idpDefinitions = (Map<String, AbstractXOAuthIdentityProviderDefinition>) model.asMap().get(OAUTH_LINKS);
assertEquals(2, idpDefinitions.size());
}
@Test
public void oauth_provider_links_shown() throws Exception {
RawXOAuthIdentityProviderDefinition definition = new RawXOAuthIdentityProviderDefinition();
definition.setAuthUrl(new URL("http://auth.url"));
definition.setTokenUrl(new URL("http://token.url"));
IdentityProvider<AbstractXOAuthIdentityProviderDefinition> identityProvider = MultitenancyFixture.identityProvider("oauth-idp-alias", "uaa");
identityProvider.setConfig(definition);
when(identityProviderProvisioning.retrieveAll(anyBoolean(), anyString())).thenReturn(Collections.singletonList(identityProvider));
LoginInfoEndpoint endpoint = getEndpoint();
endpoint.loginForHtml(model, null, new MockHttpServletRequest());
assertThat(model.get(SHOW_LOGIN_LINKS), equalTo(true));
}
@Test
public void passcode_prompt_present_whenThereIsAtleastOneActiveOauthProvider() throws Exception {
RawXOAuthIdentityProviderDefinition definition = new RawXOAuthIdentityProviderDefinition()
.setAuthUrl(new URL("http://auth.url"))
.setTokenUrl(new URL("http://token.url"));
IdentityProvider<AbstractXOAuthIdentityProviderDefinition> identityProvider = MultitenancyFixture.identityProvider("oauth-idp-alias", "uaa");
identityProvider.setConfig(definition);
when(identityProviderProvisioning.retrieveAll(anyBoolean(), anyString())).thenReturn(Collections.singletonList(identityProvider));
LoginInfoEndpoint endpoint = getEndpoint();
endpoint.loginForJson(model, null, new MockHttpServletRequest("GET", endpoint.getBaseUrl()));
Map mapPrompts = (Map) model.get("prompts");
assertNotNull(mapPrompts.get("passcode"));
}
@Test
public void we_return_both_oauth_and_oidc_providers() throws Exception {
RawXOAuthIdentityProviderDefinition oauthDefinition = new RawXOAuthIdentityProviderDefinition()
.setAuthUrl(new URL("http://auth.url"))
.setTokenUrl(new URL("http://token.url"));
OIDCIdentityProviderDefinition oidcDefinition = new OIDCIdentityProviderDefinition()
.setAuthUrl(new URL("http://auth.url"))
.setTokenUrl(new URL("http://token.url"));
IdentityProvider<AbstractXOAuthIdentityProviderDefinition> oauthProvider = MultitenancyFixture.identityProvider("oauth-idp-alias", "uaa");
oauthProvider.setConfig(oauthDefinition);
IdentityProvider<AbstractXOAuthIdentityProviderDefinition> oidcProvider = MultitenancyFixture.identityProvider("oidc-idp-alias", "uaa");
oidcProvider.setConfig(oidcDefinition);
when(identityProviderProvisioning.retrieveAll(anyBoolean(), anyString())).thenReturn(Arrays.asList(oauthProvider, oidcProvider));
LoginInfoEndpoint endpoint = getEndpoint();
assertEquals(2, endpoint.getOauthIdentityProviderDefinitions(null).size());
}
@Test
public void xoauthCallback_redirectsToHomeIfNoSavedRequest() throws Exception {
HttpSession session = new MockHttpSession();
LoginInfoEndpoint endpoint = getEndpoint();
String redirectUrl = endpoint.handleXOAuthCallback(session);
assertEquals("redirect:/home", redirectUrl);
}
@Test
public void xoauthCallback_redirectsToSavedRequestIfPresent() throws Exception {
HttpSession session = new MockHttpSession();
DefaultSavedRequest savedRequest = Mockito.mock(DefaultSavedRequest.class);
when(savedRequest.getRedirectUrl()).thenReturn("/some.redirect.url");
session.setAttribute(SAVED_REQUEST_SESSION_ATTRIBUTE, savedRequest);
LoginInfoEndpoint endpoint = getEndpoint();
String redirectUrl = endpoint.handleXOAuthCallback(session);
assertEquals("redirect:/some.redirect.url", redirectUrl);
}
private MockHttpServletRequest getMockHttpServletRequest() {
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpSession session = new MockHttpSession();
SavedRequest savedRequest = mock(SavedRequest.class);
when(savedRequest.getParameterValues("client_id")).thenReturn(new String[]{"client-id"});
when(savedRequest.getRedirectUrl())
.thenReturn("http://localhost:8080/uaa/oauth/authorize?client_id=identity&redirect_uri=http%3A%2F%2Flocalhost%3A8888%2Flogin&response_type=code&state=8tp0tR");
session.setAttribute(SAVED_REQUEST_SESSION_ATTRIBUTE, savedRequest);
request.setSession(session);
return request;
}
private LoginInfoEndpoint getEndpoint() {
LoginInfoEndpoint endpoint = new LoginInfoEndpoint();
endpoint.setBaseUrl("http://someurl");
SamlIdentityProviderConfigurator emptyConfigurator = mock(SamlIdentityProviderConfigurator.class);
when(emptyConfigurator.getIdentityProviderDefinitions()).thenReturn(Collections.EMPTY_LIST);
when(emptyConfigurator.getIdentityProviderDefinitionsForZone(anyObject())).thenReturn(Collections.EMPTY_LIST);
endpoint.setIdpDefinitions(emptyConfigurator);
IdentityZoneHolder.get().getConfig().setPrompts(prompts);
endpoint.setProviderProvisioning(identityProviderProvisioning);
endpoint.setXoAuthProviderConfigurator(configurator);
return endpoint;
}
private List<SamlIdentityProviderDefinition> getIdps() {
List<SamlIdentityProviderDefinition> idps = new LinkedList<>();
idps.add(createIdentityProviderDefinition("awesome-idp", "uaa"));
idps.add(createIdentityProviderDefinition("my-client-awesome-idp", "uaa"));
return idps;
}
private SamlIdentityProviderDefinition createIdentityProviderDefinition(String idpEntityAlias, String zoneId) {
SamlIdentityProviderDefinition idp1 = new SamlIdentityProviderDefinition()
.setMetaDataLocation("metadataLocation for " + idpEntityAlias)
.setIdpEntityAlias(idpEntityAlias)
.setNameID("nameID for " + idpEntityAlias)
.setMetadataTrustCheck(true)
.setLinkText("link text for " + idpEntityAlias)
.setIconUrl("icon url for " + idpEntityAlias)
.setZoneId(zoneId);
idp1.setIdpEntityAlias(idpEntityAlias);
idp1.setShowSamlLink(true);
idp1.setZoneId(zoneId);
return idp1;
}
private IdentityProvider createOIDCIdentityProvider(String originKey) throws MalformedURLException {
IdentityProvider<AbstractXOAuthIdentityProviderDefinition> oidcIdentityProvider= new IdentityProvider<>();
oidcIdentityProvider.setOriginKey(originKey);
oidcIdentityProvider.setType(OriginKeys.OIDC10);
OIDCIdentityProviderDefinition definition = new OIDCIdentityProviderDefinition();
definition.setAuthUrl(new URL("https://"+originKey+".com"));
oidcIdentityProvider.setConfig(definition);
return oidcIdentityProvider;
}
}