Index: security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/spi/LdapExtLoginModule.java =================================================================== --- security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/spi/LdapExtLoginModule.java (revision 363) +++ security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/spi/LdapExtLoginModule.java (working copy) @@ -23,9 +23,11 @@ import java.security.Principal; import java.security.acl.Group; +import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Properties; +import java.util.Set; import java.util.StringTokenizer; import java.util.Map.Entry; @@ -118,13 +120,17 @@ directory schemas (e.g., Microsoft Active Directory), role (group)attributes in the user object are stored as DNs to role objects instead of as simple names, in which case, this property should be set to true. The default value of this - property is false. + property is false. If this options is set to true, then the __roleFilter__ and + __rolesCtxDN__ options are ignored, the role attribute search is done using the DN + instead. * __roleNameAttributeID__ : The name of the attribute of the role object which corresponds to the name of the role. If the __roleAttributeIsDN__ property is set to true, this property is used to find the role object's name attribute. If the __roleAttributeIsDN__ property is set to false, this property is ignored. * __roleRecursion__ : How deep the role search will go below a given matching - context. Disable with 0, which is the default. + context. Disable with 0, which is the default. If the __roleAttributeIsDN__ option + is set, then setting __roleRecursion__ to -1 disables the recursion depth checking + (infinite recursion, but each DN will be processed only once). * __searchTimeLimit__ : The timeout in milliseconds for the user/role searches. Defaults to 10000 (10 seconds). * __searchScope__ : Sets the search scope to one of the strings. The default is @@ -142,8 +148,8 @@ @author Andy Oliver @author Scott.Stark@jboss.org + @author Radics Péter @version $Revision$ */ -@SuppressWarnings("rawtypes") public class LdapExtLoginModule extends UsernamePasswordLoginModule { // see AbstractServerLoginModule @@ -226,6 +232,8 @@ protected boolean roleAttributeIsDN; protected boolean parseRoleNameFromDN; + + protected String[] attributeIDs; protected int recursion = 0; @@ -250,8 +258,8 @@ private transient SimpleGroup userRoles = new SimpleGroup("Roles"); - @SuppressWarnings("unchecked") - public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) + @Override + public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { addValidOptions(ALL_VALID_OPTIONS); super.initialize(subject, callbackHandler, sharedState, options); @@ -262,6 +270,7 @@ user's password. We also override the validatePassword so this is ok. @return and empty password String */ + @Override protected String getUsersPassword() throws LoginException { return ""; @@ -275,6 +284,7 @@ rather than the security domain identity. @return Group[] containing the sets of roles */ + @Override protected Group[] getRoleSets() throws LoginException { // SECURITY-225: check if authentication was already done in a previous login module @@ -306,6 +316,7 @@ @param inputPassword the password to validate. @param expectedPassword ignored */ + @Override protected boolean validatePassword(String inputPassword, String expectedPassword) { isPasswordValidated = true; @@ -348,8 +359,8 @@ */ private void defaultRole() { - String defaultRole = (String) options.get(DEFAULT_ROLE); - try + String defaultRole = (String) options.get(DEFAULT_ROLE); + try { if (defaultRole == null || defaultRole.equals("")) { @@ -408,6 +419,10 @@ //JBAS-4619:Parse Role Name from DN String parseRoleNameFromDNOption = (String) options.get(PARSE_ROLE_NAME_FROM_DN_OPT); parseRoleNameFromDN = Boolean.valueOf(parseRoleNameFromDNOption).booleanValue(); + if (parseRoleNameFromDN) + attributeIDs = new String[] { roleAttributeID }; + else + attributeIDs = new String[] { roleNameAttributeID, roleAttributeID }; rolesCtxDN = (String) options.get(ROLES_CTX_DN_OPT); String strRecursion = (String) options.get(ROLE_RECURSION); @@ -456,12 +471,20 @@ // Validate the user by binding against the userDN String userDN = bindDNAuthentication(ctx, username, credential, baseDN, baseFilter); - // Query for roles matching the role filter - SearchControls constraints = new SearchControls(); - constraints.setSearchScope(searchScope); - constraints.setReturningAttributes(new String[0]); - constraints.setTimeLimit(searchTimeLimit); - rolesSearch(ctx, constraints, username, userDN, recursion, 0); + if (roleAttributeIsDN) + { + // Query for roles using the roleAttributeID as DN + rolesSearchByDN(ctx, userDN); + } + else + { + // Query for roles matching the role filter + SearchControls constraints = new SearchControls(); + constraints.setSearchScope(searchScope); + constraints.setReturningAttributes(new String[0]); + constraints.setTimeLimit(searchTimeLimit); + rolesSearch(ctx, constraints, username, userDN, recursion, 0); + } } catch(Exception e) { @@ -495,7 +518,7 @@ String attrList[] = {distinguishedNameAttribute}; constraints.setReturningAttributes(attrList); - NamingEnumeration results = null; + NamingEnumeration results = null; Object[] filterArgs = {user}; results = ctx.search(baseDN, filter, filterArgs, constraints); @@ -505,7 +528,7 @@ throw PicketBoxMessages.MESSAGES.failedToFindBaseContextDN(baseDN); } - SearchResult sr = (SearchResult) results.next(); + SearchResult sr = results.next(); String name = sr.getName(); String userDN = null; Attributes attrs = sr.getAttributes(); @@ -551,12 +574,12 @@ int recursionMax, int nesting) throws NamingException { Object[] filterArgs = {user, userDN}; - NamingEnumeration results = ctx.search(rolesCtxDN, roleFilter, filterArgs, constraints); + NamingEnumeration results = ctx.search(rolesCtxDN, roleFilter, filterArgs, constraints); try { while (results.hasMore()) { - SearchResult sr = (SearchResult) results.next(); + SearchResult sr = results.next(); String dn = canonicalize(sr.getName()); if (nesting == 0 && roleAttributeIsDN && roleNameAttributeID != null) { @@ -636,16 +659,72 @@ if (results != null) results.close(); } - } + + /** + @param ctx + @param userDN + @throws NamingException + */ + protected void rolesSearchByDN(InitialLdapContext ctx, String userDN) throws NamingException + { + Set processedDNs = new HashSet(); + Attributes attributes = ctx.getAttributes(userDN, new String[]{ roleAttributeID } ); + processRoleAttribute(ctx, attributes.get(roleAttributeID), processedDNs, 0); + } + + private void processRoleAttribute(InitialLdapContext ctx, Attribute roleAttribute, Set processedDNs, int depth) throws NamingException + { + if (roleAttribute == null) + return; + int numRoleAttributes = roleAttribute.size(); + for (int i = 0; i < numRoleAttributes; ++i) + { + String roleDN = (String)roleAttribute.get(i); + rolesSearchByDN(ctx, roleDN, processedDNs, depth); + } + } + + private void rolesSearchByDN(InitialLdapContext ctx, String roleDN, Set processedDNs, int depth) { + if (!processedDNs.add(roleDN)) + return; + try + { + Attributes attributes = ctx.getAttributes(roleDN, attributeIDs); + if (parseRoleNameFromDN) + parseRole(roleDN); + else + { + Attribute roleNameAttribute = attributes.get(roleNameAttributeID); + if (roleNameAttribute != null) + { + int numRoleNames = roleNameAttribute.size(); + for (int i = 0; i < numRoleNames; ++i) + { + String roleName = (String)roleNameAttribute.get(i); + addRole(roleName); + } + } + } + if (recursion < 0 || depth < recursion) + { + processRoleAttribute(ctx, attributes.get(roleAttributeID), processedDNs, depth + 1); + } + } + catch (Throwable e) + { + PicketBoxLogger.LOGGER.debugFailureToQueryLDAPAttribute(roleNameAttributeID, roleDN, e); + } + + } private InitialLdapContext constructInitialLdapContext(String dn, Object credential) throws NamingException { Properties env = new Properties(); - Iterator iter = options.entrySet().iterator(); + Iterator iter = options.entrySet().iterator(); while (iter.hasNext()) { - Entry entry = (Entry) iter.next(); + Entry entry = (Entry) iter.next(); env.put(entry.getKey(), entry.getValue()); } @@ -715,11 +794,11 @@ while(st != null && st.hasMoreTokens()) { String keyVal = st.nextToken(); - if(keyVal.indexOf(roleNameAttributeID) > -1) + StringTokenizer kst = new StringTokenizer(keyVal, "="); + String key = kst.nextToken(); + if (roleNameAttributeID.equalsIgnoreCase(key)) { - StringTokenizer kst = new StringTokenizer(keyVal,"="); - kst.nextToken(); - addRole(kst.nextToken()); + addRole(kst.nextToken()); } } }