You don't add groups to users, you add users (or groups, or computers ..) to groups !
One of the attributes of a user object is "memberOf" and conversely one of the attributes of a group is "member"
memberOf is not a "real" attribute per se, meaning that it is read only, it contains "links" to groups and that it is constructed from all the groups that the user is a member of in that domain.
The reason why it has links to groups is to ensure referential integrity Eg. if you delete, rename or move a user, the group's membership is correctly maintained.
it is read-only, and if you attempt to modify it's values you wil get a ldap Error of the form:
javax.naming.OperationNotSupportedException: [LDAP: error code 53 - 0000209A: SvcErr: DSID-031A0DD1, problem 5003 (WILL_NOT_PERFORM),data 0];
One other phenonema is that the user's membership only reflects the groups that are known on the domain controller that is being queried. In a multi-domain or multi-forest environment, a domain controller will only have knowledge of groups in it's own domain.
If the domain controller is a Global Catalog, it will have knowledge of all groups in the forest so it will reflect the list of groups in the forest that a user is a member of.
This is a slight simplification, with Windows Server 2003, branch office scenarios enable caching of group memberships without the need for a global catalog. (Me thinks a description of the Global Catalog is a future forum topic).
Therefore viewing a user's memberOf attribute may not reveal the full list of groups that a user is a member of. In addition, memberOf does not contain the user's Primary Group membership, nor does it reflect groups in other forests that the user may belong to.
The following code demonstrates viewing a user's memberOf attribute
import java.util.Hashtable;
import javax.naming.*;
import javax.naming.ldap.*;
import javax.naming.directory.*;
public class memberof {
public static void main (String[] args) {
Hashtable env = new Hashtable();
String adminName = "CN=Administrator,CN=Users,DC=ANTIPODES,DC=COM";
String adminPassword = "XXXXXXX";
String ldapURL = "ldap://mydc.antipodes.com:389";
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
//set security credentials, note using simple cleartext authentication
env.put(Context.SECURITY_AUTHENTICATION,"simple");
env.put(Context.SECURITY_PRINCIPAL,adminName);
env.put(Context.SECURITY_CREDENTIALS,adminPassword);
//connect to my domain controller
env.put(Context.PROVIDER_URL,ldapURL);
try {
//Create the initial directory context
LdapContext ctx = new InitialLdapContext(env,null);
//Create the search controls
SearchControls searchCtls = new SearchControls();
//Specify the search scope
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
//specify the LDAP search filter
String searchFilter = "(&(objectClass=user)(CN=Andrew Anderson))";
//Specify the Base for the search
String searchBase = "DC=antipodes,DC=com";
//initialize counter to total the group members
int totalResults = 0;
//Specify the attributes to return
String returnedAtts[]={"memberOf"};
searchCtls.setReturningAttributes(returnedAtts);
//Search for objects using the filter
NamingEnumeration answer = ctx.search(searchBase, searchFilter, searchCtls);
//Loop through the search results
while (answer.hasMoreElements()) {
SearchResult sr = (SearchResult)answer.next();
System.out.println(">>>" + sr.getName());
//Print out the groups
Attributes attrs = sr.getAttributes();
if (attrs != null) {
try {
for (NamingEnumeration ae = attrs.getAll();ae.hasMore();) {
Attribute attr = (Attribute)ae.next();
System.out.println("Attribute: " + attr.getID());
for (NamingEnumeration e = attr.getAll();e.hasMore();totalResults++) {
System.out.println(" " + totalResults + ". " + e.next());
}
}
}
catch (NamingException e) {
System.err.println("Problem listing membership: " + e);
}
}
}
System.out.println("Total groups: " + totalResults);
ctx.close();
}
catch (NamingException e) {
System.err.println("Problem searching directory: " + e);
}
}
}
An alternative is to use another constructed attribute, tokenGroups. It will return the list of Security Identifiers (SID) that are in the user's security token.
There are a few things to be aware of when using tokenGroups:
1. The SID's are in binary format and would need to be formatted into the "S-1-5-aa-bb-cc-dd" format to be human readable
2. You would then need to do searches using the SID to
find the distingusihed names of the groups that tehy map to.
3. The search base must be OBJECT_SCOPE
Eg.
//Specify the search scope
searchCtls.setSearchScope(SearchControls.OBJECT_SCOPE);
//specify the LDAP search filter
String searchFilter = "(objectClass=user)";
//Specify the Base for the search
String searchBase = "CN=Andrew Anderson,OU=Research,DC=antipodes,DC=com";