[Pki-devel] [PATCH] 0027..0029 support external authorization LDAP server
Fraser Tweedale
ftweedal at redhat.com
Thu Mar 12 08:18:43 UTC 2015
New patch attached (squashed some bugs and cleaned up the
implementation considerably ; squashed back down into one commit).
Other comments inline below.
On Thu, Mar 12, 2015 at 09:40:20AM +0700, Endi Sukma Dewata wrote:
> On 3/12/2015 8:12 AM, Fraser Tweedale wrote:
> >On Wed, Mar 11, 2015 at 02:04:56PM -0400, Ade Lee wrote:
> >>Looks good in general.
> >>
> >>I notice that your patch adds the use of the Vector class.
> >>Vector is old and synchronized - which can slow things down
> >>unnecessarily. Use ArrayList or similar instead.
> >>
> >>Ade
> >>
> >Roger that; I will switch to ArrayList. For now you can all
> >s/Vector/ArrayList/g in your heads while you review this patch :)
>
> Quick comments:
>
> 1. The TOKEN_GROUPS probably should be a List<String> to simplify the
> creation and the usage of the list of groups.
>
We are constrained by the IAuthToken interface, which has only the
String[] method.
> 2. I'm not quite clear the purpose of this enhancement. If it's meant to be
> a general-purpose directory-based authentication plugin, it would make sense
> to have a fully configurable parameters for retrieving the group
> information. However, if this is only to be used for Dogtag authentication,
> there's already a user-group subsystem that can provide the information. See
> PKIRealm.getRoles().
>
It is apparently needed for retrieving groups from external
directories. https://fedorahosted.org/pki/ticket/1174
Cheers,
Fraser
> I may have more comments later.
>
> --
> Endi S. Dewata
-------------- next part --------------
>From 24d1144636c08841ff57503b18bf7f67010e3555 Mon Sep 17 00:00:00 2001
From: Christina Fu <cfu at redhat.com>
Date: Tue, 10 Mar 2015 02:06:02 -0400
Subject: [PATCH] Store groups on AuthToken and update group evaluator
Update the UidPwdDirAuthentication plugin to retrieve all the user's
groups from a directory and store them on the AuthToken.
Also update the group evaluator to match against all the groups
stored in the AuthToken. The "gid" and "groups" are merged into a
single collection, if the ACL operation is "=" the collection is
checked under disjunction, and if the operation is "!=", then
conjunction.
---
.../certsrv/authentication/IAuthToken.java | 3 +
.../cms/authentication/DirBasedAuthentication.java | 36 ++++++-
.../authentication/UidPwdDirAuthentication.java | 117 ++++++++++++++++++---
.../cms/evaluators/GroupAccessEvaluator.java | 33 ++++--
4 files changed, 164 insertions(+), 25 deletions(-)
diff --git a/base/common/src/com/netscape/certsrv/authentication/IAuthToken.java b/base/common/src/com/netscape/certsrv/authentication/IAuthToken.java
index 3c03cc1f5e85a92237067fde98e20d9f13a0b947..c552c4ce24730dc84906c95131f8d5ea9392d883 100644
--- a/base/common/src/com/netscape/certsrv/authentication/IAuthToken.java
+++ b/base/common/src/com/netscape/certsrv/authentication/IAuthToken.java
@@ -38,6 +38,9 @@ public interface IAuthToken {
* Constant for userid.
*/
public static final String USER_ID = "userid";
+ public static final String UID = "uid";
+ public static final String GID = "gid";
+ public static final String GROUPS = "groups";
/**
* Sets an attribute value within this AttrSet.
diff --git a/base/server/cms/src/com/netscape/cms/authentication/DirBasedAuthentication.java b/base/server/cms/src/com/netscape/cms/authentication/DirBasedAuthentication.java
index f2d09df9e410ac817e2a90d4f82bbd4c488a8ab2..78aa399b41263c3da1b050f1729eb48157d730e4 100644
--- a/base/server/cms/src/com/netscape/cms/authentication/DirBasedAuthentication.java
+++ b/base/server/cms/src/com/netscape/cms/authentication/DirBasedAuthentication.java
@@ -74,6 +74,13 @@ public abstract class DirBasedAuthentication
/* configuration parameter keys */
protected static final String PROP_LDAP = "ldap";
protected static final String PROP_BASEDN = "basedn";
+ protected static final String PROP_GROUPS_ENABLE = "groupsEnable";
+ protected static final String PROP_GROUPS_BASEDN = "groupsBasedn";
+ protected static final String PROP_GROUPS = "groups";
+ protected static final String PROP_GROUP_OBJECT_CLASS = "groupObjectClass";
+ protected static final String PROP_GROUP_USERID_NAME = "groupUseridName";
+ protected static final String PROP_USERID_NAME = "useridName";
+ protected static final String PROP_SEARCH_GROUP_USER_BY_USERDN = "searchGroupUserByUserdn";
protected static final String PROP_DNPATTERN = "dnpattern";
protected static final String PROP_LDAPSTRINGATTRS = "ldapStringAttributes";
protected static final String PROP_LDAPBYTEATTRS = "ldapByteAttributes";
@@ -94,6 +101,14 @@ public abstract class DirBasedAuthentication
/* ldap base dn */
protected String mBaseDN = null;
+ protected boolean mGroupsEnable = false;
+ protected String mGroups = null; // e.g. "ou=Groups"
+ protected String mGroupsBaseDN = null; // in case it's different from mBaseDN
+ protected String mGroupObjectClass = null;
+ protected String mUserIDName = null; // e.g. "uid"
+ protected String mGroupUserIDName = null; // in case it's different from mUserIDName
+ /* whether to search for member=<userDN> or member=<mGroupUserIdName>=<uid> */
+ protected boolean mSearchGroupUserByUserdn = true;
/* factory of anonymous ldap connections */
protected ILdapConnFactory mConnFactory = null;
@@ -243,10 +258,25 @@ public abstract class DirBasedAuthentication
/* initialize ldap server configuration */
mLdapConfig = mConfig.getSubStore(PROP_LDAP);
- if (needBaseDN)
+ if (needBaseDN) {
mBaseDN = mLdapConfig.getString(PROP_BASEDN);
- if (needBaseDN && ((mBaseDN == null) || (mBaseDN.length() == 0) || (mBaseDN.trim().equals(""))))
- throw new EPropertyNotFound(CMS.getUserMessage("CMS_BASE_GET_PROPERTY_FAILED", "basedn"));
+ if (mBaseDN == null || mBaseDN.trim().equals(""))
+ throw new EPropertyNotFound(CMS.getUserMessage("CMS_BASE_GET_PROPERTY_FAILED", "basedn"));
+ mGroupsEnable = mLdapConfig.getBoolean(PROP_GROUPS_ENABLE, false);
+ CMS.debug("DirBasedAuthentication: mGroupsEnable=" + (mGroupsEnable ? "true" : "false"));
+ mGroupsBaseDN = mLdapConfig.getString(PROP_GROUPS_BASEDN, mBaseDN);
+ CMS.debug("DirBasedAuthentication: mGroupsBaseDN="+ mGroupsBaseDN);
+ mGroups= mLdapConfig.getString(PROP_GROUPS, "ou=groups");
+ CMS.debug("DirBasedAuthentication: mGroups="+ mGroups);
+ mGroupObjectClass = mLdapConfig.getString(PROP_GROUP_OBJECT_CLASS, "groupofuniquenames");
+ CMS.debug("DirBasedAuthentication: mGroupObjectClass="+ mGroupObjectClass);
+ mUserIDName = mLdapConfig.getString(PROP_USERID_NAME, "uid");
+ CMS.debug("DirBasedAuthentication: mUserIDName="+ mUserIDName);
+ mSearchGroupUserByUserdn = mLdapConfig.getBoolean(PROP_SEARCH_GROUP_USER_BY_USERDN, true);
+ CMS.debug("DirBasedAuthentication: mSearchGroupUserByUserdn="+ mSearchGroupUserByUserdn);
+ mGroupUserIDName = mLdapConfig.getString(PROP_GROUP_USERID_NAME, "cn");
+ CMS.debug("DirBasedAuthentication: mGroupUserIDName="+ mGroupUserIDName);
+ }
mConnFactory = CMS.getLdapAnonConnFactory();
mConnFactory.init(mLdapConfig);
diff --git a/base/server/cms/src/com/netscape/cms/authentication/UidPwdDirAuthentication.java b/base/server/cms/src/com/netscape/cms/authentication/UidPwdDirAuthentication.java
index c85644d59eb4ab0354517140c623f3e36eb5d93c..78244c9c6016776b481b77e9a795bc96fb0839cd 100644
--- a/base/server/cms/src/com/netscape/cms/authentication/UidPwdDirAuthentication.java
+++ b/base/server/cms/src/com/netscape/cms/authentication/UidPwdDirAuthentication.java
@@ -18,10 +18,12 @@
package com.netscape.cms.authentication;
// ldap java sdk
+import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Vector;
+import netscape.ldap.LDAPAttribute;
import netscape.ldap.LDAPConnection;
import netscape.ldap.LDAPEntry;
import netscape.ldap.LDAPException;
@@ -45,6 +47,7 @@ import com.netscape.certsrv.profile.IProfileAuthenticator;
import com.netscape.certsrv.property.Descriptor;
import com.netscape.certsrv.property.IDescriptor;
import com.netscape.certsrv.request.IRequest;
+import com.netscape.certsrv.usrgrp.EUsrGrpException;
/**
* uid/pwd directory based authentication manager
@@ -59,7 +62,6 @@ public class UidPwdDirAuthentication extends DirBasedAuthentication
public static final String CRED_UID = "uid";
public static final String CRED_PWD = "pwd";
protected static String[] mRequiredCreds = { CRED_UID, CRED_PWD };
- public static final String USERID = "userid";
/* Holds configuration parameters accepted by this implementation.
* This list is passed to the configuration console so configuration
@@ -89,10 +91,68 @@ public class UidPwdDirAuthentication extends DirBasedAuthentication
};
/**
- * Default constructor, initialization must follow.
+ * Retrieves group base dn.
*/
- public UidPwdDirAuthentication() {
- super();
+ private String getGroupBaseDN() {
+ return mGroups + "," + mGroupsBaseDN;
+ }
+
+ /**
+ * List groups of which user is a member.
+ */
+ private ArrayList<String> listGroups(LDAPConnection ldapconn, String uid, String userdn)
+ throws EUsrGrpException {
+ String method = "UidPwdDirAuthentication: listGroups: ";
+ CMS.debug(method + " begins");
+ String[] attrs = {};
+
+ String k = null;
+ if (mGroupObjectClass.equalsIgnoreCase("groupOfUniqueNames"))
+ k = "uniquemember";
+ else if (mGroupObjectClass.equalsIgnoreCase("groupOfNames"))
+ k = "member";
+ else {
+ CMS.debug("UidPwdDirAuthentication: isMemberOfLdapGroup: unrecognized mGroupObjectClass: " + mGroupObjectClass);
+ return null;
+ }
+
+ String filter = null;
+ if (mSearchGroupUserByUserdn)
+ filter = k + "=" + userdn;
+ else
+ filter = k + "=" + mGroupUserIDName + "=" + uid;
+
+ CMS.debug(method + "searching " + getGroupBaseDN() + " for (&(objectclass=" + mGroupObjectClass + ")(" + filter + "))");
+ try {
+ LDAPSearchResults res = ldapconn.search(
+ getGroupBaseDN(),
+ LDAPv2.SCOPE_SUB,
+ "(&(objectclass=" + mGroupObjectClass + ")(" + filter + "))",
+ attrs, true /* attrsOnly */ );
+ return buildGroups(res);
+ } catch (LDAPException e) {
+ String errMsg = "listGroups()" + e.toString();
+
+ if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) {
+ errMsg = "listGroups: " + "LDAP database is unavailable";
+ }
+ CMS.debug(method + e);
+ } finally {
+ CMS.debug(method + " ends");
+ }
+ return null;
+ }
+
+ private ArrayList<String> buildGroups(LDAPSearchResults res) {
+ ArrayList<String> v = new ArrayList<>();
+
+ while (res.hasMoreElements()) {
+ LDAPEntry entry = (LDAPEntry) res.nextElement();
+ String groupDN = entry.getDN();
+ CMS.debug("UidPwdDirAuthentication: Authenticate: Found group membership: " + groupDN);
+ v.add(groupDN);
+ }
+ return v;
}
/**
@@ -131,18 +191,29 @@ public class UidPwdDirAuthentication extends DirBasedAuthentication
throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
}
+ /*
+ * first try and see if the directory server supports "memberOf"
+ * if so, use it, if not, then pull all groups to check
+ */
+ String emptyAttrs[] = {};
+ String groupAttrs[] = {"memberOf"};
+
// get user dn.
- CMS.debug("Authenticating: Searching for UID=" + uid +
- " base DN=" + mBaseDN);
- LDAPSearchResults res = conn.search(mBaseDN,
- LDAPv2.SCOPE_SUB, "(uid=" + uid + ")", null, false);
+ CMS.debug("UidPwdDirAuthentication: Authenticating: Searching for " +
+ mUserIDName + "=" + uid + " base DN=" + mBaseDN);
+ LDAPSearchResults res = conn.search(
+ mBaseDN,
+ LDAPv2.SCOPE_SUB,
+ "(" + mUserIDName + "=" + uid + ")",
+ (mGroupsEnable ? groupAttrs : emptyAttrs),
+ false);
+ LDAPEntry entry = null;
if (res.hasMoreElements()) {
- //LDAPEntry entry = (LDAPEntry)res.nextElement();
- LDAPEntry entry = res.next();
+ entry = res.next();
userdn = entry.getDN();
- CMS.debug("Authenticating: Found User DN=" + userdn);
+ CMS.debug("UidPwdDirAuthentication: Authenticating: Found User DN=" + userdn);
} else {
log(ILogger.LL_SECURITY, CMS.getLogMessage("CMS_AUTH_USER_NOT_EXIST", uid));
throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
@@ -150,9 +221,29 @@ public class UidPwdDirAuthentication extends DirBasedAuthentication
// bind as user dn and pwd - authenticates user with pwd.
conn.authenticate(userdn, pwd);
+
+ LDAPAttribute attribute = entry.getAttribute("memberOf");
+ if ( attribute != null ) {
+ CMS.debug("UidPwdDirAuthentication: Authenticate: Found memberOf attribute");
+ String[] groups = attribute.getStringValueArray();
+ token.set(IAuthToken.GROUPS, groups);
+ } else if (mGroupsEnable) {
+ CMS.debug("UidPwdDirAuthentication: Authenticate: memberOf attribute not found.");
+ ArrayList<String> groups = null;
+ try {
+ groups = listGroups(conn, uid, userdn);
+ } catch (Exception ex) {
+ CMS.debug("UidPwdDirAuthentication: authenticate: failed listGroups():" + ex + ". ok to continue");
+ }
+ if (groups != null) {
+ String[] groupsArray = new String[groups.size()];
+ token.set(IAuthToken.GROUPS, groups.toArray(groupsArray));
+ }
+ }
+
// set uid in the token.
- token.set(CRED_UID, uid);
- token.set(USERID, uid);
+ token.set(IAuthToken.UID, uid);
+ token.set(IAuthToken.USER_ID, uid);
return userdn;
} catch (ELdapException e) {
diff --git a/base/server/cms/src/com/netscape/cms/evaluators/GroupAccessEvaluator.java b/base/server/cms/src/com/netscape/cms/evaluators/GroupAccessEvaluator.java
index e8a32d752be327bf8cbb4c45d5717f84900fbc4a..9d3cb0e9725a795c06110a2e308f1fc98f3b2238 100644
--- a/base/server/cms/src/com/netscape/cms/evaluators/GroupAccessEvaluator.java
+++ b/base/server/cms/src/com/netscape/cms/evaluators/GroupAccessEvaluator.java
@@ -17,6 +17,7 @@
// --- END COPYRIGHT BLOCK ---
package com.netscape.cms.evaluators;
+import java.util.ArrayList;
import com.netscape.certsrv.apps.CMS;
import com.netscape.certsrv.authentication.IAuthToken;
import com.netscape.certsrv.base.EBaseException;
@@ -101,9 +102,9 @@ public class GroupAccessEvaluator implements IAccessEvaluator {
// should define "uid" at a common place
String uid = null;
- uid = authToken.getInString("userid");
+ uid = authToken.getInString(IAuthToken.USER_ID);
if (uid == null) {
- uid = authToken.getInString("uid");
+ uid = authToken.getInString(IAuthToken.UID);
if (uid == null) {
CMS.debug("GroupAccessEvaluator: evaluate: uid null");
log(ILogger.LL_FAILURE, CMS.getLogMessage("EVALUTOR_UID_NULL"));
@@ -112,15 +113,29 @@ public class GroupAccessEvaluator implements IAccessEvaluator {
}
CMS.debug("GroupAccessEvaluator: evaluate: uid=" + uid + " value=" + value);
- String groupname = authToken.getInString("gid");
+ ArrayList<String> groups = new ArrayList<>();
- if (groupname != null) {
- CMS.debug("GroupAccessEvaluator: evaluate: authToken gid=" + groupname);
- if (op.equals("=")) {
- return groupname.equals(Utils.stripQuotes(value));
- } else if (op.equals("!=")) {
- return !groupname.equals(Utils.stripQuotes(value));
+ String gid = authToken.getInString(IAuthToken.GID);
+ if (gid != null)
+ groups.add(gid);
+
+ String[] groupsArray = authToken.getInStringArray(IAuthToken.GROUPS);
+ if (groupsArray != null) {
+ for (int i = 0; i < groupsArray.length; i++)
+ groups.add(groupsArray[i]);
+ }
+
+ if (groups.size() > 0) {
+ boolean matched = false;
+ for (String group : groups) {
+ CMS.debug("GroupAccessEvaluator: evaluate: authToken gid=" + group);
+ if (group.equals(Utils.stripQuotes(value)))
+ matched = true;
}
+ if (op.equals("="))
+ return matched;
+ else if (op.equals("!="))
+ return !matched;
} else {
CMS.debug("GroupAccessEvaluator: evaluate: no gid in authToken");
IUser id = null;
--
2.1.0
More information about the Pki-devel
mailing list