Related
Nothing I've found is working for me. And I'm having trouble finding answers that have valid code and aren't simply pasting a search filter string from another site.
The relevant code that's trying to do the search is:
SearchResult sr = executeSearchSingleResult(ctx, SearchControls.SUBTREE_SCOPE, "dc=mydomain,dc=local", "(&(objectClass=person)(sAMAccountName=admin2))", new String[]{"memberOf"});
if (sr != null) {
Attribute memberOf = sr.getAttributes().get("memberOf");
if (memberOf != null) {
for (int i = 0; i < memberOf.size(); i++) {
Attributes attributes = ctx.getAttributes(memberOf.get(i).toString(), new String[]{"CN"});
Attribute attribute = attributes.get("CN");
if (attribute != null) {
log.info("member of : " + attribute.get(0));
}
}
for (Enumeration e1 = memberOf.getAll(); e1.hasMoreElements();) {
String unprocessedGroupDN = e1.nextElement().toString();
String unprocessedGroupCN = getCN(unprocessedGroupDN);
//checking something here
}
}
}
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;
try {
while (result.hasMoreElements()) {
sr = (SearchResult) result.next();
break;
}
} catch (Exception e) {
log.error(e, e);
}
return sr;
}
private static NamingEnumeration executeSearch(DirContext ctx, int searchScope, String searchBase, String searchFilter, String[] attributes) throws NamingException {
SearchControls searchCtls = new SearchControls();
if (attributes != null) {
searchCtls.setReturningAttributes(attributes);
}
searchCtls.setSearchScope(searchScope);
NamingEnumeration result = ctx.search(searchBase, searchFilter, searchCtls);
return result;
}
This works fine when there are no nested groups. But say I have the following structure of groups and users:
My Admins (dn = CN=My Admins,CN=Users,DC=mydomain,DC=local)
AdminUser1 (dn = CN=AdminUser 1,CN=Users,DC=mydomain,DC=local)
AdminGroup1 (dn = CN=AdminGroup 1,CN=Users,DC=,mydomain,DC=local)
AdminUser2 (dn = CN=AdminUser 2,CN=Users,DC=mydomain,DC=local)
This finds AdminUser1 just fine. It cannot find AdminUser2. What I need to do, is discover that AdminUser2 goes all the way back up to the highest level group called My Admins.
I found a lot of references to 1.2.840.113556.1.4.1941, but different ways of putting that into the search filter have not helped.
What do I need to change in the code and/or the search filter to glean that a particular user at any particular depth of group nesting goes all the way back up to the topmost group?
Using the LDAP_MATCHING_RULE_IN_CHAIN filter similar to:
(member:1.2.840.113556.1.4.1941:=(CN=UserName,CN=Users,DC=YOURDOMAIN,DC=NET))
Will normally find all the groups that the user CN=UserName,CN=Users,DC=YOURDOMAIN,DC=NET is a member of.
But it is complicated.
Microsoft Active Directory has several group
types
Microsoft Active Directory has different LDAP Services
(normal and Global Catalog)
Limitations
Any which may cause a group to not show within results.
So the groups MUST be Security Groups and you should use the Global Catalog
And then there are limitations. The LDAP_MATCHING_RULE_IN_CHAIN type searches tend to fail when groups are nested "too deep" or "Too Broad". That is too many nesting levels or too many groups that the member is a member of.
In LDAP we can query if a User belongs to a given group once you have established a connection you can query using either member or memberOf attribute.
Query for memberOf Attribute :
filter used : (&(Group Member Attribute=Group DN)(objectClass=Group Object class))
Ex : (&(memberOf=CN=group,ou=qa_ou,dc=ppma,dc=org)(objectClass=group))
Using member Attribute :
filter used : (&(Group Member Attribute=User DN)(objectClass=Group Object class))
Ex : (&(member=CN=user,ou=qa_ou,dc=ppma,dc=org)(objectClass=group))
But You'll have to search recursively using the member or memberOf attribute list for a user. e.g. if a user has the following group hierarchy :
cn: user1 memberOf: CN=group1,DC=foo,DC=example,DC=com memberOf: CN=group2,DC=foo,DC=example,DC=com
then you'll want to recursively lookup group1 and group2 with additional LDAP searches, and so on for groups which those groups are members of.
We can't use LDAP_MATCHING_RULE_IN_CHAIN in production since it doesn't work when we have too deep nested hierarchy and it only works for Active Directory. Below Solution works independently with AD or OpenLDAP we just need to replace the group attributes.
Below is the sample code to query all the nested groups a User belongs to :
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
public class MemberDemo {
private static final String contextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
private static final String connectionURL = "ldap://10.224.243.133:389";
private static final String connectionName = "CN=administrator,CN=users,DC=ppma,DC=org";
private static final String connectionPassword = "Conleyqa12345";
public static int nestLevel = 3;
public static int level = 1;
// Optional
private static final String authentication = null;
private static final String protocol = null;
private static String userBase = "OU=qa_OU,DC=ppma,DC=org";
public static void main(String[] args) throws NamingException {
long start = System.currentTimeMillis();
Hashtable<String, String> env = new Hashtable<String, String>();
// Configure our directory context environment.
env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory);
env.put(Context.PROVIDER_URL, connectionURL);
env.put(Context.SECURITY_PRINCIPAL, connectionName);
env.put(Context.SECURITY_CREDENTIALS, connectionPassword);
if (authentication != null)
env.put(Context.SECURITY_AUTHENTICATION, authentication);
if (protocol != null)
env.put(Context.SECURITY_PROTOCOL, protocol);
InitialDirContext context = new InitialDirContext(env);
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
Set<String> traversedGroups = new HashSet<String>();
Set<String> relatedGroups = new HashSet<String>();
List<String> tempParentColl = new CopyOnWriteArrayList<String>();
List<String> tempGroups = new ArrayList<String>();
String loginUser = "CN=qa20Nest,OU=qa_OU,DC=ppma,DC=org";
String filter = "(&(member=" + loginUser + ")(objectClass=group))";
tempGroups = findNestedGroups(tempGroups, context, filter, loginUser, constraints,
tempParentColl, traversedGroups, getUserName(loginUser));
relatedGroups.addAll(tempGroups);
System.out.println("Parent Groups :");
for (String group : relatedGroups) {
System.out.println(group);
}
long end = System.currentTimeMillis();
long elapsedTime = end - start;
System.out.println("Total time taken in sec : " + elapsedTime / 1000);
}
#SuppressWarnings("rawtypes")
public static List<String> findNestedGroups(List<String> tempGrpRelations, InitialDirContext context, String filter,
String groupName, SearchControls constraints, List<String> tempParentColl, Set<String> traversedGrp,
String groupIdentifier) {
NamingEnumeration results;
try {
traversedGrp.add(groupName);
results = context.search(userBase, filter, constraints);
// Fail if no entries found
if (results == null || !results.hasMore()) {
System.out.println("No result found for :" + groupName);
if (tempParentColl.isEmpty()) {
return tempGrpRelations;
} else {
tempParentColl.remove(groupName);
}
}
while (results.hasMore()) {
SearchResult result = (SearchResult) results.next();
System.out.println("DN - " + result.getNameInNamespace());
tempParentColl.add(result.getNameInNamespace());
tempGrpRelations.add(result.getNameInNamespace());
}
Iterator<String> itr = tempParentColl.iterator();
while (itr.hasNext()) {
String groupDn = itr.next();
String nfilter = "(&(member=" + groupDn + ")(objectClass=group))";
tempParentColl.remove(groupDn);
if (!traversedGrp.contains(groupDn)) {
findNestedGroups(tempGrpRelations, context, nfilter, groupDn, constraints, tempParentColl,
traversedGrp, getUserName(groupDn));
}
}
} catch (NamingException e) {
e.printStackTrace();
}
return tempGrpRelations;
}
public static String getUserName(String cnName) {
String name = cnName.substring(cnName.indexOf("CN=")).split(",")[0].split("=")[1];
return name;
}
}
I have following code
public class UserDAO {
private final MongoCollection<Document> usersCollection;
private Random random = new SecureRandom();
public UserDAO(final MongoDatabase blogDatabase) {
usersCollection = blogDatabase.getCollection("users");
}
public boolean addUser(String username, String password, String email) {
String passwordHash = makePasswordHash(password, Integer.toString(random.nextInt()));
Document doc = new Document("_id", username).append("password", passwordHash);
if (email != null && !email.equals("")) {
doc = new Document("_id", username)
.append("password", passwordHash)
.append("email", email);
}
try {
usersCollection.insertOne(doc);
return true;
} catch (MongoWriteException e) {
if (e.getError().getCategory().equals(ErrorCategory.DUPLICATE_KEY)) {
System.out.println("Username already in use: " + username);
return false;
}
throw e;
}
}
public Document validateLogin(String username, String password) {
Document user = null;
user = (Document) usersCollection.find(new BasicDBObject("_id", username));// at this line I'm getting error
if (user == null) {
System.out.println("User not in database");
return null;
}
String hashedAndSalted = user.get("password").toString();
String salt = hashedAndSalted.split(",")[1];
if (!hashedAndSalted.equals(makePasswordHash(password, salt))) {
System.out.println("Submitted password is not a match");
return null;
}
return user;
}
}
I want to get one document/record but I'm getting error
java.lang.ClassCastException: com.mongodb.FindIterableImpl cannot be cast to org
.bson.Document
at course.UserDAO.validateLogin(UserDAO.java:94)
at course.BlogController$6.doHandle(BlogController.java:231)
at course.BlogController$FreemarkerBasedRoute.handle(BlogController.java
:92)
at spark.webserver.MatcherFilter.doFilter(MatcherFilter.java:139)
at spark.webserver.JettyHandler.doHandle(JettyHandler.java:54)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandle
r.java:179)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.j
ava:136)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper
.java:97)
at org.eclipse.jetty.server.Server.handle(Server.java:451)
at org.eclipse.jetty.server.HttpChannel.run(HttpChannel.java:252)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.jav
a:266)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.run(AbstractConn
ection.java:240)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPoo
l.java:596)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool
.java:527)
at java.lang.Thread.run(Thread.java:745)
are there any mistakes in my code, any solutions.
You can do this also:
Document user = null;
user = usersCollection.find(eq("_id", username)).first();
use Filter.eq() to compare the equality of the username, If you have used import static com.mongodb.client.model.Filters.eq; then only use eq() method.
And use first() it returns the first Document from the collection.
OK I've found this answer that is make some changes in validateLogin() as
public Document validateLogin(String username, String password) {
FindIterable<Document> user = null; // Change to get the object of FindIterable<Document>
user = usersCollection.find(new Document("_id", username) );// give Document as the find() argument
if (user == null) {
System.out.println("User not in database");
return null;
}
String hashedAndSalted = user.first().get("password").toString();// get user.first()
String salt = hashedAndSalted.split(",")[1];
if (!hashedAndSalted.equals(makePasswordHash(password, salt))) {
System.out.println("Submitted password is not a match");
return null;
}
return user.first();//get user.first()
}
hence the issue is resolved.
by just adding first() to your search query will save you all the trouble.
Bson filter = new Document("_id",username);
Document user = coll.find().filter(filter).first();
My goal is recursive getting all group members from domain and subdomains using only connection to main dc.
I have Active Directory forest containing domain.com, sub.domain.com.
My 'MainGroup' located in domain.com and contains members/groups from domain.com and sub.domain.com.
I'm getting 'member' field strings of MainGroup using ldapContext connection with dc.domain.com. Here is it:
cn=userA,ou=Users,dc=domain,dc=com
cn=userB,ou=Users,dc=sub,dc=domain,dc=com
cn=groupB,ou=Users,dc=sub,dc=domain,dc=com
Can I get subdomain's user/group data (I need ObjectClass to recursive iterate) using current ldapConext?
private LdapContext createLdapContext() {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapHost);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "user#" + domain);
env.put(Context.SECURITY_CREDENTIALS, "******");
try {
ldapContext = new InitialLdapContext(env, null);
} catch (NamingException e) {
e.printStackTrace();
}
return ldapContext;
}
public List getGroupMembers(String groupName) {
List resultList = new ArrayList<String>();
int Start = 0;
int Finish = 1499;
int Step = 1500;
boolean Finished = false;
String Range;
try {
while (!Finished) {
Range = Start + "-" + Finish;
String[] returningAttrs = {"member;range=" + Range};
String searchFilter = "(&(objectClass=group)(sAMAccountName=" + groupName + "))";
SearchControls searchControls = new SearchControls();
searchControls.setReturningAttributes(returningAttrs);
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration answer = ldapContext.search(searchBase, searchFilter, searchControls);
while (answer.hasMoreElements()) {
SearchResult sr = (SearchResult) answer.next();
Attributes attrs = sr.getAttributes();
if (attrs != null) {
for (NamingEnumeration ae = attrs.getAll(); ae.hasMoreElements(); ) {
Attribute attr = (Attribute) ae.next();
if (attr.getID().endsWith("*")) {
Finished = true;
}
for (NamingEnumeration e = attr.getAll(); e.hasMoreElements(); ) {
resultList.add(e.next().toString());
}
}
}
}
Start = Start + Step;
Finish = Finish + Step;
}
ldapContext.close();
} catch (NamingException e) {
e.printStackTrace();
} finally {
return resultList;
}
}
Edited. Found another solution:
Anyway we should query every domain.
We can make 'recursive one shoot query' using filter (memberof:1.2.840.113556.1.4.1941:=cn=Group1,OU=groupsOU,DC=x)
Related c# question
msdn Search Filter Syntax
I am using JNDI to connect to the LDAP active directory, and I want to search for users where the name contains the search string, so my search method is as follows:
public static List<LDAPUser> searchContactsByName(
ExtendedDirContext extendedDirContext, String name) {
try {
LdapContext ldapContext = extendedDirContext.getLdapContext();
String searchBaseStr = extendedDirContext.getSearchBase();
String sortKey = LDAPAttributes.NAME;
ldapContext.setRequestControls(new Control[] { new SortControl(
sortKey, Control.CRITICAL) });
SearchControls searchCtls = new SearchControls();
searchCtls.setTimeLimit(1000 * 10);
String returnedAtts[] = { LDAPAttributes.USER_NAME,
LDAPAttributes.NAME };
searchCtls.setReturningAttributes(returnedAtts);
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String searchFilter = "(&(ObjectCategory=person)(cn=*" + name
+ "*))";
NamingEnumeration<SearchResult> results = ldapContext.search(
searchBaseStr, searchFilter, searchCtls);
List<LDAPUser> users = new ArrayList<LDAPUser>(0);
while (results.hasMoreElements()) {
SearchResult sr = (SearchResult) results.next();
Attributes attrs = sr.getAttributes();
LDAPUser user = new LDAPUser();
user.setName(attrs.get(LDAPAttributes.NAME).toString()
.replace("cn: ", ""));
user.setUserName(attrs.get(LDAPAttributes.USER_NAME).toString()
.replace("sAMAccountName: ", ""));
users.add(user);
}
return users;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
and here is how I am making the connection to LDAP:
public static ExtendedDirContext connectToLdap(MessageSource messageSource) {
try {
log.debug("connectToLdap");
String providerUrl = messageSource.getMessage("provider.url", null,
null);
String securityPrincipal = messageSource.getMessage(
"security.principal", null, null);
String securityCredentials = messageSource.getMessage(
"security.credentials", null, null);
String searchBase = messageSource.getMessage("search.base", null,
null);
boolean ssl = Boolean.parseBoolean(messageSource.getMessage("ssl",
null, null));
LdapContext ldapContext;
Hashtable<String, String> ldapEnv = new Hashtable<String, String>(
11);
ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
ldapEnv.put(Context.PROVIDER_URL, providerUrl);
ldapEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
ldapEnv.put(Context.SECURITY_PRINCIPAL, securityPrincipal);
ldapEnv.put(Context.SECURITY_CREDENTIALS, securityCredentials);
if (ssl)
ldapEnv.put(Context.SECURITY_PROTOCOL, "ssl");
// To get rid of the PartialResultException when using Active
// Directory
ldapEnv.put(Context.REFERRAL, "follow");
ldapContext = new InitialLdapContext(ldapEnv, null);
ExtendedDirContext extendedDirContext = new ExtendedDirContext();
extendedDirContext.setLdapContext(ldapContext);
extendedDirContext.setSearchBase(searchBase);
log.debug("success connection to ldap");
return extendedDirContext;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
The LDAP credentials are as follows:
provider.url=ldap://dc.fabrikam.com:389
security.principal=CN=administrator,CN=Users,DC=fabrikam,DC=com
security.credentials=password
search.base=dc=fabrikam,dc=com
Why does the search take so much time to retrieve the data? Is there any change that I can do to make the search faster, since I have only 285 contacts in the AD?
Solution was to change ldapEnv.put(Context.REFERRAL, "follow"); to ldapEnv.put(Context.REFERRAL, "ignore");
Your filter:
"(&(ObjectCategory=person)(cn=*" + name + "*))"
May be an issue.
I would recommend that you download a known LDAP utility (Apache Directory Studio Browser as an example) and try different search filters until you find one that works.
To Start, try
"(&(ObjectCategory=person)(cn= + name ))"
You're right,
ldapEnv.put(Context.REFERRAL, "ignore")
didn't get exception about connection timed out. But when I first try I get a partialexception. After I changed my LDAP configuration port from 389 to 3268 I didn't get any exception, build successfully. 3268 port about global catalog of LDAP. For example Outlook clients query the global catalog to locate Address Book information. You can try global catalog if you get an exception referral type setting.
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;
}
}
None of above code snippets didn't worked for me. After 1 day spending on Google and tomcat source following code worked well to find user groups.
import java.util.Hashtable;
import javax.naming.CompositeName;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NameParser;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
public class MemberOfTest{
private static final String contextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
private static final String connectionURL = "ldap://HOST:PORT";
private static final String connectionName = "CN=Query,CN=Users,DC=XXX,DC=XX";
private static final String connectionPassword = "XXX";
// Optioanl
private static final String authentication = null;
private static final String protocol = null;
private static String username = "XXXX";
private static final String MEMBER_OF = "memberOf";
private static final String[] attrIdsToSearch = new String[] { MEMBER_OF };
public static final String SEARCH_BY_SAM_ACCOUNT_NAME = "(sAMAccountName=%s)";
public static final String SEARCH_GROUP_BY_GROUP_CN = "(&(objectCategory=group)(cn={0}))";
private static String userBase = "DC=XXX,DC=XXX";
public static void main(String[] args) throws NamingException {
Hashtable<String, String> env = new Hashtable<String, String>();
// Configure our directory context environment.
env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory);
env.put(Context.PROVIDER_URL, connectionURL);
env.put(Context.SECURITY_PRINCIPAL, connectionName);
env.put(Context.SECURITY_CREDENTIALS, connectionPassword);
if (authentication != null)
env.put(Context.SECURITY_AUTHENTICATION, authentication);
if (protocol != null)
env.put(Context.SECURITY_PROTOCOL, protocol);
InitialDirContext context = new InitialDirContext(env);
String filter = String.format(SEARCH_BY_SAM_ACCOUNT_NAME, username);
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
constraints.setReturningAttributes(attrIdsToSearch);
NamingEnumeration results = context.search(userBase, filter,constraints);
// Fail if no entries found
if (results == null || !results.hasMore()) {
System.out.println("No result found");
return;
}
// Get result for the first entry found
SearchResult result = (SearchResult) results.next();
// Get the entry's distinguished name
NameParser parser = context.getNameParser("");
Name contextName = parser.parse(context.getNameInNamespace());
Name baseName = parser.parse(userBase);
Name entryName = parser.parse(new CompositeName(result.getName())
.get(0));
// Get the entry's attributes
Attributes attrs = result.getAttributes();
Attribute attr = attrs.get(attrIdsToSearch[0]);
NamingEnumeration e = attr.getAll();
System.out.println("Member of");
while (e.hasMore()) {
String value = (String) e.next();
System.out.println(value);
}
}
}
The easiest way is with 'lookup': (to open an Ldap Context: look above examples)
/**
* Tests if an Active Directory user exists in an Active Directory group.
* #param ctx LDAP Context.
* #param dnADGroup distinguishedName of group.
* #param dnADUser distinguishedName of user.
* #return True if user is member of group.
*/
public static boolean isMemberOfADGroup(LdapContext ctx, String dnADGroup, String dnADUser) {
try {
DirContext lookedContext = (DirContext) (ctx.lookup(dnADGroup));
Attribute attrs = lookedContext.getAttributes("").get("member");
for (int i = 0; i < attrs.size(); i++) {
String foundMember = (String) attrs.get(i);
if(foundMember.equals(dnADUser)) {
return true;
}
}
} catch (NamingException ex) {
String msg = "There has been an error trying to determin a group membership for AD user with distinguishedName: "+dnADUser;
System.out.println(msg);
ex.printStackTrace();
}
return false;
}
LDAP lookup methods of finding whether a user is a member of a group are not correct, especially if you're talking about a logged on user. For a user that's actually logged on the list of groups varies depending on which computer the user logged on. That list needs to include groups from domain trusts, nested groups and local groups.
If you're looking for group memberships of the currently logged on user or a user that you're logging on with a username and password in Java, try Waffle.
IWindowsAuthProvider prov = new WindowsAuthProviderImpl();
IWindowsIdentity identity = prov.logonUser("username", "password");
System.out.println("User identity: " + identity.getFqn());
for(IWindowsAccount group : identity.getGroups()) {
System.out.println(" " + group.getFqn() + " (" + group.getSidString() + ")");
}
Following up on Sundaramurthi's answer, it could be done even more straightforward way, where you don't query for all the user's group:
(&(objectClass=user)(sAMAccountName=XXXX)(memberOf=CN=YYY,OU=_Common-Access,OU=Groups,OU=_CORP,DC=XXX,DC=XX))
where
XXXX - user name
XXX.XX - domain name
YYY - group name
This lets you to just get an answer whether user is in a group or not.
So just do:
String userBase = "DC=XXX,DC=XX";
String CHECK_IF_USER_IN_GROUP = "(&(objectClass=user)(sAMAccountName=%s)(memberOf=CN=%s,OU=...,OU=...,OU=...,%s))";
String queryFilter = String.format(CHECK_IF_USER_IN_GROUP, user, group, userBase);
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration results = context.search(userBase, queryFilter, constraints);
if (results == null) {
throw new Exception("No answer from LDAP");
}
if (!results.hasMore()) {
System.out.println("No result found");
// user is not in the group
}
// user is in the group
Not sure about Java API specifics, but the generic way of doing this is adding a group check to the query/binding.
I can't give you a working code using java naming ldap.
I used Spring LDAP, and the way you do it:
Get the User object, do a search on the username something like sAMAccountName=USERNAME
After you get the object you retreive the property memberOf -> this will be a list and check for a specific one in Java.
That is the only way I could think of.
To query if a user belongs to a given group once you have established a connection you can query using either member or memberOf attribute in Active Directory.
Using memberOf Attribute :
filter used : (&(Group Member Attribute=Group DN)(objectClass=Group Object class))
Ex : (&(memberOf=CN=group,ou=qa_ou,dc=ppma,dc=org)(objectClass=group))
Using member Attribute :
filter used : (&(Group Member Attribute=User DN)(objectClass=Group Object class))
Ex : (&(member=CN=user,ou=qa_ou,dc=ppma,dc=org)(objectClass=group))
Unfortunately the answer varies with installations of AD as well as other types of LDAP server.
We had to solve the same problem. In the end we allowed the system administrator to provide us with an LDAP query-pattern where we substitute the user name (and group name if that needs to be variable too) into the pattern.
I found this useful:
retrieves-group-membership for Active Directory
And I have this piece of working code:
import java.util.Hashtable;
import javax.naming.CompositeName;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NameParser;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
public class TestAD1 {
private static String userBase = "DC=SomeName,DC=SomeName,DC=SomeName,DC=SomeName,DC=COM,DC=US";
public static void main(String[] args) {
TestAD1 tad = new TestAD1();
try {
// Create a LDAP Context
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "ldap.serviceaccount#domain.com");
env.put(Context.SECURITY_CREDENTIALS, "drowssap");
env.put(Context.PROVIDER_URL, "ldap://fully.qualified.server.name:389");
LdapContext ctx = new InitialLdapContext(env, null);
InitialDirContext inidircontext = new InitialDirContext(env);
DirContext dirctx = new InitialLdapContext(env, null);
System.out.println("Connection Successful.");
// Print all attributes of the name in namespace
SearchControls sctls = new SearchControls();
String retatts[] = {"sn", "mail", "displayName", "sAMAccountName"};
sctls.setReturningAttributes(retatts);
sctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String srchfilter = "(&(objectClass=user)(mail=*))";
String srchbase = userBase;
int totalresults = 0;
NamingEnumeration answer = dirctx.search(srchbase, srchfilter, sctls);
while (answer.hasMoreElements()) {
SearchResult sr = (SearchResult) answer.next();
totalresults++;
System.out.println(">>> " + sr.getName());
Attributes attrs = sr.getAttributes();
if (answer == null || !answer.hasMore()) {
System.out.println("No result found");
return;
}
if (attrs != null) {
try {
System.out.println(" surname: " + attrs.get("sn").get());
System.out.println(" Email - ID: " + attrs.get("mail").get());
System.out.println(" User - ID: " + attrs.get("displayName").get());
System.out.println(" Account Name: " + attrs.get("sAMAccountName").get());
tad.GetGroups(inidircontext, attrs.get("sAMAccountName").get().toString());
} catch (NullPointerException e) {
System.out.println("Error listing attributes..." + e);
}
}
System.out.println("Total Results : " + totalresults);
// close dir context
dirctx.close();
}
ctx.close();
} catch (NamingException e) {
System.out.println("Problem Search Active Directory..." + e);
//e.printStackTrace();
}
}
// Get all the groups.
public void GetGroups(InitialDirContext context, String username) throws NamingException {
String[] attrIdsToSearch = new String[]{"memberOf"};
String SEARCH_BY_SAM_ACCOUNT_NAME = "(sAMAccountName=%s)";
String filter = String.format(SEARCH_BY_SAM_ACCOUNT_NAME, username);
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
constraints.setReturningAttributes(attrIdsToSearch);
NamingEnumeration results = context.search(userBase, filter, constraints);
// Fail if no entries found
if (results == null || !results.hasMore()) {
System.out.println("No result found");
return;
}
SearchResult result = (SearchResult) results.next();
Attributes attrs = result.getAttributes();
Attribute attr = attrs.get(attrIdsToSearch[0]);
NamingEnumeration e = attr.getAll();
System.out.println(username + " is Member of the following groups : \n");
while (e.hasMore()) {
String value = (String) e.next();
System.out.println(value);
}
}
}
Also you can modify the accepted answer from here: Authenticating against Active Directory with Java on Linux with the following:
String group="name of the group";
Iterator ig = groups.iterator();
Boolean bool=false;
while (ig.hasNext()) {
String a=ig.next().toString();
if (a.equals(group)) {
JOptionPane.showMessageDialog(this, "Authentication succeeded!");
bool=true;
// here you can do smth in case of success
}
}
if (bool==false){
JOptionPane.showMessageDialog(this, "Permission denied");
}