We logon users to Active Directory via LDAP using the Java LDAP API. We want to enhance our logon functionality to further check if the user is in a given AD group. Does anyone know how to do this?
Current code:
import javax.naming.*; import javax.naming.ldap.*; LdapContext ctx = null; Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.SECURITY_AUTHENTICATION,"simple"); env.put(Context.PROVIDER_URL, Config.get("ldap-url")); try { Control[] connCtls = new Control[] {new FastBindConnectionControl()}; ctx = new InitialLdapContext(env, connCtls); ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, "DOMAIN\\" + username); ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, password); ctx.reconnect(connCtls); /* TODO: Only return true if user is in group "ABC" */ return true; //User authenticated } catch (Exception e) { return false; //User could NOT be authenticated } finally { ... }
Update: See the solution below.
We solved this with the class below. Just call the authenticate method:
import java.text.MessageFormat; import java.util.*; import javax.naming.*; import org.apache.log4j.Level; public class LdapGroupAuthenticator { public static final String DISTINGUISHED_NAME = "distinguishedName"; public static final String CN = "cn"; public static final String MEMBER = "member"; public static final String MEMBER_OF = "memberOf"; public static final String SEARCH_BY_SAM_ACCOUNT_NAME = "(SAMAccountName={0})"; public static final String SEARCH_GROUP_BY_GROUP_CN = "(&(objectCategory=group)(cn={0}))"; /* * Prepares and returns CN that can be used for AD query * e.g. Converts "CN=**Dev - Test Group" to "**Dev - Test Group" * Converts CN=**Dev - Test Group,OU=Distribution Lists,DC=DOMAIN,DC=com to "**Dev - Test Group" */ public static String getCN(String cnName) { if (cnName != null && cnName.toUpperCase().startsWith("CN=")) { cnName = cnName.substring(3); } int position = cnName.indexOf(','); if (position == -1) { return cnName; } else { return cnName.substring(0, position); } } public static boolean isSame(String target, String candidate) { if (target != null && target.equalsIgnoreCase(candidate)) { return true; } return false; } public static boolean authenticate(String domain, String username, String password) { Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.PROVIDER_URL, "ldap://1.2.3.4:389"); env.put(Context.SECURITY_AUTHENTICATION, "simple"); env.put(Context.SECURITY_PRINCIPAL, domain + "\\" + username); env.put(Context.SECURITY_CREDENTIALS, password); DirContext ctx = null; String defaultSearchBase = "DC=DOMAIN,DC=com"; String groupDistinguishedName = "DN=CN=DLS-APP-MyAdmin-C,OU=DLS File Permissions,DC=DOMAIN,DC=com"; try { ctx = new InitialDirContext(env); // userName is SAMAccountName SearchResult sr = executeSearchSingleResult(ctx, SearchControls.SUBTREE_SCOPE, defaultSearchBase, MessageFormat.format( SEARCH_BY_SAM_ACCOUNT_NAME, new Object[] {username}), new String[] {DISTINGUISHED_NAME, CN, MEMBER_OF} ); String groupCN = getCN(groupDistinguishedName); HashMap processedUserGroups = new HashMap(); HashMap unProcessedUserGroups = new HashMap(); // Look for and process memberOf Attribute memberOf = sr.getAttributes().get(MEMBER_OF); if (memberOf != null) { for ( Enumeration e1 = memberOf.getAll() ; e1.hasMoreElements() ; ) { String unprocessedGroupDN = e1.nextElement().toString(); String unprocessedGroupCN = getCN(unprocessedGroupDN); // Quick check for direct membership if (isSame (groupCN, unprocessedGroupCN) && isSame (groupDistinguishedName, unprocessedGroupDN)) { Log.info(username + " is authorized."); return true; } else { unProcessedUserGroups.put(unprocessedGroupDN, unprocessedGroupCN); } } if (userMemberOf(ctx, defaultSearchBase, processedUserGroups, unProcessedUserGroups, groupCN, groupDistinguishedName)) { Log.info(username + " is authorized."); return true; } } Log.info(username + " is NOT authorized."); return false; } catch (AuthenticationException e) { Log.info(username + " is NOT authenticated"); return false; } catch (NamingException e) { throw new SystemException(e); } finally { if (ctx != null) { try { ctx.close(); } catch (NamingException e) { throw new SystemException(e); } } } } public static boolean userMemberOf(DirContext ctx, String searchBase, HashMap processedUserGroups, HashMap unProcessedUserGroups, String groupCN, String groupDistinguishedName) throws NamingException { HashMap newUnProcessedGroups = new HashMap(); for (Iterator entry = unProcessedUserGroups.keySet().iterator(); entry.hasNext();) { String unprocessedGroupDistinguishedName = (String) entry.next(); String unprocessedGroupCN = (String)unProcessedUserGroups.get(unprocessedGroupDistinguishedName); if ( processedUserGroups.get(unprocessedGroupDistinguishedName) != null) { Log.info("Found : " + unprocessedGroupDistinguishedName +" in processedGroups. skipping further processing of it..." ); // We already traversed this. continue; } if (isSame (groupCN, unprocessedGroupCN) && isSame (groupDistinguishedName, unprocessedGroupDistinguishedName)) { Log.info("Found Match DistinguishedName : " + unprocessedGroupDistinguishedName +", CN : " + unprocessedGroupCN ); return true; } } for (Iterator entry = unProcessedUserGroups.keySet().iterator(); entry.hasNext();) { String unprocessedGroupDistinguishedName = (String) entry.next(); String unprocessedGroupCN = (String)unProcessedUserGroups.get(unprocessedGroupDistinguishedName); processedUserGroups.put(unprocessedGroupDistinguishedName, unprocessedGroupCN); // Fetch Groups in unprocessedGroupCN and put them in newUnProcessedGroups NamingEnumeration ns = executeSearch(ctx, SearchControls.SUBTREE_SCOPE, searchBase, MessageFormat.format( SEARCH_GROUP_BY_GROUP_CN, new Object[] {unprocessedGroupCN}), new String[] {CN, DISTINGUISHED_NAME, MEMBER_OF}); // Loop through the search results while (ns.hasMoreElements()) { SearchResult sr = (SearchResult) ns.next(); // Make sure we're looking at correct distinguishedName, because we're querying by CN String userDistinguishedName = sr.getAttributes().get(DISTINGUISHED_NAME).get().toString(); if (!isSame(unprocessedGroupDistinguishedName, userDistinguishedName)) { Log.info("Processing CN : " + unprocessedGroupCN + ", DN : " + unprocessedGroupDistinguishedName +", Got DN : " + userDistinguishedName +", Ignoring..."); continue; } Log.info("Processing for memberOf CN : " + unprocessedGroupCN + ", DN : " + unprocessedGroupDistinguishedName); // Look for and process memberOf Attribute memberOf = sr.getAttributes().get(MEMBER_OF); if (memberOf != null) { for ( Enumeration e1 = memberOf.getAll() ; e1.hasMoreElements() ; ) { String unprocessedChildGroupDN = e1.nextElement().toString(); String unprocessedChildGroupCN = getCN(unprocessedChildGroupDN); Log.info("Adding to List of un-processed groups : " + unprocessedChildGroupDN +", CN : " + unprocessedChildGroupCN); newUnProcessedGroups.put(unprocessedChildGroupDN, unprocessedChildGroupCN); } } } } if (newUnProcessedGroups.size() == 0) { Log.info("newUnProcessedGroups.size() is 0. returning false..."); return false; } // process unProcessedUserGroups return userMemberOf(ctx, searchBase, processedUserGroups, newUnProcessedGroups, groupCN, groupDistinguishedName); } private static NamingEnumeration executeSearch(DirContext ctx, int searchScope, String searchBase, String searchFilter, String[] attributes) throws NamingException { // Create the search controls SearchControls searchCtls = new SearchControls(); // Specify the attributes to return if (attributes != null) { searchCtls.setReturningAttributes(attributes); } // Specify the search scope searchCtls.setSearchScope(searchScope); // Search for objects using the filter NamingEnumeration result = ctx.search(searchBase, searchFilter,searchCtls); return result; } private static SearchResult executeSearchSingleResult(DirContext ctx, int searchScope, String searchBase, String searchFilter, String[] attributes) throws NamingException { NamingEnumeration result = executeSearch(ctx, searchScope, searchBase, searchFilter, attributes); SearchResult sr = null; // Loop through the search results while (result.hasMoreElements()) { sr = (SearchResult) result.next(); break; } return sr; } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With