Check Group Membership for a Linux User Using Java

Check group membership for a Linux user using Java

You can try to read the file /etc/group.

I have developed a class to easily query this file:

public class UserInfo {

public UserInfo() throws FileNotFoundException, IOException {
this.group2users = new HashMap<>();

FileReader fileReader = new FileReader(groupsFilePath);
BufferedReader groupsReader = new BufferedReader(fileReader);
while(groupsReader.ready())
{
try
{
String line = groupsReader.readLine();
String [] tokens = line.split(":");
String groupName = tokens[0];
Set<String> users = group2users.get(groupName);
if(users == null)
{
users = new HashSet<String>();
group2users.put(groupName, users);
}
if(tokens.length>3)
{
for(String uStr: tokens[3].split(","))
users.add(uStr);
}
} catch (Exception e) { continue; }
}
groupsReader.close();
fileReader.close();
}

public boolean belongs2group(String user, String group)
{
Set<String> groupRef = group2users.get(group);
if(groupRef == null) return false;
return groupRef.contains(user);
}

private String groupsFilePath = "/etc/group";
private Map<String, Set<String>> group2users;

}

This code maps the /etc/group file and keep a map of groups-their users set.
I have developed just one query method (belongs2group) but it is fairly easy to add methods to list all groups and/or all users.

This code is written using the old-fashioned-mainstream java io-api but I think it can be easily adapted to nio. Let me know if you need me to complete that step.

Enumerating a user's groups in Java

There is no "clean" to way to do this in Java. Java does not have any standard APIs for enumerating a user's groups. Your options would be:

  • implement a native library wrapper that called the getgroups C library method, and then call it from Java via JNI or JNA
  • use java.lang.Process to call an external program to get the information
  • query LDAP or Kerberos / AD or JNDI from Java

The last one only works if the system uses LDAP / Kerberos / JNDI and you have the appropriate credentials, schema configs and so on. These make this possibly the hardest approach ... and in practice just as non-portable as the other approaches.

Java LDAP - Determine if user in a given group?

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;
}
}

Check if a user is in a group

Try doing this :

username=ANY_USERNAME
if getent group customers | grep -q "\b${username}\b"; then
echo true
else
echo false
fi

or

username=ANY_USERNAME
if groups $username | grep -q '\bcustomers\b'; then
echo true
else
echo false
fi

How to get all the LDAP groups for a particular user?

It may not be possible to get a list of all groups without using LDAP.
JAAS APIs generally give you a way to ask whether the user belongs to a certain group but not to get all groups at once.

The best you may be able to do without accessing LDAP directly is something like

for (String group : allGroups) { 
if (request.isUserInRole(group)) {
userGroups.add(group);
}
}

The performance hit should not be too bad if you do it once on session creation and then make userGroups session-scoped. (The container may well get all the groups on login.)



Related Topics



Leave a reply



Submit