Skip to content

Commit

Permalink
Complete SAML invitations
Browse files Browse the repository at this point in the history
  • Loading branch information
fhanik committed Sep 9, 2015
1 parent d95f5ba commit 306efdc
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 33 deletions.
Expand Up @@ -9,6 +9,12 @@ public LockoutPolicy() {
lockoutPeriodSeconds = lockoutAfterFailures = countFailuresWithin = -1;
}

public LockoutPolicy(int countFailuresWithin, int lockoutAfterFailures, int lockoutPeriodSeconds) {
this.countFailuresWithin = countFailuresWithin;
this.lockoutAfterFailures = lockoutAfterFailures;
this.lockoutPeriodSeconds = lockoutPeriodSeconds;
}

public LockoutPolicy setLockoutPeriodSeconds(int lockoutPeriod) {
this.lockoutPeriodSeconds = lockoutPeriod;
return this;
Expand Down
Expand Up @@ -197,18 +197,25 @@ public String acceptInvitePage(@RequestParam String code, Model model, HttpServl
Map<String, String> codeData = expiringCodeService.verifyCode(code);
List<IdentityProvider> providers = filterIdpsForClientAndEmailDomain(codeData.get("client_id"), codeData.get("email"));
if (providers!=null && providers.size()==0) {
logger.debug(String.format("No available invitation providers for email:%s, id:%s", codeData.get("email"), codeData.get("user_id")));
return handleUnprocessableEntity(model, response, "error_message_code", "no_suitable_idp", "invitations/accept_invite");
} else if (providers!=null && providers.size()==1 && Origin.SAML.equals(providers.get(0).getType())) {
SamlIdentityProviderDefinition definition = providers.get(0).getConfigValue(SamlIdentityProviderDefinition.class);
return "redirect:"+ SamlRedirectUtils.getIdpRedirectUrl(definition, getSpEntityID());
} else {
getProvidersByType(model, providers, Origin.UAA);
getProvidersByType(model, providers, Origin.SAML);
getProvidersByType(model, providers, Origin.LDAP);
UaaPrincipal uaaPrincipal = new UaaPrincipal(codeData.get("user_id"), codeData.get("email"), codeData.get("email"), Origin.UNKNOWN, null, IdentityZoneHolder.get().getId());
UaaAuthentication token = new UaaAuthentication(uaaPrincipal, UaaAuthority.USER_AUTHORITIES, new UaaAuthenticationDetails(request));
SecurityContextHolder.getContext().setAuthentication(token);
if (providers != null && providers.size() == 1 && Origin.SAML.equals(providers.get(0).getType())) {
SamlIdentityProviderDefinition definition = providers.get(0).getConfigValue(SamlIdentityProviderDefinition.class);
String redirect = "redirect:/" + SamlRedirectUtils.getIdpRedirectUrl(definition, getSpEntityID());
logger.debug(String.format("Redirecting invitation for email:%s, id:%s single SAML IDP URL:%s", codeData.get("email"), codeData.get("user_id"), redirect));
return redirect;
} else {
getProvidersByType(model, providers, Origin.UAA);
getProvidersByType(model, providers, Origin.SAML);
getProvidersByType(model, providers, Origin.LDAP);
model.addAttribute("entityID", SamlRedirectUtils.getZonifiedEntityId(getSpEntityID()));
logger.debug(String.format("Sending user to accept invitation page email:%s, id:%s", codeData.get("email"), codeData.get("user_id")));
}
}
UaaPrincipal uaaPrincipal = new UaaPrincipal(codeData.get("user_id"), codeData.get("email"), codeData.get("email"), Origin.UNKNOWN, null, IdentityZoneHolder.get().getId());
UaaAuthentication token = new UaaAuthentication(uaaPrincipal, UaaAuthority.USER_AUTHORITIES, new UaaAuthenticationDetails(request));
SecurityContextHolder.getContext().setAuthentication(token);
model.addAllAttributes(codeData);
return "invitations/accept_invite";
} catch (CodeNotFoundException e) {
Expand Down
Expand Up @@ -43,7 +43,7 @@ <h1>Create your <th:block th:text="${pivotal and isUaa ? 'Pivotal ID' : 'account
<div class="saml-login">
<p>Sign in with: </p>
<div th:each="idp : ${idps}" th:if="${idp.showSamlLink}">
<a href="" th:href="@{saml/discovery(returnIDParam=idp,entityID=${entityID},idp=${idp.idpEntityAlias},isPassive=true)}" th:text="${idp.linkText}" class="saml-login-link">Use your corporate credentials</a>
<a href="" th:href="@{/saml/discovery(returnIDParam=idp,entityID=${entityID},idp=${idp.idpEntityAlias},isPassive=true)}" th:text="${idp.linkText}" class="saml-login-link">Use your corporate credentials</a>
</div>
</div>
</th:block>
Expand Down
Expand Up @@ -241,8 +241,12 @@ private String generateCode() {
return generateCode(userEmail, userEmail, "http://localhost:8080/app/");
}
private String generateCode(String username, String userEmail, String redirectUri) {
return generateCode(baseUrl, uaaUrl, username, userEmail, redirectUri, loginToken, scimToken);
}

public static String generateCode(String baseUrl, String uaaUrl, String username, String userEmail, String redirectUri, String scimWriteToken, String scimReadToken) {
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + loginToken);
headers.add("Authorization", "Bearer " + scimWriteToken);
RestTemplate uaaTemplate = new RestTemplate();
ScimUser scimUser = new ScimUser();
scimUser.setUserName(username);
Expand All @@ -251,7 +255,7 @@ private String generateCode(String username, String userEmail, String redirectUr

String userId = null;
try {
userId = IntegrationTestUtils.getUserId(scimToken, baseUrl, Origin.UNKNOWN, username);
userId = IntegrationTestUtils.getUserId(scimReadToken, baseUrl, Origin.UNKNOWN, username);
} catch (RuntimeException x) {
}
if (userId==null) {
Expand Down
Expand Up @@ -18,6 +18,8 @@
import org.cloudfoundry.identity.uaa.ServerRunning;
import org.cloudfoundry.identity.uaa.authentication.Origin;
import org.cloudfoundry.identity.uaa.client.ClientConstants;
import org.cloudfoundry.identity.uaa.config.LockoutPolicy;
import org.cloudfoundry.identity.uaa.config.PasswordPolicy;
import org.cloudfoundry.identity.uaa.integration.util.IntegrationTestUtils;
import org.cloudfoundry.identity.uaa.login.saml.SamlIdentityProviderDefinition;
import org.cloudfoundry.identity.uaa.login.test.LoginServerClassRunner;
Expand All @@ -27,6 +29,8 @@
import org.cloudfoundry.identity.uaa.util.JsonUtils;
import org.cloudfoundry.identity.uaa.zone.IdentityProvider;
import org.cloudfoundry.identity.uaa.zone.IdentityZone;
import org.cloudfoundry.identity.uaa.zone.UaaIdentityProviderDefinition;
import org.flywaydb.core.internal.util.StringUtils;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Before;
Expand All @@ -45,6 +49,7 @@
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.client.test.TestAccounts;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.common.util.RandomValueStringGenerator;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.test.context.ContextConfiguration;
Expand All @@ -56,12 +61,14 @@
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
Expand Down Expand Up @@ -208,7 +215,7 @@ private void testSimpleSamlLogin(String firstUrl, String lookfor) throws Excepti

webDriver.get(baseUrl + firstUrl);
Assert.assertEquals("Cloud Foundry", webDriver.getTitle());
webDriver.findElement(By.xpath("//a[text()='"+provider.getConfigValue(SamlIdentityProviderDefinition.class).getLinkText()+"']")).click();
webDriver.findElement(By.xpath("//a[text()='" + provider.getConfigValue(SamlIdentityProviderDefinition.class).getLinkText() + "']")).click();
//takeScreenShot();
webDriver.findElement(By.xpath("//h2[contains(text(), 'Enter your username and password')]"));
webDriver.findElement(By.name("username")).clear();
Expand Down Expand Up @@ -298,7 +305,7 @@ protected void deleteUser(String origin, String username)
throws Exception {

String zoneAdminToken = IntegrationTestUtils.getClientCredentialsToken(serverRunning,
"admin", "adminsecret");
"admin", "adminsecret");

String userId = IntegrationTestUtils.getUserId(zoneAdminToken, baseUrl, origin, username);
if (null == userId) {
Expand All @@ -308,6 +315,114 @@ protected void deleteUser(String origin, String username)
IntegrationTestUtils.deleteUser(zoneAdminToken, baseUrl, userId);
}

@Test
public void test_SamlInvitation_Automatic_Redirect_In_Zone2() throws Exception {
perform_SamlInvitation_Automatic_Redirect_In_Zone2("marissa2",true);
perform_SamlInvitation_Automatic_Redirect_In_Zone2("marissa2",true);
perform_SamlInvitation_Automatic_Redirect_In_Zone2("marissa2",true);

perform_SamlInvitation_Automatic_Redirect_In_Zone2("marissa3",false);
perform_SamlInvitation_Automatic_Redirect_In_Zone2("marissa3",false);
perform_SamlInvitation_Automatic_Redirect_In_Zone2("marissa3", false);
}

public void perform_SamlInvitation_Automatic_Redirect_In_Zone2(String username, boolean emptyList) throws Exception {
//ensure we are able to resolve DNS for hostname testzone1.localhost
assumeTrue("Expected testzone1/2.localhost to resolve to 127.0.0.1", doesSupportZoneDNS());
String zoneId = "testzone2";

//identity client token
RestTemplate identityClient = IntegrationTestUtils.getClientCredentialsTempate(
IntegrationTestUtils.getClientCredentialsResource(baseUrl,new String[] {"zones.write", "zones.read", "scim.zones"}, "identity", "identitysecret")
);
//admin client token - to create users
RestTemplate adminClient = IntegrationTestUtils.getClientCredentialsTempate(
IntegrationTestUtils.getClientCredentialsResource(baseUrl,new String[0] , "admin", "adminsecret")
);
//create the zone
IntegrationTestUtils.createZoneOrUpdateSubdomain(identityClient, baseUrl, zoneId, zoneId);

//create a zone admin user
String email = new RandomValueStringGenerator().generate() +"@samltesting.org";
ScimUser user = IntegrationTestUtils.createUser(adminClient, baseUrl,email ,"firstname", "lastname", email, true);
IntegrationTestUtils.makeZoneAdmin(identityClient, baseUrl, user.getId(), zoneId);

//get the zone admin token
String zoneAdminToken =
IntegrationTestUtils.getAuthorizationCodeToken(serverRunning,
UaaTestAccounts.standard(serverRunning),
"identity",
"identitysecret",
email,
"secr3T");

SamlIdentityProviderDefinition samlIdentityProviderDefinition = createTestZone2IDP("simplesamlphp");
IdentityProvider provider = new IdentityProvider();
provider.setIdentityZoneId(zoneId);
provider.setType(Origin.SAML);
provider.setActive(true);
provider.setConfig(JsonUtils.writeValueAsString(samlIdentityProviderDefinition));
provider.setOriginKey(samlIdentityProviderDefinition.getIdpEntityAlias());
provider.setName("simplesamlphp for testzone2");

provider = IntegrationTestUtils.createOrUpdateProvider(zoneAdminToken,baseUrl,provider);
assertEquals(provider.getOriginKey(), provider.getConfigValue(SamlIdentityProviderDefinition.class).getIdpEntityAlias());

UaaIdentityProviderDefinition uaaDefinition = new UaaIdentityProviderDefinition(
new PasswordPolicy(1,255,0,0,0,0,12),
new LockoutPolicy(10, 10, 10)
);
uaaDefinition.setEmailDomain(emptyList ? Collections.EMPTY_LIST : null);
IdentityProvider uaaProvider = IntegrationTestUtils.getProvider(zoneAdminToken, baseUrl, zoneId, Origin.UAA);
uaaProvider.setConfig(JsonUtils.writeValueAsString(uaaDefinition));
uaaProvider = IntegrationTestUtils.createOrUpdateProvider(zoneAdminToken,baseUrl,uaaProvider);

BaseClientDetails uaaAdmin = new BaseClientDetails("admin","","", "client_credentials","uaa.admin,scim.read,scim.write");
uaaAdmin.setClientSecret("adminsecret");
IntegrationTestUtils.createOrUpdateClient(zoneAdminToken, baseUrl, zoneId, uaaAdmin);

String zoneUrl = baseUrl.replace("localhost", "testzone2.localhost");
String uaaAdminToken = testClient.getOAuthAccessToken(zoneUrl, "admin", "adminsecret", "client_credentials", "");

String useremail = username + "@test.org";
String code = InvitationsIT.generateCode(zoneUrl, zoneUrl, useremail, useremail, "", uaaAdminToken, uaaAdminToken);
String invitedUserId = IntegrationTestUtils.getUserId(uaaAdminToken, zoneUrl, Origin.UNKNOWN, useremail);
String existingUserId = IntegrationTestUtils.getUserId(uaaAdminToken, zoneUrl, samlIdentityProviderDefinition.getIdpEntityAlias(), useremail);
assertNotEquals(invitedUserId, existingUserId);
webDriver.get(zoneUrl + "/logout.do");
webDriver.get(zoneUrl + "/invitations/accept?code=" + code);
if (!emptyList) {
WebElement element = webDriver.findElement(By.xpath("//a[text()='" + samlIdentityProviderDefinition.getLinkText() + "']"));
assertNotNull(element);
element.click();
}

//IntegrationTestUtils.takeScreenShot(webDriver);
//we should now be in the Simple SAML PHP site


webDriver.findElement(By.xpath("//h2[contains(text(), 'Enter your username and password')]"));
webDriver.findElement(By.name("username")).clear();
webDriver.findElement(By.name("username")).sendKeys(username);
webDriver.findElement(By.name("password")).sendKeys("saml2");
webDriver.findElement(By.xpath("//input[@value='Login']")).click();
assertThat(webDriver.findElement(By.cssSelector("h1")).getText(), Matchers.containsString("Where to?"));

uaaProvider.setConfig(JsonUtils.writeValueAsString(uaaDefinition.setEmailDomain(null)));
IntegrationTestUtils.createOrUpdateProvider(zoneAdminToken,baseUrl,uaaProvider);

String acceptedUserId = IntegrationTestUtils.getUserId(uaaAdminToken, zoneUrl, samlIdentityProviderDefinition.getIdpEntityAlias(), useremail);
if (StringUtils.hasText(existingUserId)) {
assertEquals(acceptedUserId, existingUserId);
} else {
assertEquals(invitedUserId, acceptedUserId);
}

webDriver.get(baseUrl + "/logout.do");
webDriver.get(zoneUrl + "/logout.do");
webDriver.get("http://simplesamlphp.cfapps.io/module.php/core/authenticate.php?as=example-userpass&logout");
}

@Test
public void testSamlLoginClientIDPAuthorizationAutomaticRedirectInZone1() throws Exception {
//ensure we are able to resolve DNS for hostname testzone1.localhost
Expand Down Expand Up @@ -497,7 +612,7 @@ public void testLoginPageShowsIDPsForAuthcodeClient() throws Exception {

webDriver.get(baseUrl + "/oauth/authorize?client_id=" + clientId + "&redirect_uri=http%3A%2F%2Flocalhost%3A8888%2Flogin&response_type=code&state=8tp0tR");
webDriver.findElement(By.xpath("//a[text()='" + provider.getConfigValue(SamlIdentityProviderDefinition.class).getLinkText() + "']"));
webDriver.findElement(By.xpath("//a[text()='" + provider2.getConfigValue(SamlIdentityProviderDefinition.class).getLinkText()+"']"));
webDriver.findElement(By.xpath("//a[text()='" + provider2.getConfigValue(SamlIdentityProviderDefinition.class).getLinkText() + "']"));
}

@Test
Expand Down Expand Up @@ -587,6 +702,10 @@ protected boolean doesSupportZoneDNS() {
}
}

public SamlIdentityProviderDefinition createTestZone2IDP(String alias) {
return createSimplePHPSamlIDP(alias, "testzone2");
}

public SamlIdentityProviderDefinition createTestZone1IDP(String alias) {
return createSimplePHPSamlIDP(alias, "testzone1");
}
Expand Down
@@ -1,5 +1,5 @@
/*******************************************************************************
* Cloud Foundry
* Cloud Foundry
* Copyright (c) [2009-2014] Pivotal Software, Inc. All Rights Reserved.
*
* This product is licensed to you under the Apache License, Version 2.0 (the "License").
Expand Down Expand Up @@ -47,6 +47,9 @@ public String getBasicAuthHeaderValue(String username, String password) {
}

public String getOAuthAccessToken(String username, String password, String grantType, String scope) {
return getOAuthAccessToken(baseUrl, username, password, grantType, scope);
}
public String getOAuthAccessToken(String baseUrl, String username, String password, String grantType, String scope) {
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", getBasicAuthHeaderValue(username, password));

Expand Down

0 comments on commit 306efdc

Please sign in to comment.