Show simple properties of an LDAP group

To help administrators verify their group configurations we now show
some basic non-repeating properties of an LDAP group on the group
page within the admin section.

Bug: GERRIT-276
Change-Id: Ia415f003964401ca4a5eb92d711ae93d936c986e
Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2009-09-03 14:31:25 -07:00
parent e04a97ffd2
commit 0760b34199
9 changed files with 187 additions and 13 deletions

View File

@@ -14,10 +14,13 @@
package com.google.gerrit.server.account;
import com.google.gerrit.client.admin.GroupDetail.RealmProperty;
import com.google.gerrit.client.reviewdb.Account;
import com.google.gerrit.client.reviewdb.AccountGroup;
import com.google.inject.Inject;
import java.util.Collections;
import java.util.List;
import java.util.Set;
public final class DefaultRealm implements Realm {
@@ -64,4 +67,9 @@ public final class DefaultRealm implements Realm {
}
return null;
}
@Override
public List<RealmProperty> getProperties(final AccountGroup group) {
return Collections.emptyList();
}
}

View File

@@ -14,9 +14,11 @@
package com.google.gerrit.server.account;
import com.google.gerrit.client.admin.GroupDetail;
import com.google.gerrit.client.reviewdb.Account;
import com.google.gerrit.client.reviewdb.AccountGroup;
import java.util.List;
import java.util.Set;
public interface Realm {
@@ -38,4 +40,7 @@ public interface Realm {
* user by that email address.
*/
public Account.Id lookup(String accountName);
/** Obtain detailed properties about this group, for display to owners. */
public List<GroupDetail.RealmProperty> getProperties(AccountGroup group);
}

View File

@@ -15,6 +15,7 @@
package com.google.gerrit.server.ldap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -29,6 +30,8 @@ import javax.naming.directory.SearchResult;
/** Supports issuing parameterized queries against an LDAP data source. */
class LdapQuery {
static final Set<String> ALL_ATTRIBUTES = null;
private final String base;
private final SearchScope searchScope;
private final String pattern;
@@ -65,8 +68,12 @@ class LdapQuery {
this.patternArgs = new String[a.size()];
a.toArray(this.patternArgs);
this.returnAttributes = new String[returnAttributes.size()];
returnAttributes.toArray(this.returnAttributes);
if (returnAttributes != null) {
this.returnAttributes = new String[returnAttributes.size()];
returnAttributes.toArray(this.returnAttributes);
} else {
this.returnAttributes = null;
}
}
String[] getParameters() {
@@ -107,10 +114,20 @@ class LdapQuery {
private final Map<String, String> atts = new HashMap<String, String>();
Result(final SearchResult sr) throws NamingException {
for (final String attName : returnAttributes) {
final Attribute a = sr.getAttributes().get(attName);
if (a != null && a.size() > 0) {
atts.put(attName, String.valueOf(a.get(0)));
if (returnAttributes != null) {
for (final String attName : returnAttributes) {
final Attribute a = sr.getAttributes().get(attName);
if (a != null && a.size() > 0) {
atts.put(attName, String.valueOf(a.get(0)));
}
}
} else {
NamingEnumeration<? extends Attribute> e = sr.getAttributes().getAll();
while (e.hasMoreElements()) {
final Attribute a = e.nextElement();
if (a.size() == 1) {
atts.put(a.getID(), String.valueOf(a.get(0)));
}
}
}
atts.put("dn", sr.getNameInNamespace());
@@ -119,5 +136,14 @@ class LdapQuery {
String get(final String attName) {
return atts.get(attName);
}
Set<String> keySet() {
return Collections.unmodifiableSet(atts.keySet());
}
@Override
public String toString() {
return atts.get("dn");
}
}
}

View File

@@ -14,6 +14,7 @@
package com.google.gerrit.server.ldap;
import com.google.gerrit.client.admin.GroupDetail.RealmProperty;
import com.google.gerrit.client.reviewdb.Account;
import com.google.gerrit.client.reviewdb.AccountExternalId;
import com.google.gerrit.client.reviewdb.AccountGroup;
@@ -38,11 +39,14 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spearce.jgit.lib.Config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@@ -53,6 +57,7 @@ import javax.naming.directory.InitialDirContext;
@Singleton
class LdapRealm implements Realm {
private static final String GROUPNAME = "groupname";
private static final Logger log = LoggerFactory.getLogger(LdapRealm.class);
private static final String LDAP = "com.sun.jndi.ldap.LdapCtxFactory";
private static final String USERNAME = "username";
@@ -73,6 +78,7 @@ class LdapRealm implements Realm {
private final String groupName;
private boolean groupNeedsAccount;
private final LdapQuery groupMemberQuery;
private final LdapQuery groupByNameQuery;
private final SelfPopulatingCache<String, Set<AccountGroup.Id>> membershipCache;
@Inject
@@ -106,6 +112,9 @@ class LdapRealm implements Realm {
throw new IllegalArgumentException(
"No variables in ldap.groupMemberPattern");
}
groupByNameQuery =
new LdapQuery(groupBase, groupScope,
"(" + groupName + "=${groupname})", LdapQuery.ALL_ATTRIBUTES);
membershipCache =
new SelfPopulatingCache<String, Set<AccountGroup.Id>>(rawGroup) {
@@ -327,6 +336,67 @@ class LdapRealm implements Realm {
return usernameCache.get(accountName);
}
@Override
public List<RealmProperty> getProperties(final AccountGroup group) {
if (!isLdapGroup(group)) {
return Collections.emptyList();
}
try {
final DirContext ctx = open();
try {
final Map<String, String> params = new HashMap<String, String>();
params.put(GROUPNAME, group.getName());
final List<RealmProperty> props = new ArrayList<RealmProperty>();
final List<LdapQuery.Result> q = groupByNameQuery.query(ctx, params);
switch (q.size()) {
case 0:
log.warn("Group \"" + group.getName() + "\" not found in LDAP.");
props.add(new RealmProperty("error", "NOT FOUND"));
break;
case 1:
for (final String name : q.get(0).keySet()) {
props.add(new RealmProperty(name, q.get(0).get(name)));
}
Collections.sort(props, new Comparator<RealmProperty>() {
@Override
public int compare(final RealmProperty a, final RealmProperty b) {
int sort = classOf(a) - classOf(b);
if (sort == 0) sort = a.getName().compareTo(b.getName());
return sort;
}
private int classOf(final RealmProperty p) {
final String n = p.getName();
if ("dn".equals(n) || "distinguishedName".equals(n)) return 0;
if ("cn".equals(n)) return 1;
return 5000;
}
});
break;
default:
log.warn("Group \"" + group.getName()
+ "\" has multiple matches in LDAP: " + q);
props.add(new RealmProperty("error", "MULTIPLE MATCHES"));
break;
}
return props;
} finally {
try {
ctx.close();
} catch (NamingException e) {
log.warn("Cannot close LDAP query handle", e);
}
}
} catch (NamingException e) {
log.error("Cannot query LDAP directory for group " + group.getName(), e);
return Collections.emptyList();
}
}
private Account.Id queryForUsername(final String username) {
try {
final ReviewDb db = schema.open();

View File

@@ -23,6 +23,7 @@ import com.google.gerrit.server.account.AccountInfoCacheFactory;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.account.NoSuchGroupException;
import com.google.gerrit.server.account.Realm;
import com.google.gerrit.server.rpc.Handler;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
@@ -41,6 +42,7 @@ class GroupDetailFactory extends Handler<GroupDetail> {
private final ReviewDb db;
private final GroupControl.Factory groupControl;
private final GroupCache groupCache;
private final Realm realm;
private final AccountInfoCacheFactory aic;
private final AccountGroup.Id groupId;
@@ -49,11 +51,13 @@ class GroupDetailFactory extends Handler<GroupDetail> {
@Inject
GroupDetailFactory(final ReviewDb db,
final GroupControl.Factory groupControl, final GroupCache groupCache,
final Realm realm,
final AccountInfoCacheFactory.Factory accountInfoCacheFactory,
@Assisted final AccountGroup.Id groupId) {
this.db = db;
this.groupControl = groupControl;
this.groupCache = groupCache;
this.realm = realm;
this.aic = accountInfoCacheFactory.create();
this.groupId = groupId;
@@ -66,6 +70,7 @@ class GroupDetailFactory extends Handler<GroupDetail> {
final GroupDetail detail = new GroupDetail();
detail.setGroup(group);
detail.setOwnerGroup(groupCache.get(group.getOwnerGroupId()));
detail.setRealmProperties(realm.getProperties(group));
if (!group.isAutomaticMembership()) {
detail.setMembers(loadMembers());
}