Index: security/src/main/org/jboss/security/auth/spi/LdapExtLoginModule.java =================================================================== --- security/src/main/org/jboss/security/auth/spi/LdapExtLoginModule.java (revision 97240) +++ security/src/main/org/jboss/security/auth/spi/LdapExtLoginModule.java (working copy) @@ -131,6 +131,19 @@ anonymous login by some ldap servers and this may not be a desirable feature. Set this to false to reject empty passwords, true to have the ldap server validate the empty password. The default is true. + * __authorizeOnly__ : Bind only as a well known user and don't check the + username and password (just authorize not authenticate) for use with stacking + * __principalIsDN__ : The (usually from a stack) principal is actually an + LDAP DN rather than straight "username" like "jsmith" + * __principalIsDN__ : The (usually from a stack) principal is actually an + LDAP DN rather than straight "username" like "jsmith" + * __authorizeOnly__ : Used with stacking, DONT validate credentials and use + a common or anonymous connection to find the ROLES, requires principalIsDN + * __removePrincipalElements__ : if the principal is a DN (above) but you have + superfluous elements not in LDAP (such as Microsoft Certificate Server + includes EMAILADDRESS) that you want to remove (to authenticate against say + Active Directory) then list them here as a comma delemeted list i.e + "phone,EMAILADDRESS,zip" @author Andy Oliver @author Scott.Stark@jboss.org @@ -153,6 +166,9 @@ private static final String SEARCH_TIME_LIMIT_OPT = "searchTimeLimit"; private static final String SEARCH_SCOPE_OPT = "searchScope"; private static final String SECURITY_DOMAIN_OPT = "jaasSecurityDomain"; + private static final String ROLES_ONLY = "authorizeOnly"; + private static final String PRINCIPAL_IS_DN = "principalIsDN"; + private static final String REMOVE_PRINCIPAL_ELEMENTS = "removePrincipalElements"; protected String bindDN; protected String bindCredential; @@ -162,7 +178,10 @@ protected String roleFilter; protected String roleAttributeID; protected String roleNameAttributeID; + protected String removeElements; protected boolean roleAttributeIsDN; + protected boolean rolesOnly; + protected boolean principalIsDN; protected boolean parseRoleNameFromDN; protected int recursion = 0; protected int searchTimeLimit = 10000; @@ -207,6 +226,9 @@ */ protected boolean validatePassword(String inputPassword, String expectedPassword) { + removeElements = (String)options.get(REMOVE_PRINCIPAL_ELEMENTS); + boolean doRemoveElements = removeElements != null + && !removeElements.equals(""); boolean isValid = false; if (inputPassword != null) { @@ -229,6 +251,9 @@ { // Validate the password by trying to create an initial context String username = getUsername(); + username = doRemoveElements ? removeElements(removeElements, + username) : + username; isValid = createLdapInitContext(username, inputPassword); defaultRole(); isValid = true; @@ -274,6 +299,13 @@ private boolean createLdapInitContext(String username, Object credential) throws Exception { + rolesOnly = options.get(ROLES_ONLY) != null && + options.get(ROLES_ONLY).toString().trim().equals("true"); + principalIsDN = options.get(PRINCIPAL_IS_DN) != null && + options.get(PRINCIPAL_IS_DN).toString().trim().equals("true"); + removeElements = (String)options.get(REMOVE_PRINCIPAL_ELEMENTS); + boolean doRemoveElements = removeElements != null + && !removeElements.equals(""); bindDN = (String) options.get(BIND_DN); bindCredential = (String) options.get(BIND_CREDENTIAL); String securityDomain = (String) options.get(SECURITY_DOMAIN_OPT); @@ -340,15 +372,16 @@ try { ctx = constructInitialLdapContext(bindDN, bindCredential); + String parsedUser = doRemoveElements ? removeElements(removeElements,username) : username; // Validate the user by binding against the userDN - String userDN = bindDNAuthentication(ctx, username, credential, baseDN, baseFilter); + String userDN = rolesOnly && principalIsDN ? parsedUser : bindDNAuthentication(ctx, parsedUser, 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); + rolesSearch(ctx, constraints, parsedUser, userDN, recursion, 0); } finally { @@ -607,4 +640,41 @@ } } } + + /** assuming the principal is a DN then parse off the ignorable parts + * like EMAILADDRESS so we have pure LDAP DN + */ + private String removeElements(String elementList, String user) + { + String newUser = ""; + String[] userParts = user.split("\\,"); + String[] ignored = elementList.split("\\,"); + for(int i = 0; i < userParts.length; i++) + { + String part = userParts[i]; + if(!checkIgnoreMatches(part,ignored)) + { + newUser += part; + newUser = i == userParts.length-1 ? newUser : newUser +","; + } + } + return newUser.endsWith(",") ? newUser.substring(0,newUser.length()-1) : + newUser ; //if a final element was ignored we have to parse off an extra comma + } + + /** return if the string begins with any of the elements in the array + * @return boolean + */ + public boolean checkIgnoreMatches( String part, String[] ignored ) + { + for( String ignore : ignored) + { + if(part.startsWith(ignore)) + { + return true; + } + } + return false; + } + }