Skip to content

Commit

Permalink
Zonify idp discovery
Browse files Browse the repository at this point in the history
Signed-off-by: Priyata Agrawal <pagrawal@pivotal.io>
  • Loading branch information
mbhave authored and jlo committed May 10, 2016
1 parent b0c27f9 commit 068c3b6
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 27 deletions.
Expand Up @@ -32,6 +32,7 @@ public class IdentityZoneConfiguration {
new Prompt("password", "password", "Password"),
new Prompt("passcode", "password", "One Time Code (Get on at /passcode)")
);
private boolean idpDiscoveryEnabled = false;

public IdentityZoneConfiguration() {}

Expand Down Expand Up @@ -73,4 +74,12 @@ public IdentityZoneConfiguration setPrompts(List<Prompt> prompts) {
this.prompts = prompts;
return this;
}

public boolean isIdpDiscoveryEnabled() {
return idpDiscoveryEnabled;
}

public void setIdpDiscoveryEnabled(boolean idpDiscoveryEnabled) {
this.idpDiscoveryEnabled = idpDiscoveryEnabled;
}
}
Expand Up @@ -44,6 +44,7 @@ public class IdentityZoneConfigurationBootstrap implements InitializingBean {
private String samlSpPrivateKey;
private String samlSpPrivateKeyPassphrase;
private String samlSpCertificate;
private boolean idpDiscoveryEnabled = false;

@Autowired
private IdentityZoneValidator validator = (config, mode) -> config;
Expand All @@ -65,6 +66,7 @@ public void afterPropertiesSet() throws InvalidIdentityZoneDetailsException {
definition.getSamlConfig().setCertificate(samlSpCertificate);
definition.getSamlConfig().setPrivateKey(samlSpPrivateKey);
definition.getSamlConfig().setPrivateKeyPassword(samlSpPrivateKeyPassphrase);
definition.setIdpDiscoveryEnabled(idpDiscoveryEnabled);

if (selfServiceLinks!=null) {
String signup = selfServiceLinks.get("signup");
Expand Down Expand Up @@ -149,4 +151,12 @@ public void setSamlSpPrivateKey(String samlSpPrivateKey) {
public void setSamlSpPrivateKeyPassphrase(String samlSpPrivateKeyPassphrase) {
this.samlSpPrivateKeyPassphrase = samlSpPrivateKeyPassphrase;
}

public boolean isIdpDiscoveryEnabled() {
return idpDiscoveryEnabled;
}

public void setIdpDiscoveryEnabled(boolean idpDiscoveryEnabled) {
this.idpDiscoveryEnabled = idpDiscoveryEnabled;
}
}
Expand Up @@ -126,8 +126,6 @@ public class LoginInfoEndpoint {

private String baseUrl;

private boolean idpDiscoveryEnabled;

private String externalLoginUrl;

private String samlSPBaseUrl;
Expand Down Expand Up @@ -354,7 +352,7 @@ private String login(Model model, Principal principal, List<String> excludedProm

if (principal == null) {
boolean discoveryPerformed = Boolean.parseBoolean(request != null ? request.getParameter("discoveryPerformed") : null);
if (idpDiscoveryEnabled && !discoveryPerformed) {
if (IdentityZoneHolder.get().getConfig().isIdpDiscoveryEnabled() && !discoveryPerformed) {
return "idp_discovery/email";
}
return "login";
Expand Down Expand Up @@ -756,14 +754,6 @@ public void setProviderProvisioning(IdentityProviderProvisioning providerProvisi
this.providerProvisioning = providerProvisioning;
}

public boolean isIdpDiscoveryEnabled() {
return idpDiscoveryEnabled;
}

public void setIdpDiscoveryEnabled(boolean idpDiscoveryEnabled) {
this.idpDiscoveryEnabled = idpDiscoveryEnabled;
}

@ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "Unknown authentication token type, unable to derive user ID.")
public static final class UnknownPrincipalException extends RuntimeException {}

Expand Down
Expand Up @@ -32,6 +32,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

public class IdentityZoneConfigurationBootstrapTests extends JdbcTestBase {

Expand Down Expand Up @@ -169,4 +170,12 @@ public void test_prompts() throws Exception {
IdentityZoneConfiguration config = provisioning.retrieve(IdentityZone.getUaa().getId()).getConfig();
assertEquals(prompts, config.getPrompts());
}

@Test
public void idpDiscoveryEnabled() throws Exception {
bootstrap.setIdpDiscoveryEnabled(true);
bootstrap.afterPropertiesSet();
IdentityZoneConfiguration config = provisioning.retrieve(IdentityZone.getUaa().getId()).getConfig();
assertTrue(config.isIdpDiscoveryEnabled());
}
}
3 changes: 1 addition & 2 deletions uaa/src/main/webapp/WEB-INF/spring-servlet.xml
Expand Up @@ -393,7 +393,6 @@
<property name="expiringCodeStore" ref="codeStore"/>
<property name="externalLoginUrl" value="${login.url:''}"/>
<property name="providerProvisioning" ref="identityProviderProvisioning"/>
<property name="idpDiscoveryEnabled" value="${login.idpDiscoveryEnabled:false}"/>
</bean>

<bean id="healthzEndpoint" class="org.cloudfoundry.identity.uaa.health.HealthzEndpoint" />
Expand Down Expand Up @@ -427,6 +426,7 @@
<property name="selfServiceLinksEnabled" value="${login.selfServiceLinksEnabled:true}"/>
<property name="selfServiceLinks" ref="links" />
<property name="homeRedirect" value="${login.homeRedirect:null}"/>
<property name="idpDiscoveryEnabled" value="${login.idpDiscoveryEnabled:false}" />
<property name="logoutRedirectWhitelist"
value="#{@config['logout']==null ? null :
@config['logout']['redirect']==null ? null :
Expand All @@ -439,7 +439,6 @@
<property name="samlSpPrivateKey" value="#{'${login.serviceProviderKey:' + @defaultSamlKey + '}'}" />
<property name="samlSpPrivateKeyPassphrase" value="${login.serviceProviderKeyPassword:password}" />
<property name="samlSpCertificate" value="#{'${login.serviceProviderCertificate:' + @defaultSamlCert + '}'}" />

</bean>

<bean id="ldapLoginAuthenticationMgr" class="org.cloudfoundry.identity.uaa.authentication.manager.LdapLoginAuthenticationManager">
Expand Down
Expand Up @@ -18,6 +18,7 @@
import org.cloudfoundry.identity.uaa.account.ResetPasswordController;
import org.cloudfoundry.identity.uaa.authentication.manager.PeriodLockoutPolicy;
import org.cloudfoundry.identity.uaa.constants.OriginKeys;
import org.cloudfoundry.identity.uaa.impl.config.IdentityZoneConfigurationBootstrap;
import org.cloudfoundry.identity.uaa.impl.config.YamlServletProfileInitializer;
import org.cloudfoundry.identity.uaa.message.EmailService;
import org.cloudfoundry.identity.uaa.message.NotificationsService;
Expand Down Expand Up @@ -524,6 +525,13 @@ public void bootstrap_scim_groups_asMap_from_yaml() throws Exception {
assertThat(scimGroups, PredicateMatcher.<ScimGroup>has(g -> g.getDisplayName().equals("cat") && "The cat".equals(g.getDescription())));
}

@Test
public void bootstrap_idpDiscoveryEnabled_from_yml() throws Exception {
context = getServletContext(null, "login.yml", "test/bootstrap/bootstrap-test.yml", "file:./src/main/webapp/WEB-INF/spring-servlet.xml");
IdentityZoneConfigurationBootstrap bean = context.getBean(IdentityZoneConfigurationBootstrap.class);
assertTrue(bean.isIdpDiscoveryEnabled());
}

@Test
public void testBootstrappedIdps_and_ExcludedClaims_and_CorsConfig() throws Exception {

Expand Down
Expand Up @@ -166,7 +166,6 @@ public void tearDown() throws Exception {
MockPropertySource originalPropertySource = new MockPropertySource(originalProperties);
ReflectionUtils.setField(f, mockEnvironment, new MockPropertySource(originalProperties));
mockEnvironment.getPropertySources().addLast(originalPropertySource);
getWebApplicationContext().getBean(LoginInfoEndpoint.class).setIdpDiscoveryEnabled(false);
SecurityContextHolder.clearContext();
IdentityZoneHolder.clear();
}
Expand Down Expand Up @@ -255,9 +254,9 @@ public void testLogin_Post_When_DisableInternalUserManagement_Is_True() throws E
setDisableInternalAuth(false);
}
getMockMvc().perform(post("/login.do")
.with(cookieCsrf())
.param("username", user.getUserName())
.param("password", user.getPassword()))
.with(cookieCsrf())
.param("username", user.getUserName())
.param("password", user.getPassword()))
.andDo(print())
.andExpect(redirectedUrl("/"));
}
Expand Down Expand Up @@ -1669,9 +1668,12 @@ public void autologin_with_validCode_RedirectsToHome() throws Exception {

@Test
public void idpDiscoveryPageDisplayed_IfFlagIsEnabled() throws Exception {
getWebApplicationContext().getBean(LoginInfoEndpoint.class).setIdpDiscoveryEnabled(true);
IdentityZoneConfiguration config = new IdentityZoneConfiguration();
config.setIdpDiscoveryEnabled(true);
IdentityZone zone = setupZoneForIdpDiscovery(config);
getMockMvc().perform(get("/login")
.header("Accept", TEXT_HTML))
.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")))
Expand All @@ -1682,29 +1684,37 @@ public void idpDiscoveryPageDisplayed_IfFlagIsEnabled() throws Exception {

@Test
public void idpDiscoveryPageNotDisplayed_IfFlagIsEnabledAndDiscoveryFailedPreviously() throws Exception {
getWebApplicationContext().getBean(LoginInfoEndpoint.class).setIdpDiscoveryEnabled(true);
IdentityZoneConfiguration config = new IdentityZoneConfiguration();
config.setIdpDiscoveryEnabled(true);
IdentityZone zone = setupZoneForIdpDiscovery(config);

getMockMvc().perform(get("/login?discoveryPerformed=true")
.header("Accept", TEXT_HTML))
.header("Accept", TEXT_HTML)
.with(new SetServerNameRequestPostProcessor(zone.getSubdomain() + ".localhost")))
.andExpect(status().isOk())
.andExpect(view().name("login"));
}

@Test
public void idpDiscoveryClientNameDisplayed() throws Exception {
getWebApplicationContext().getBean(LoginInfoEndpoint.class).setIdpDiscoveryEnabled(true);
IdentityZoneConfiguration config = new IdentityZoneConfiguration();
config.setIdpDiscoveryEnabled(true);
IdentityZone zone = setupZoneForIdpDiscovery(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, "woohoo");
MockMvcUtils.utils().createClient(getMockMvc(), adminToken, client);
MockMvcUtils.utils().createClient(getMockMvc(), adminToken, client, zone);

SavedRequest savedRequest = getSavedRequest(client);
session.setAttribute("SPRING_SECURITY_SAVED_REQUEST", savedRequest);

getMockMvc().perform(get("/login")
.session(session)
.header("Accept", TEXT_HTML))
.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 woohoo")))
Expand All @@ -1715,11 +1725,16 @@ public void idpDiscoveryClientNameDisplayed() throws Exception {

@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 = setupZoneForIdpDiscovery(config);

setSelfServiceLinksEnabled(false);
getWebApplicationContext().getBean(LoginInfoEndpoint.class).setIdpDiscoveryEnabled(true);

getMockMvc().perform(MockMvcRequestBuilders.get("/login"))
.andExpect(xpath("//div[@class='action']//a").doesNotExist());
getMockMvc().perform(MockMvcRequestBuilders.get("/login")
.with(new SetServerNameRequestPostProcessor(zone.getSubdomain() + ".localhost")))
.andExpect(xpath("//div[@class='action']//a").doesNotExist());
}

@Test
Expand Down Expand Up @@ -1920,6 +1935,15 @@ private void attemptFailedLogin(int numberOfAttempts, String username, String su
}
}

private IdentityZone setupZoneForIdpDiscovery(IdentityZoneConfiguration config) throws Exception {
String zoneId = generator.generate().toLowerCase();
IdentityZone zone = MultitenancyFixture.identityZone(zoneId, zoneId);
zone = createOtherIdentityZone(zone.getSubdomain(), 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
Expand Down
Expand Up @@ -67,6 +67,7 @@ public class IdentityZoneEndpointDocs extends InjectedMockContextTest {
private static final String PROMPTS_NAME_DESC = "Name of field";
private static final String PROMPTS_TYPE_DESC = "What kind of field this is (e.g. text or password)";
private static final String PROMPTS_TEXT_DESC = "Actual text displayed on prompt for field";
private static final String IDP_DISCOVERY_ENABLED_FLAG = "IDP Discovery should be set to true if you have configured more than one identity provider for UAA. The discovery relies on email domain being set for each additional provider";
private TestClient testClient;

@Before
Expand Down Expand Up @@ -128,6 +129,8 @@ public void createIdentityZone() throws Exception {
fieldWithPath("config.prompts[].type").description(PROMPTS_TYPE_DESC).attributes(key("constraints").value("Optional")),
fieldWithPath("config.prompts[].text").description(PROMPTS_TEXT_DESC).attributes(key("constraints").value("Optional")),

fieldWithPath("config.idpDiscoveryEnabled").description(IDP_DISCOVERY_ENABLED_FLAG).attributes(key("constraints").value("Optional")),

fieldWithPath("created").ignored(),
fieldWithPath("last_modified").ignored()
};
Expand Down Expand Up @@ -223,6 +226,8 @@ public void getAllIdentityZones() throws Exception {
fieldWithPath("[].config.prompts[].type").description(PROMPTS_TYPE_DESC),
fieldWithPath("[].config.prompts[].text").description(PROMPTS_TEXT_DESC),

fieldWithPath("[].config.idpDiscoveryEnabled").description(IDP_DISCOVERY_ENABLED_FLAG),

fieldWithPath("[].created").ignored(),
fieldWithPath("[].last_modified").ignored()
);
Expand Down Expand Up @@ -292,6 +297,8 @@ public void updateIdentityZone() throws Exception {
fieldWithPath("config.prompts[].type").description(PROMPTS_TYPE_DESC).attributes(key("constraints").value("Optional")),
fieldWithPath("config.prompts[].text").description(PROMPTS_TEXT_DESC).attributes(key("constraints").value("Optional")),

fieldWithPath("config.idpDiscoveryEnabled").description(IDP_DISCOVERY_ENABLED_FLAG).attributes(key("constraints").value("Optional")),

fieldWithPath("created").ignored(),
fieldWithPath("last_modified").ignored()
);
Expand Down Expand Up @@ -398,6 +405,8 @@ private Snippet getResponseFields() {
fieldWithPath("config.prompts[].type").description(PROMPTS_TYPE_DESC),
fieldWithPath("config.prompts[].text").description(PROMPTS_TEXT_DESC),

fieldWithPath("config.idpDiscoveryEnabled").description(IDP_DISCOVERY_ENABLED_FLAG),

fieldWithPath("created").ignored(),
fieldWithPath("last_modified").ignored()
);
Expand Down
1 change: 1 addition & 0 deletions uaa/src/test/resources/test/bootstrap/bootstrap-test.yml
Expand Up @@ -15,6 +15,7 @@ zones:
- test4.localhost

login:
idpDiscoveryEnabled: true
oauth:
providers:
my-oauth-provider:
Expand Down

0 comments on commit 068c3b6

Please sign in to comment.