Skip to content

Commit

Permalink
Add unit tests for inviting a saml user whose username is not email.
Browse files Browse the repository at this point in the history
  • Loading branch information
mbhave committed Oct 30, 2015
1 parent 074ae70 commit c27c008
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 15 deletions.
Expand Up @@ -226,7 +226,7 @@ protected UaaUser createIfMissing(UaaPrincipal samlPrincipal, boolean addNew, Co
invitedUserId = (String) RequestContextHolder.currentRequestAttributes().getAttribute("user_id", RequestAttributes.SCOPE_SESSION);
user = userDatabase.retrieveUserById(invitedUserId);
if ( userAttributes.getFirst(EMAIL_ATTRIBUTE_NAME) != null ) {
if ( userAttributes.getFirst(EMAIL_ATTRIBUTE_NAME).equalsIgnoreCase(user.getEmail()) ) {
if (!userAttributes.getFirst(EMAIL_ATTRIBUTE_NAME).equalsIgnoreCase(user.getEmail()) ) {
throw new BadCredentialsException("SAML User email mismatch. Authenticated email doesn't match invited email.");
}
} else {
Expand Down
Expand Up @@ -17,6 +17,7 @@
import org.cloudfoundry.identity.uaa.util.JsonUtils;
import org.cloudfoundry.identity.uaa.zone.IdentityProvider;
import org.cloudfoundry.identity.uaa.zone.IdentityProviderProvisioning;
import org.cloudfoundry.identity.uaa.zone.IdentityZone;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
import org.junit.After;
import org.junit.Before;
Expand All @@ -27,6 +28,7 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.ClientDetailsService;
Expand All @@ -35,12 +37,17 @@
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.util.UriComponentsBuilder;

import java.sql.Timestamp;
import java.util.HashMap;
Expand All @@ -60,6 +67,7 @@
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
Expand Down Expand Up @@ -92,6 +100,9 @@ public class InvitationsControllerTest {
@Autowired
IdentityProviderProvisioning providerProvisioning;

@Autowired
UaaUserDatabase userDatabase;

@Before
public void setUp() throws Exception {
SecurityContextHolder.clearContext();
Expand Down Expand Up @@ -133,6 +144,55 @@ public void testAcceptInvitationsPage() throws Exception {
assertEquals("user@example.com", principal.getEmail());
}

@Test
public void acceptInvitePage_for_unverifiedSamlUser() throws Exception {
Map<String,String> codeData = new HashMap<>();
codeData.put("user_id", "user-id-001");
codeData.put("email", "user@example.com");
codeData.put("client_id", "client-id");
codeData.put("redirect_uri", "blah.test.com");
codeData.put("origin", "test-saml");
when(expiringCodeStore.retrieveCode("the_secret_code")).thenReturn(new ExpiringCode("code", new Timestamp(System.currentTimeMillis()), JsonUtils.writeValueAsString(codeData)));
when(expiringCodeStore.generateCode(anyString(), anyObject())).thenReturn(new ExpiringCode("code", new Timestamp(System.currentTimeMillis()), JsonUtils.writeValueAsString(codeData)));
IdentityProvider provider = new IdentityProvider();
SamlIdentityProviderDefinition definition = new SamlIdentityProviderDefinition("http://test.saml.com", "test-saml", "test", 0, false, true, "testsaml", "test.com", IdentityZone.getUaa().getId());
provider.setConfig(JsonUtils.writeValueAsString(definition));
provider.setType(Origin.SAML);
when(providerProvisioning.retrieveByOrigin(eq("test-saml"), anyString())).thenReturn(provider);
MockHttpServletRequestBuilder get = get("/invitations/accept")
.param("code", "the_secret_code");

MvcResult result = mockMvc.perform(get)
.andExpect(redirectedUrl("/saml/discovery?returnIDParam=idp&entityID=sp-entity-id&idp=test-saml&isPassive=true"))
.andReturn();

assertEquals(true, result.getRequest().getSession().getAttribute("IS_INVITE_ACCEPTANCE"));
assertEquals("user-id-001", result.getRequest().getSession().getAttribute("user_id"));
}

@Test
public void acceptInvitePage_for_verifiedUser() throws Exception {
UaaUser user = new UaaUser("user@example.com", "", "user@example.com", "Given", "family");
user.modifyId("verified-user");
user.setVerified(true);
when(userDatabase.retrieveUserById("verified-user")).thenReturn(user);
Map<String,String> codeData = new HashMap<>();
codeData.put("user_id", "verified-user");
codeData.put("email", "user@example.com");

when(expiringCodeStore.retrieveCode("the_secret_code")).thenReturn(new ExpiringCode("code", new Timestamp(System.currentTimeMillis()), JsonUtils.writeValueAsString(codeData)));
when(expiringCodeStore.generateCode(anyString(), anyObject())).thenReturn(new ExpiringCode("code", new Timestamp(System.currentTimeMillis()), JsonUtils.writeValueAsString(codeData)));
when(invitationsService.acceptInvitation(anyString(), anyString())).thenReturn(new InvitationsService.AcceptedInvitation("blah.test.com", new ScimUser()));
IdentityProvider provider = new IdentityProvider();
provider.setType(Origin.UAA);
when(providerProvisioning.retrieveByOrigin(anyString(), anyString())).thenReturn(provider);
MockHttpServletRequestBuilder get = get("/invitations/accept")
.param("code", "the_secret_code");

mockMvc.perform(get)
.andExpect(redirectedUrl("blah.test.com"));
}

@Test
public void testAcceptInvitePageWithExpiredCode() throws Exception {
when(expiringCodeStore.retrieveCode(anyString())).thenReturn(null);
Expand Down Expand Up @@ -298,6 +358,7 @@ InvitationsController invitationsController(InvitationsService invitationsServic
result.setPasswordValidator(passwordPolicyValidator);
result.setProviderProvisioning(providerProvisioning);
result.setUserDatabase(userDatabase);
result.setSpEntityID("sp-entity-id");
return result;
}

Expand Down
Expand Up @@ -112,7 +112,7 @@ public void testInviteUserWithClientRedirect() throws Exception {

public void performInviteUser(String email, boolean isVerified) throws Exception {
webDriver.get(baseUrl + "/logout.do");
String code = generateCode(email, email, "http://localhost:8080/app/", Origin.UAA);
String code = createInvitation(email, email, "http://localhost:8080/app/", Origin.UAA);
String invitedUserId = IntegrationTestUtils.getUserIdByField(scimToken, baseUrl, Origin.UAA, "email", email);
if (isVerified) {
ScimUser user = IntegrationTestUtils.getUser(scimToken, baseUrl, invitedUserId);
Expand Down Expand Up @@ -146,10 +146,10 @@ public void performInviteUser(String email, boolean isVerified) throws Exception
}

@Test
public void acceptInvitation_for_externalUser() throws Exception {
public void acceptInvitation_for_samlUser() throws Exception {
webDriver.get(baseUrl + "/logout.do");
String email = "testinvite@test.org";
String code = generateCode(email, email, "http://localhost:8080/app/", "simplesamlphp");
String code = createInvitation(email, email, "http://localhost:8080/app/", "simplesamlphp");

String invitedUserId = IntegrationTestUtils.getUserIdByField(scimToken, baseUrl, "simplesamlphp", "email", email);
IntegrationTestUtils.createIdentityProvider("simplesamlphp", true, baseUrl, serverRunning);
Expand All @@ -161,17 +161,16 @@ public void acceptInvitation_for_externalUser() throws Exception {
webDriver.findElement(By.name("password")).sendKeys("saml");
webDriver.findElement(By.xpath("//input[@value='Login']")).click();

ScimUser scimuser = IntegrationTestUtils.getUser(scimToken, baseUrl, invitedUserId);
IntegrationTestUtils.getUser(scimToken, baseUrl, invitedUserId);
String acceptedUsername = IntegrationTestUtils.getUsernameById(scimToken, baseUrl, invitedUserId);
assertEquals("user_only_for_invitations_test", acceptedUsername);
//webdriver follows redirects so we should be on the UAA authorization page
webDriver.findElement(By.id("application_authorization"));
}


@Test
public void testInsecurePasswordDisplaysErrorMessage() throws Exception {
String code = generateCode();
String code = createInvitation();
webDriver.get(baseUrl + "/invitations/accept?code=" + code);
assertEquals("Create your account", webDriver.findElement(By.tagName("h1")).getText());

Expand All @@ -183,16 +182,16 @@ public void testInsecurePasswordDisplaysErrorMessage() throws Exception {
assertThat(webDriver.findElement(By.cssSelector(".alert-error")).getText(), containsString("Password must be no more than 255 characters in length."));
}

private String generateCode() {
private String createInvitation() {
String userEmail = "user" + new SecureRandom().nextInt() + "@example.com";
return generateCode(userEmail, userEmail, "http://localhost:8080/app/", Origin.UAA);
return createInvitation(userEmail, userEmail, "http://localhost:8080/app/", Origin.UAA);
}

private String generateCode(String username, String userEmail, String redirectUri, String origin) {
return generateCode(baseUrl, uaaUrl, username, userEmail, origin, redirectUri, loginToken, scimToken);
private String createInvitation(String username, String userEmail, String redirectUri, String origin) {
return createInvitation(baseUrl, uaaUrl, username, userEmail, origin, redirectUri, loginToken, scimToken);
}

public static String generateCode(String baseUrl, String uaaUrl, String username, String userEmail, String origin, String redirectUri, String scimWriteToken, String scimReadToken) {
public static String createInvitation(String baseUrl, String uaaUrl, String username, String userEmail, String origin, String redirectUri, String scimWriteToken, String scimReadToken) {
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + scimWriteToken);
RestTemplate uaaTemplate = new RestTemplate();
Expand All @@ -216,7 +215,7 @@ public static String generateCode(String baseUrl, String uaaUrl, String username
userId = response.getBody().getId();
} else {
scimUser.setVerified(false);
scimUser = IntegrationTestUtils.updateUser(scimWriteToken, uaaUrl, scimUser);
IntegrationTestUtils.updateUser(scimWriteToken, uaaUrl, scimUser);
}

Timestamp expiry = new Timestamp(System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(System.currentTimeMillis() + 24 * 3600, TimeUnit.MILLISECONDS));
Expand Down
Expand Up @@ -365,7 +365,7 @@ public void perform_SamlInvitation_Automatic_Redirect_In_Zone2(String username,
String uaaAdminToken = testClient.getOAuthAccessToken(zoneUrl, "admin", "adminsecret", "client_credentials", "");

String useremail = username + "@test.org";
String code = InvitationsIT.generateCode(zoneUrl, zoneUrl, useremail, useremail, samlIdentityProviderDefinition.getIdpEntityAlias(), "", uaaAdminToken, uaaAdminToken);
String code = InvitationsIT.createInvitation(zoneUrl, zoneUrl, useremail, useremail, samlIdentityProviderDefinition.getIdpEntityAlias(), "", uaaAdminToken, uaaAdminToken);
String invitedUserId = IntegrationTestUtils.getUserId(uaaAdminToken, zoneUrl, samlIdentityProviderDefinition.getIdpEntityAlias(), useremail);
String existingUserId = IntegrationTestUtils.getUserId(uaaAdminToken, zoneUrl, samlIdentityProviderDefinition.getIdpEntityAlias(), useremail);
webDriver.get(zoneUrl + "/logout.do");
Expand Down
Expand Up @@ -21,6 +21,7 @@
import org.cloudfoundry.identity.uaa.rest.jdbc.JdbcPagingListFactory;
import org.cloudfoundry.identity.uaa.scim.ScimGroup;
import org.cloudfoundry.identity.uaa.scim.ScimGroupProvisioning;
import org.cloudfoundry.identity.uaa.scim.ScimUser;
import org.cloudfoundry.identity.uaa.scim.ScimUserProvisioning;
import org.cloudfoundry.identity.uaa.scim.bootstrap.ScimUserBootstrap;
import org.cloudfoundry.identity.uaa.scim.jdbc.JdbcScimGroupExternalMembershipManager;
Expand All @@ -36,6 +37,7 @@
import org.cloudfoundry.identity.uaa.zone.IdentityProviderProvisioning;
import org.cloudfoundry.identity.uaa.zone.IdentityZone;
import org.cloudfoundry.identity.uaa.zone.JdbcIdentityProviderProvisioning;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.opensaml.saml2.core.Assertion;
Expand All @@ -45,6 +47,8 @@
import org.opensaml.xml.XMLObject;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.saml.SAMLAuthenticationToken;
Expand All @@ -54,6 +58,9 @@
import org.springframework.security.saml.log.SAMLLogger;
import org.springframework.security.saml.metadata.ExtendedMetadata;
import org.springframework.security.saml.websso.WebSSOProfileConsumer;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Arrays;
import java.util.Collections;
Expand All @@ -66,9 +73,11 @@
import static org.cloudfoundry.identity.uaa.ExternalIdentityProviderDefinition.USER_ATTRIBUTE_PREFIX;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
Expand Down Expand Up @@ -99,6 +108,7 @@ public class LoginSamlAuthenticationProviderTests extends JdbcTestBase {
SAMLLogger samlLogger = mock(SAMLLogger.class);
SamlIdentityProviderDefinition providerDefinition = new SamlIdentityProviderDefinition();
private IdentityProvider provider;
private ScimUserProvisioning userProvisioning;

public List<Attribute> getAttributes(Map<String,Object> values) {
List<Attribute> result = new LinkedList<>();
Expand Down Expand Up @@ -130,7 +140,7 @@ public List<Attribute> getAttributes(final String name, Object value) {

@Before
public void configureProvider() throws Exception {
ScimUserProvisioning userProvisioning = new JdbcScimUserProvisioning(jdbcTemplate, new JdbcPagingListFactory(jdbcTemplate, limitSqlAdapter));
userProvisioning = new JdbcScimUserProvisioning(jdbcTemplate, new JdbcPagingListFactory(jdbcTemplate, limitSqlAdapter));
ScimGroupProvisioning groupProvisioning = new JdbcScimGroupProvisioning(jdbcTemplate, new JdbcPagingListFactory(jdbcTemplate, limitSqlAdapter));

ScimGroup uaaSamlUser = groupProvisioning.create(new ScimGroup(null,UAA_SAML_USER,IdentityZone.getUaa().getId()));
Expand Down Expand Up @@ -289,6 +299,58 @@ public void add_external_groups_to_authentication_with_whitelist() throws Except
assertEquals(Collections.singleton(SAML_ADMIN), authentication.getExternalGroups());
}

@Test
public void update_invitedUser_whose_username_is_notEmail() throws Exception {
ScimUser scimUser = getInvitedUser();

SAMLCredential credential = getUserCredential("marissa-invited", "Marissa-invited", null, "marissa.invited@test.org", null);
when(consumer.processAuthenticationResponse(anyObject())).thenReturn(credential);
getAuthentication();

UaaUser user = userDatabase.retrieveUserById(scimUser.getId());
assertTrue(user.isVerified());
assertEquals("marissa-invited", user.getUsername());
assertEquals("marissa.invited@test.org", user.getEmail());

RequestContextHolder.resetRequestAttributes();
}

@Test
public void invitedUser_authentication_whenAuthenticatedEmailDoesNotMatchInvitedEmail() throws Exception {
Map<String,Object> attributeMappings = new HashMap<>();
attributeMappings.put("email", "emailAddress");
providerDefinition.setAttributeMappings(attributeMappings);
provider.setConfig(JsonUtils.writeValueAsString(providerDefinition));
providerProvisioning.update(provider);

ScimUser scimUser = getInvitedUser();

SAMLCredential credential = getUserCredential("marissa-invited", "Marissa-invited", null, "different@test.org", null);
when(consumer.processAuthenticationResponse(anyObject())).thenReturn(credential);
try {
getAuthentication();
fail();
} catch (BadCredentialsException e) {
UaaUser user = userDatabase.retrieveUserById(scimUser.getId());
assertFalse(user.isVerified());
}
RequestContextHolder.resetRequestAttributes();
}

private ScimUser getInvitedUser() {
ScimUser invitedUser = new ScimUser(null, "marissa.invited@test.org", "Marissa", "Bloggs");
invitedUser.setPassword("a");
invitedUser.setPrimaryEmail("marissa.invited@test.org");
ScimUser scimUser = userProvisioning.create(invitedUser);

RequestAttributes attributes = new ServletRequestAttributes(new MockHttpServletRequest());
attributes.setAttribute("IS_INVITE_ACCEPTANCE", true, RequestAttributes.SCOPE_SESSION);
attributes.setAttribute("user_id", scimUser.getId(), RequestAttributes.SCOPE_SESSION);
RequestContextHolder.setRequestAttributes(attributes);

return scimUser;
}

@Test
public void update_existingUser_if_attributes_different() throws Exception {
getAuthentication();
Expand Down

0 comments on commit c27c008

Please sign in to comment.