diff --git a/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java b/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java index c09c5b63aa..2fc65fa09f 100644 --- a/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java +++ b/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java @@ -32,10 +32,12 @@ import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.Panel; import com.google.gwt.user.client.ui.SuggestBox; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter; +import com.google.gwt.user.client.ui.HTMLTable.CellFormatter; import com.google.gwtexpui.globalkey.client.NpTextArea; import com.google.gwtexpui.globalkey.client.NpTextBox; import com.google.gwtjsonrpc.client.VoidResult; @@ -62,6 +64,9 @@ public class AccountGroupScreen extends AccountScreen { private AddMemberBox addMemberBox; private Button delMember; + private Panel realmProperties; + private Grid realmPropertiesTable; + public AccountGroupScreen(final AccountGroup.Id toShow) { groupId = toShow; } @@ -69,13 +74,13 @@ public class AccountGroupScreen extends AccountScreen { @Override protected void onLoad() { super.onLoad(); - Util.GROUP_SVC.groupDetail(groupId, - new ScreenLoadCallback(this) { - @Override - protected void preDisplay(final GroupDetail result) { - display(result); - } - }); + Util.GROUP_SVC.groupDetail(groupId, new ScreenLoadCallback( + this) { + @Override + protected void preDisplay(final GroupDetail result) { + display(result); + } + }); } @Override @@ -84,6 +89,7 @@ public class AccountGroupScreen extends AccountScreen { initName(); initOwner(); initDescription(); + initRealmProperties(); initMemberList(); } @@ -174,6 +180,17 @@ public class AccountGroupScreen extends AccountScreen { new TextSaveButtonListener(descTxt, saveDesc); } + private void initRealmProperties() { + realmPropertiesTable = new Grid(0, 0); + realmPropertiesTable.setStyleName("gerrit-InfoBlock"); + + realmProperties = new FlowPanel(); + realmProperties.add(new SmallHeading(Util.C.headingGroupProperties())); + realmProperties.add(realmPropertiesTable); + + add(realmProperties); + } + private void initMemberList() { addMemberBox = new AddMemberBox(); @@ -212,6 +229,25 @@ public class AccountGroupScreen extends AccountScreen { ownerTxt.setText(Util.M.deletedGroup(group.getOwnerGroupId().get())); } descTxt.setText(group.getDescription()); + + final List propertyList = result.realmProperties; + if (!propertyList.isEmpty()) { + final int cnt = propertyList.size(); + final CellFormatter fmt = realmPropertiesTable.getCellFormatter(); + realmProperties.setVisible(true); + realmPropertiesTable.resize(cnt, 2); + for (int i = 0; i < cnt; i++) { + fmt.addStyleName(i, 0, "header"); + realmPropertiesTable.setText(i, 0, propertyList.get(i).name); + realmPropertiesTable.setText(i, 1, propertyList.get(i).value); + } + fmt.addStyleName(0, 0, "topmost"); + fmt.addStyleName(0, 1, "topmost"); + fmt.addStyleName(cnt - 1, 0, "bottomheader"); + } else { + realmProperties.setVisible(false); + } + if (group.isAutomaticMembership()) { memberPanel.setVisible(false); } else { diff --git a/src/main/java/com/google/gerrit/client/admin/AdminConstants.java b/src/main/java/com/google/gerrit/client/admin/AdminConstants.java index d51ff34eac..d4d41ff501 100644 --- a/src/main/java/com/google/gerrit/client/admin/AdminConstants.java +++ b/src/main/java/com/google/gerrit/client/admin/AdminConstants.java @@ -36,6 +36,7 @@ public interface AdminConstants extends Constants { String headingOwner(); String headingDescription(); String headingSubmitType(); + String headingGroupProperties(); String headingMembers(); String headingCreateGroup(); String headingAccessRights(); diff --git a/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties b/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties index 87fea6b349..4fec723fa2 100644 --- a/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties +++ b/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties @@ -17,6 +17,7 @@ useSignedOffBy = Require keySet() { + return Collections.unmodifiableSet(atts.keySet()); + } + + @Override + public String toString() { + return atts.get("dn"); + } } } diff --git a/src/main/java/com/google/gerrit/server/ldap/LdapRealm.java b/src/main/java/com/google/gerrit/server/ldap/LdapRealm.java index d8d26dff68..f419503e31 100644 --- a/src/main/java/com/google/gerrit/server/ldap/LdapRealm.java +++ b/src/main/java/com/google/gerrit/server/ldap/LdapRealm.java @@ -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> 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>(rawGroup) { @@ -327,6 +336,67 @@ class LdapRealm implements Realm { return usernameCache.get(accountName); } + @Override + public List getProperties(final AccountGroup group) { + if (!isLdapGroup(group)) { + return Collections.emptyList(); + } + + try { + final DirContext ctx = open(); + try { + final Map params = new HashMap(); + params.put(GROUPNAME, group.getName()); + + final List props = new ArrayList(); + final List 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() { + @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(); diff --git a/src/main/java/com/google/gerrit/server/rpc/account/GroupDetailFactory.java b/src/main/java/com/google/gerrit/server/rpc/account/GroupDetailFactory.java index 93c73be790..6bb4f1bea1 100644 --- a/src/main/java/com/google/gerrit/server/rpc/account/GroupDetailFactory.java +++ b/src/main/java/com/google/gerrit/server/rpc/account/GroupDetailFactory.java @@ -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 { 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 { @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 { 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()); }