Skip to content

Commit

Permalink
Merge branch 'feature/ldap_external_groups' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
fhanik committed Oct 27, 2015
2 parents fcc19c7 + 99007b4 commit 6dcf1a2
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
import org.springframework.util.MultiValueMap;

import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;

public class ExternalLoginAuthenticationManager implements AuthenticationManager, ApplicationEventPublisherAware, BeanNameAware {

Expand Down Expand Up @@ -121,7 +124,9 @@ public Authentication authenticate(Authentication request) throws Authentication
}
UaaAuthentication success = new UaaAuthentication(new UaaPrincipal(user), user.getAuthorities(), uaaAuthenticationDetails);
if (request.getPrincipal() instanceof UserDetails) {
success.setUserAttributes(getUserAttributes((UserDetails) request.getPrincipal()));
UserDetails userDetails = (UserDetails) request.getPrincipal();
success.setUserAttributes(getUserAttributes(userDetails));
success.setExternalGroups(new HashSet<>(getExternalUserAuthorities(userDetails)));
}
publish(new UserAuthenticationSuccessEvent(user, success));
return success;
Expand All @@ -131,6 +136,10 @@ protected MultiValueMap<String, String> getUserAttributes(UserDetails request) {
return new LinkedMultiValueMap<>();
}

protected List<String> getExternalUserAuthorities(UserDetails request) {
return new LinkedList<>();
}

protected void publish(ApplicationEvent event) {
if (eventPublisher != null) {
eventPublisher.publishEvent(event);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,29 @@
import org.apache.commons.lang.StringUtils;
import org.cloudfoundry.identity.uaa.ldap.ExtendedLdapUserDetails;
import org.cloudfoundry.identity.uaa.ldap.LdapIdentityProviderDefinition;
import org.cloudfoundry.identity.uaa.ldap.extension.LdapAuthority;
import org.cloudfoundry.identity.uaa.user.UaaUser;
import org.cloudfoundry.identity.uaa.zone.IdentityProvider;
import org.cloudfoundry.identity.uaa.zone.IdentityProviderProvisioning;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.MultiValueMap;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static java.util.Collections.EMPTY_LIST;

public class LdapLoginAuthenticationManager extends ExternalLoginAuthenticationManager {

public static final String USER_ATTRIBUTE_PREFIX = "user.attribute.";
private boolean autoAddAuthorities = false;
private IdentityProviderProvisioning provisioning;

public void setProvisioning(IdentityProviderProvisioning provisioning) {
Expand Down Expand Up @@ -62,6 +70,34 @@ protected MultiValueMap<String, String> getUserAttributes(UserDetails request) {
return result;
}

@Override
protected List<String> getExternalUserAuthorities(UserDetails request) {
List<String> result = super.getExternalUserAuthorities(request);
if (provisioning!=null) {
IdentityProvider provider = provisioning.retrieveByOrigin(getOrigin(), IdentityZoneHolder.get().getId());
LdapIdentityProviderDefinition ldapIdentityProviderDefinition = provider.getConfigValue(LdapIdentityProviderDefinition.class);
List<String> externalWhiteList = ldapIdentityProviderDefinition.getExternalGroupsWhitelist();
result = new LinkedList<>(getAuthoritesAsNames(request.getAuthorities()));
result.retainAll(externalWhiteList);
}
return result;
}

protected Set<String> getAuthoritesAsNames(Collection<? extends GrantedAuthority> authorities) {
Set<String> result = new HashSet<>();
authorities = new LinkedList(authorities!=null?authorities: EMPTY_LIST);
for (GrantedAuthority a : authorities) {
if (a instanceof LdapAuthority) {
LdapAuthority la = (LdapAuthority)a;
String[] groupNames = la.getAttributeValues("cn");
if (groupNames!=null) {
result.addAll(Arrays.asList(groupNames));
}
}
}
return result;
}

@Override
protected UaaUser userAuthenticated(Authentication request, UaaUser user) {
boolean userModified = false;
Expand All @@ -78,12 +114,16 @@ protected UaaUser userAuthenticated(Authentication request, UaaUser user) {
return getUserDatabase().retrieveUserById(user.getId());
}

public boolean isAutoAddAuthorities() {
return autoAddAuthorities;
}

public void setAutoAddAuthorities(boolean autoAddAuthorities) {
this.autoAddAuthorities = autoAddAuthorities;
protected boolean isAutoAddAuthorities() {
Boolean result = true;
if (provisioning!=null) {
IdentityProvider provider = provisioning.retrieveByOrigin(getOrigin(), IdentityZoneHolder.get().getId());
LdapIdentityProviderDefinition ldapIdentityProviderDefinition = provider.getConfigValue(LdapIdentityProviderDefinition.class);
if (ldapIdentityProviderDefinition!=null) {
result = ldapIdentityProviderDefinition.isAutoAddGroups();
}
}
return result!=null ? result.booleanValue() : true;
}

private boolean haveUserAttributesChanged(UaaUser existingUser, UaaUser user) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,15 @@ public void testGetUserWithNonLdapInfo() throws Exception {

@Test
public void testUserAuthenticated() throws Exception {


UaaUser user = getUaaUser();
am.setAutoAddAuthorities(true);
definition.setAutoAddGroups(true);
UaaUser result = am.userAuthenticated(auth, user);
assertSame(dbUser, result);
verify(publisher, times(1)).publishEvent(Matchers.<ApplicationEvent>anyObject());

am.setAutoAddAuthorities(false);
definition.setAutoAddGroups(false);
result = am.userAuthenticated(auth, user);
assertSame(dbUser, result);
verify(publisher, times(2)).publishEvent(Matchers.<ApplicationEvent>anyObject());
Expand Down
8 changes: 8 additions & 0 deletions uaa/src/main/resources/ldap_init.ldif
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,14 @@ cn: uaa.admin
member: cn=admin,ou=Users,dc=test,dc=com
member: cn=marissa3,ou=Users,dc=test,dc=com

dn: cn=thirdmarissa,ou=scopes,dc=test,dc=com
changetype: add
objectClass: groupOfNames
objectClass: top
cn: thirdmarissa
description: thirdmarissa
member: cn=marissa3,ou=Users,dc=test,dc=com

dn: cn=developers,ou=scopes,dc=test,dc=com
changetype: add
objectClass: groupOfNames
Expand Down
5 changes: 0 additions & 5 deletions uaa/src/main/webapp/WEB-INF/spring-servlet.xml
Original file line number Diff line number Diff line change
Expand Up @@ -298,14 +298,9 @@
<property name="samlProviders" ref="metaDataProviders"/>
</bean>

<bean id="autoAddAuthorities" class="java.lang.Boolean">
<constructor-arg value="${ldap.groups.autoAdd:true}"/>
</bean>

<bean id="ldapLoginAuthenticationMgr" class="org.cloudfoundry.identity.uaa.authentication.manager.LdapLoginAuthenticationManager">
<property name="userDatabase" ref="userDatabase" />
<property name="origin" value="ldap"/>
<property name="autoAddAuthorities" ref="autoAddAuthorities"/>
<property name="provisioning" ref="identityProviderProvisioning"/>
</bean>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ public void test_LDAP_Custom_User_Attributes_In_ID_Token() throws Exception {
true);
ldapIdentityProviderDefinition.addAttributeMapping(USER_ATTRIBUTE_PREFIX+COST_CENTERS, COST_CENTER);
ldapIdentityProviderDefinition.addAttributeMapping(USER_ATTRIBUTE_PREFIX+MANAGERS, MANAGER);
ldapIdentityProviderDefinition.addWhiteListedGroup("marissaniner");
ldapIdentityProviderDefinition.addWhiteListedGroup("marissaniner2");


IdentityProvider provider = new IdentityProvider();
provider.setIdentityZoneId(zoneId);
Expand All @@ -130,7 +133,7 @@ public void test_LDAP_Custom_User_Attributes_In_ID_Token() throws Exception {
List<String> idps = Arrays.asList(provider.getOriginKey());

String adminClientInZone = new RandomValueStringGenerator().generate();
BaseClientDetails clientDetails = new BaseClientDetails(adminClientInZone, null, "openid,user_attributes", "password,authorization_code,client_credentials", "uaa.admin,scim.read,scim.write,uaa.resource", zoneUrl);
BaseClientDetails clientDetails = new BaseClientDetails(adminClientInZone, null, "openid,user_attributes,roles", "password,authorization_code,client_credentials", "uaa.admin,scim.read,scim.write,uaa.resource", zoneUrl);
clientDetails.setClientSecret("secret");
clientDetails.addAdditionalInformation(ClientConstants.AUTO_APPROVE, true);
clientDetails.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, idps);
Expand All @@ -145,7 +148,7 @@ public void test_LDAP_Custom_User_Attributes_In_ID_Token() throws Exception {
clientDetails.getClientSecret(),
"marissa9",
"ldap9",
"openid user_attributes")
"openid user_attributes roles")
.get("id_token");

assertNotNull(idToken);
Expand All @@ -158,6 +161,11 @@ public void test_LDAP_Custom_User_Attributes_In_ID_Token() throws Exception {
assertThat(userAttributes.get(COST_CENTERS), containsInAnyOrder(DENVER_CO));
assertThat(userAttributes.get(MANAGERS), containsInAnyOrder(JOHN_THE_SLOTH, KARI_THE_ANT_EATER));


assertNotNull(claims.get(Claims.ROLES));
List<String> roles = (List<String>) claims.get(Claims.ROLES);
assertThat(roles, containsInAnyOrder("marissaniner", "marissaniner2"));

//no user_attribute scope provided
idToken =
(String) IntegrationTestUtils.getPasswordToken(zoneUrl,
Expand All @@ -173,6 +181,7 @@ public void test_LDAP_Custom_User_Attributes_In_ID_Token() throws Exception {
idTokenClaims = JwtHelper.decode(idToken);
claims = JsonUtils.readValue(idTokenClaims.getClaims(), new TypeReference<Map<String, Object>>() {});
assertNull(claims.get(Claims.USER_ATTRIBUTES));
assertNull(claims.get(Claims.ROLES));
}

protected boolean doesSupportZoneDNS_and_isLdapEnabled() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
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.junit.Assert.fail;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
Expand Down Expand Up @@ -220,9 +221,48 @@ private void deleteLdapUsers() {
jdbcTemplate.update("delete from users where origin='" + Origin.LDAP + "'");
}

@Test
public void test_whitelisted_external_groups() throws Exception {
Assume.assumeThat("ldap-groups-map-to-scopes.xml, ldap-groups-as-scopes.xml", StringContains.containsString(ldapGroup));
setUp();
IdentityProviderProvisioning idpProvisioning = mainContext.getBean(IdentityProviderProvisioning.class);
IdentityProvider idp = idpProvisioning.retrieveByOrigin(Origin.LDAP, IdentityZone.getUaa().getId());
LdapIdentityProviderDefinition def = idp.getConfigValue(LdapIdentityProviderDefinition.class);
def.addWhiteListedGroup("admins");
def.addWhiteListedGroup("thirdmarissa");
idp.setConfig(JsonUtils.writeValueAsString(def));
idpProvisioning.update(idp);
AuthenticationManager manager = mainContext.getBean(DynamicZoneAwareAuthenticationManager.class);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("marissa3", "ldap3");
Authentication auth = manager.authenticate(token);
assertNotNull(auth);
assertTrue(auth instanceof UaaAuthentication);
UaaAuthentication uaaAuth = (UaaAuthentication) auth;
Set<String> externalGroups = uaaAuth.getExternalGroups();
assertNotNull(externalGroups);
assertEquals(2, externalGroups.size());
assertThat(externalGroups, containsInAnyOrder("admins", "thirdmarissa"));
}

@Test
public void test_external_groups_with_default_whitelist() throws Exception {
Assume.assumeThat("ldap-groups-map-to-scopes.xml, ldap-groups-as-scopes.xml", StringContains.containsString(ldapGroup));
setUp();
AuthenticationManager manager = mainContext.getBean(DynamicZoneAwareAuthenticationManager.class);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("marissa3", "ldap3");
Authentication auth = manager.authenticate(token);
assertNotNull(auth);
assertTrue(auth instanceof UaaAuthentication);
UaaAuthentication uaaAuth = (UaaAuthentication) auth;
Set<String> externalGroups = uaaAuth.getExternalGroups();
assertNotNull(externalGroups);
assertEquals(0, externalGroups.size());
}


@Test
public void testCustomUserAttributes() throws Exception {
Assume.assumeThat("ldap-groups-null.xml", StringContains.containsString(ldapGroup));
Assume.assumeThat("ldap-groups-map-to-scopes.xml, ldap-groups-as-scopes.xml", StringContains.containsString(ldapGroup));

final String MANAGER = "uaaManager";
final String MANAGERS = "managers";
Expand Down Expand Up @@ -957,7 +997,8 @@ public void testLdapScopes() throws Exception {
assertNotNull(auth);
String[] list = new String[]{
"uaa.admin",
"cloud_controller.read"
"cloud_controller.read",
"thirdmarissa"
};
assertThat(list, arrayContainingInAnyOrder(getAuthorities(auth.getAuthorities())));
}
Expand All @@ -984,7 +1025,8 @@ public void testLdapScopesFromChainedAuth() throws Exception {
"oauth.approvals",
"uaa.user",
"cloud_controller.read",
"user_attributes"
"user_attributes",
"thirdmarissa"
};
assertThat(list, arrayContainingInAnyOrder(getAuthorities(auth.getAuthorities())));
}
Expand Down

0 comments on commit 6dcf1a2

Please sign in to comment.