Support recursive queries against LDAP directories

The top domain of a company might be DC=example,DC=com, while a
sub-domain may be DC=asia,DC=example,DC=com.  Assuming all users
in the company will use Gerrit, but their accounts are in different
sub-domains, recursive search under DC=example,DC=com is required.
So ldap.accountScope can be set to subtree for this case.

Change-Id: I9c6f98148b00e7c3f1e197054efcb2899f370d74
Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2009-08-25 12:25:27 -07:00
parent 42cf7191e3
commit 304ccdb540
3 changed files with 68 additions and 5 deletions

View File

@@ -514,6 +514,17 @@ server is attempted.
Root of the tree containing all user accounts. This is typically
of the form `ou=people,dc=example,dc=com`.
[[ldap.accountScope]]ldap.accountScope::
+
Scope of the search performed for accounts. Must be one of:
+
* `one`: Search only one level below accountBase, but not recursive
* `sub` or `subtree`: Search recursively below accountBase
* `base` or `object`: Search exactly accountBase; probably not desired
+
Default is `subtree` as many directories have several levels.
[[ldap.accountPattern]]ldap.accountPattern::
+
Query pattern to use when searching for a user account. This may be
@@ -572,6 +583,17 @@ Default is `uid`, a common value for most servers.
Root of the tree containing all group objects. This is typically
of the form `ou=groups,dc=example,dc=com`.
[[ldap.groupScope]]ldap.groupScope::
+
Scope of the search performed for group objects. Must be one of:
+
* `one`: Search only one level below groupBase, but not recursive
* `sub` or `subtree`: Search recursively below groupBase
* `base` or `object`: Search exactly groupBase; probably not desired
+
Default is `subtree` as many directories have several levels.
[[ldap.groupName]]ldap.groupName::
+
Name of an attribute on the group object which matches to the name

View File

@@ -29,14 +29,45 @@ import javax.naming.directory.SearchResult;
/** Supports issuing parameterized queries against an LDAP data source. */
class LdapQuery {
static enum SearchScope {
// Search only the base DN
//
OBJECT(SearchControls.OBJECT_SCOPE), //
BASE(SearchControls.OBJECT_SCOPE),
// Search all entries one level under the base DN
//
// Does not include the base DN, and does not include items below items
// under the base DN.
//
ONE(SearchControls.ONELEVEL_SCOPE),
// Search all entries under the base DN, including the base DN.
//
SUBTREE(SearchControls.SUBTREE_SCOPE), //
SUB(SearchControls.SUBTREE_SCOPE);
private final int scope;
SearchScope(final int scope) {
this.scope = scope;
}
int scope() {
return scope;
}
}
private final String base;
private final SearchScope searchScope;
private final String pattern;
private final String[] patternArgs;
private final String[] returnAttributes;
LdapQuery(final String base, final String pattern,
final Set<String> returnAttributes) {
LdapQuery(final String base, final SearchScope searchScope,
final String pattern, final Set<String> returnAttributes) {
this.base = base;
this.searchScope = searchScope;
final StringBuilder p = new StringBuilder();
final List<String> a = new ArrayList<String>(4);
@@ -76,7 +107,7 @@ class LdapQuery {
final SearchControls sc = new SearchControls();
final NamingEnumeration<SearchResult> res;
sc.setSearchScope(SearchControls.ONELEVEL_SCOPE);
sc.setSearchScope(searchScope.scope());
sc.setReturningAttributes(returnAttributes);
res = ctx.search(base, pattern, bind(params), sc);
try {

View File

@@ -26,7 +26,9 @@ import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.account.Realm;
import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.cache.SelfPopulatingCache;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.ldap.LdapQuery.SearchScope;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.SchemaFactory;
import com.google.inject.Inject;
@@ -96,9 +98,11 @@ class LdapRealm implements Realm {
groupName = reqdef(config, "groupName", "cn");
groupAtts.add(groupName);
final String groupBase = required(config, "groupBase");
final SearchScope groupScope = scope(config, "groupScope");
final String groupMemberPattern =
reqdef(config, "groupMemberPattern", "(memberUid=${username})");
groupMemberQuery = new LdapQuery(groupBase, groupMemberPattern, groupAtts);
groupMemberQuery =
new LdapQuery(groupBase, groupScope, groupMemberPattern, groupAtts);
if (groupMemberQuery.getParameters().length == 0) {
throw new IllegalArgumentException(
"No variables in ldap.groupMemberPattern");
@@ -140,9 +144,11 @@ class LdapRealm implements Realm {
}
}
final String accountBase = required(config, "accountBase");
final SearchScope accountScope = scope(config, "accountScope");
final String accountPattern =
reqdef(config, "accountPattern", "(uid=${username})");
accountQuery = new LdapQuery(accountBase, accountPattern, accountAtts);
accountQuery =
new LdapQuery(accountBase, accountScope, accountPattern, accountAtts);
if (accountQuery.getParameters().length == 0) {
throw new IllegalArgumentException("No variables in ldap.accountPattern");
}
@@ -155,6 +161,10 @@ class LdapRealm implements Realm {
};
}
private static SearchScope scope(final Config c, final String setting) {
return ConfigUtil.getEnum(c, "ldap", null, setting, SearchScope.SUBTREE);
}
private static String optional(final Config config, final String name) {
return config.getString("ldap", null, name);
}