Skip to content

Commit

Permalink
Store the user attribute information received from the external SAML …
Browse files Browse the repository at this point in the history
…on the Shadow User Account

[#104932356] https://www.pivotaltracker.com/story/show/104932356

Signed-off-by: Jonathan Lo <jlo@us.ibm.com>
  • Loading branch information
mbhave committed Oct 8, 2015
1 parent 7ba367e commit 7b8f17f
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 14 deletions.
Expand Up @@ -23,6 +23,9 @@
public class ExternalIdentityProviderDefinition extends AbstractIdentityProviderDefinition {
public static final String GROUP_ATTRIBUTE_NAME = "external_groups"; //can be a string or a list of strings
public static final String EMAIL_ATTRIBUTE_NAME = "email"; //can be a string
public static final String GIVEN_NAME_ATTRIBUTE_NAME = "given_name"; //can be a string
public static final String FAMILY_NAME_ATTRIBUTE_NAME = "family_name"; //can be a string
public static final String PHONE_NUMBER_ATTRIBUTE_NAME = "phone_number"; //can be a string

public static final String EXTERNAL_GROUPS_WHITELIST = "externalGroupsWhitelist";
public static final String ATTRIBUTE_MAPPINGS = "attributeMappings";
Expand Down
Expand Up @@ -54,12 +54,17 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static org.cloudfoundry.identity.uaa.ExternalIdentityProviderDefinition.EMAIL_ATTRIBUTE_NAME;
import static org.cloudfoundry.identity.uaa.ExternalIdentityProviderDefinition.FAMILY_NAME_ATTRIBUTE_NAME;
import static org.cloudfoundry.identity.uaa.ExternalIdentityProviderDefinition.GIVEN_NAME_ATTRIBUTE_NAME;
import static org.cloudfoundry.identity.uaa.ExternalIdentityProviderDefinition.GROUP_ATTRIBUTE_NAME;

public class LoginSamlAuthenticationProvider extends SAMLAuthenticationProvider implements ApplicationEventPublisherAware {
Expand Down Expand Up @@ -121,7 +126,8 @@ public Authentication authenticate(Authentication authentication) throws Authent
Collection<? extends GrantedAuthority> authorities = mapAuthorities(idp.getOriginKey(), samlAuthorities);

Set<String> filteredExternalGroups = filterSamlAuthorities(samlConfig, samlAuthorities);
UaaUser user = createIfMissing(samlPrincipal, addNew, authorities);
Map<String, String> userAttributes = retrieveUserAttributes(samlConfig, (SAMLCredential) result.getCredentials());
UaaUser user = createIfMissing(samlPrincipal, addNew, authorities, userAttributes);
UaaPrincipal principal = new UaaPrincipal(user);
return new LoginSamlAuthenticationToken(principal, result).getUaaAuthentication(user.getAuthorities(), filteredExternalGroups);
}
Expand Down Expand Up @@ -182,7 +188,21 @@ public Collection<? extends GrantedAuthority> retrieveSamlAuthorities(SamlIdenti
return authorities == null ? Collections.EMPTY_LIST : authorities;
}

protected UaaUser createIfMissing(UaaPrincipal samlPrincipal, boolean addNew, Collection<? extends GrantedAuthority> authorities) {
public Map<String, String> retrieveUserAttributes(SamlIdentityProviderDefinition definition, SAMLCredential credential) {
Map<String, String> userAttributes = new HashMap<>();
if (definition != null && definition.getAttributeMappings() != null) {
for (Map.Entry<String, Object> attributeMapping : definition.getAttributeMappings().entrySet()) {
if (attributeMapping.getValue() instanceof String) {
if (credential.getAttribute((String)attributeMapping.getValue()) != null) {
userAttributes.put(attributeMapping.getKey(), ((XSString) credential.getAttribute((String) attributeMapping.getValue()).getAttributeValues().get(0)).getValue());
}
}
}
}
return userAttributes;
}

protected UaaUser createIfMissing(UaaPrincipal samlPrincipal, boolean addNew, Collection<? extends GrantedAuthority> authorities, Map<String,String> userAttributes) {
boolean userModified = false;
UaaPrincipal uaaPrincipal = samlPrincipal;
UaaUser user;
Expand All @@ -194,7 +214,7 @@ protected UaaUser createIfMissing(UaaPrincipal samlPrincipal, boolean addNew, Co
+ "You can correct this by creating a shadow user for the SAML user.", e);
}
// Register new users automatically
publish(new NewUserAuthenticatedEvent(getUser(uaaPrincipal)));
publish(new NewUserAuthenticatedEvent(getUser(uaaPrincipal, userAttributes)));
try {
user = userDatabase.retrieveUserByName(uaaPrincipal.getName(), uaaPrincipal.getOrigin());
} catch (UsernameNotFoundException ex) {
Expand All @@ -216,9 +236,11 @@ protected UaaUser createIfMissing(UaaPrincipal samlPrincipal, boolean addNew, Co
return user;
}

protected UaaUser getUser(UaaPrincipal principal) {
protected UaaUser getUser(UaaPrincipal principal, Map<String,String> userAttributes) {
String name = principal.getName();
String email = null;
String email = userAttributes.get(EMAIL_ATTRIBUTE_NAME);
String givenName = userAttributes.get(GIVEN_NAME_ATTRIBUTE_NAME);
String familyName = userAttributes.get(FAMILY_NAME_ATTRIBUTE_NAME);
String userId = Origin.NotANumber;
String origin = principal.getOrigin()!=null?principal.getOrigin():Origin.LOGIN_SERVER;
String zoneId = principal.getZoneId();
Expand All @@ -243,11 +265,9 @@ protected UaaUser getUser(UaaPrincipal principal) {
email = name + "@unknown.org";
}
}
String givenName = null;
if (givenName == null) {
givenName = email.split("@")[0];
}
String familyName = null;
if (familyName == null) {
familyName = email.split("@")[1];
}
Expand Down
Expand Up @@ -30,6 +30,7 @@
import org.cloudfoundry.identity.uaa.test.JdbcTestBase;
import org.cloudfoundry.identity.uaa.user.JdbcUaaUserDatabase;
import org.cloudfoundry.identity.uaa.user.UaaAuthority;
import org.cloudfoundry.identity.uaa.user.UaaUser;
import org.cloudfoundry.identity.uaa.util.JsonUtils;
import org.cloudfoundry.identity.uaa.zone.IdentityProvider;
import org.cloudfoundry.identity.uaa.zone.IdentityProviderProvisioning;
Expand Down Expand Up @@ -62,13 +63,15 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class LoginSamlAuthenticationProviderTests extends JdbcTestBase {
Expand All @@ -90,22 +93,28 @@ public class LoginSamlAuthenticationProviderTests extends JdbcTestBase {
SamlIdentityProviderDefinition providerDefinition = new SamlIdentityProviderDefinition();
private IdentityProvider provider;

public List<Attribute> getAttributes(Map<String,List<String>> values) {
public List<Attribute> getAttributes(Map<String,Object> values) {
List<Attribute> result = new LinkedList<>();
for (Map.Entry<String,List<String>> entry : values.entrySet()) {
for (Map.Entry<String,Object> entry : values.entrySet()) {
result.addAll(getAttributes(entry.getKey(), entry.getValue()));
}
return result;
}
public List<Attribute> getAttributes(final String name, List<String> values) {
public List<Attribute> getAttributes(final String name, Object value) {
Attribute attribute = mock(Attribute.class);
when(attribute.getName()).thenReturn(name);
when(attribute.getFriendlyName()).thenReturn(name);

List<XMLObject> xmlObjects = new LinkedList<>();
for (String s : values) {
AttributedStringImpl impl = new AttributedStringImpl("","","");
impl.setValue(s);
if (value instanceof List) {
for (String s : (List<String>)value) {
AttributedStringImpl impl = new AttributedStringImpl("", "", "");
impl.setValue(s);
xmlObjects.add(impl);
}
} else {
AttributedStringImpl impl = new AttributedStringImpl("", "", "");
impl.setValue((String)value);
xmlObjects.add(impl);
}
when(attribute.getAttributeValues()).thenReturn(xmlObjects);
Expand Down Expand Up @@ -136,7 +145,11 @@ public void configureProvider() throws Exception {
NameID usernameID = mock(NameID.class);
when(usernameID.getValue()).thenReturn(username);
consumer = mock(WebSSOProfileConsumer.class);
Map<String, List<String>> attributes = new HashMap<>();
Map<String, Object> attributes = new HashMap<>();
attributes.put("firstName", "Marissa");
attributes.put("lastName", "Bloggs");
attributes.put("emailAddress", "marissa.bloggs@test.com");
attributes.put("phone", "1234567890");
attributes.put("groups", Arrays.asList(SAML_USER,SAML_ADMIN,SAML_NOT_MAPPED));
attributes.put("2ndgroups", Arrays.asList(SAML_TEST));
credential = new SAMLCredential(
Expand Down Expand Up @@ -255,6 +268,41 @@ public void add_external_groups_to_authentication_with_whitelist() throws Except
assertEquals(Collections.singleton(SAML_ADMIN), authentication.getExternalGroups());
}

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

getAuthentication();
UaaUser user = userDatabase.retrieveUserByName("marissa-saml", Origin.SAML);
assertEquals("Marissa", user.getGivenName());
assertEquals("Bloggs", user.getFamilyName());
assertEquals("marissa.bloggs@test.com", user.getEmail());
// assertEquals("1234567890", user.get());
}

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

getAuthentication();
UaaUser user = userDatabase.retrieveUserByName("marissa-saml", Origin.SAML);
assertEquals("marissa.bloggs", user.getGivenName());
assertEquals("test.com", user.getFamilyName());
assertEquals("marissa.bloggs@test.com", user.getEmail());
}

protected UaaAuthentication getAuthentication() {
Authentication authentication = authprovider.authenticate(mockSamlAuthentication(Origin.SAML));
assertNotNull("Authentication should exist", authentication);
Expand Down

0 comments on commit 7b8f17f

Please sign in to comment.