Merge "Support to check via REST if a group is owned by the calling user"
This commit is contained in:
		@@ -11,8 +11,10 @@ SYNOPSIS
 | 
			
		||||
'ssh' -p <port> <host> 'gerrit ls-groups'
 | 
			
		||||
  [--project <NAME> | -p <NAME>]
 | 
			
		||||
  [--user <NAME> | -u <NAME>]
 | 
			
		||||
  [--owned]
 | 
			
		||||
  [--visible-to-all]
 | 
			
		||||
  [--type {internal | system}]
 | 
			
		||||
  [-q <GROUP>]
 | 
			
		||||
  [--verbose | -v]
 | 
			
		||||
 | 
			
		||||
DESCRIPTION
 | 
			
		||||
@@ -61,6 +63,11 @@ for other users.
 | 
			
		||||
+
 | 
			
		||||
This option can't be used together with the '--project' option.
 | 
			
		||||
 | 
			
		||||
--owned::
 | 
			
		||||
	Lists only the groups that are owned by the user that was specified
 | 
			
		||||
	by the `--user` option or if no user was specified the groups that
 | 
			
		||||
	are owned by the calling user.
 | 
			
		||||
 | 
			
		||||
--visible-to-all::
 | 
			
		||||
	Displays only groups that are visible to all registered users
 | 
			
		||||
	(groups that are explicitly marked as visible to all registered
 | 
			
		||||
@@ -75,6 +82,14 @@ This option can't be used together with the '--project' option.
 | 
			
		||||
`system`:: Any system defined and managed group.
 | 
			
		||||
--
 | 
			
		||||
 | 
			
		||||
-q::
 | 
			
		||||
	Group that should be inspected. The `-q` option can be specified
 | 
			
		||||
	multiple times to define several groups to be inspected. If
 | 
			
		||||
	specified the listed groups will only contain groups that were
 | 
			
		||||
	specified to be inspected. This is e.g. useful in combination with
 | 
			
		||||
	the `--owned` and `--user` options to check whether a group is
 | 
			
		||||
	owned by a user.
 | 
			
		||||
 | 
			
		||||
--verbose::
 | 
			
		||||
-v::
 | 
			
		||||
	Enable verbose output with tab-separated columns for the
 | 
			
		||||
@@ -106,6 +121,21 @@ List all groups for which any permission is set for the project
 | 
			
		||||
	Registered Users
 | 
			
		||||
=====
 | 
			
		||||
 | 
			
		||||
List all groups which are owned by the calling user:
 | 
			
		||||
=====
 | 
			
		||||
	$ ssh -p 29418 review.example.com gerrit ls-groups --owned
 | 
			
		||||
	MyProject_Committers
 | 
			
		||||
	MyProject_Verifiers
 | 
			
		||||
=====
 | 
			
		||||
 | 
			
		||||
Check if the calling user owns the group `MyProject_Committers`. If
 | 
			
		||||
`MyProject_Committers` is returned the calling user owns this group.
 | 
			
		||||
If the result is empty, the calling user doesn't own the group.
 | 
			
		||||
=====
 | 
			
		||||
	$ ssh -p 29418 review.example.com gerrit ls-groups --owned -q MyProject_Committers
 | 
			
		||||
	MyProject_Committers
 | 
			
		||||
=====
 | 
			
		||||
 | 
			
		||||
Extract the UUID of the 'Administrators' group:
 | 
			
		||||
 | 
			
		||||
=====
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,20 @@ public class GroupApi {
 | 
			
		||||
    new RestApi("/groups/").id(groupName).ifNoneMatch().put(in, cb);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Check if the current user is owner of a group */
 | 
			
		||||
  public static void isGroupOwner(String groupName, final AsyncCallback<Boolean> cb) {
 | 
			
		||||
    GroupMap.myOwned(groupName, new AsyncCallback<GroupMap>() {
 | 
			
		||||
      @Override
 | 
			
		||||
      public void onSuccess(GroupMap result) {
 | 
			
		||||
        cb.onSuccess(!result.isEmpty());
 | 
			
		||||
      }
 | 
			
		||||
      @Override
 | 
			
		||||
      public void onFailure(Throwable caught) {
 | 
			
		||||
        cb.onFailure(caught);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Rename a group */
 | 
			
		||||
  public static void renameGroup(AccountGroup.UUID group,
 | 
			
		||||
      String newName, AsyncCallback<VoidResult> cb) {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,20 +21,34 @@ import com.google.gwt.user.client.rpc.AsyncCallback;
 | 
			
		||||
/** Groups available from {@code /groups/}. */
 | 
			
		||||
public class GroupMap extends NativeMap<GroupInfo> {
 | 
			
		||||
  public static void all(AsyncCallback<GroupMap> callback) {
 | 
			
		||||
    new RestApi("/groups/")
 | 
			
		||||
        .get(NativeMap.copyKeysIntoChildren(callback));
 | 
			
		||||
    groups().get(NativeMap.copyKeysIntoChildren(callback));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static void match(String match, AsyncCallback<GroupMap> cb) {
 | 
			
		||||
    if (match == null || "".equals(match)) {
 | 
			
		||||
      all(cb);
 | 
			
		||||
    } else {
 | 
			
		||||
      new RestApi("/groups/")
 | 
			
		||||
          .addParameter("m", match)
 | 
			
		||||
          .get(NativeMap.copyKeysIntoChildren(cb));
 | 
			
		||||
      groups().addParameter("m", match).get(NativeMap.copyKeysIntoChildren(cb));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static void myOwned(AsyncCallback<GroupMap> cb) {
 | 
			
		||||
    myOwnedGroups().get(NativeMap.copyKeysIntoChildren(cb));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static void myOwned(String groupName, AsyncCallback<GroupMap> cb) {
 | 
			
		||||
    myOwnedGroups().addParameter("q", groupName).get(
 | 
			
		||||
        NativeMap.copyKeysIntoChildren(cb));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static RestApi myOwnedGroups() {
 | 
			
		||||
    return groups().addParameterTrue("owned");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static RestApi groups() {
 | 
			
		||||
    return new RestApi("/groups/");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected GroupMap() {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -23,9 +23,31 @@ import com.google.gerrit.server.CurrentUser;
 | 
			
		||||
import com.google.gerrit.server.IdentifiedUser;
 | 
			
		||||
import com.google.inject.Inject;
 | 
			
		||||
import com.google.inject.Provider;
 | 
			
		||||
import com.google.inject.Singleton;
 | 
			
		||||
 | 
			
		||||
/** Access control management for a group of accounts managed in Gerrit. */
 | 
			
		||||
public class GroupControl {
 | 
			
		||||
 | 
			
		||||
  @Singleton
 | 
			
		||||
  public static class GenericFactory {
 | 
			
		||||
    private final GroupBackend groupBackend;
 | 
			
		||||
 | 
			
		||||
    @Inject
 | 
			
		||||
    GenericFactory(final GroupBackend gb) {
 | 
			
		||||
      groupBackend = gb;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public GroupControl controlFor(final CurrentUser who,
 | 
			
		||||
        final AccountGroup.UUID groupId)
 | 
			
		||||
        throws NoSuchGroupException {
 | 
			
		||||
      final GroupDescription.Basic group = groupBackend.get(groupId);
 | 
			
		||||
      if (group == null) {
 | 
			
		||||
        throw new NoSuchGroupException(groupId);
 | 
			
		||||
      }
 | 
			
		||||
      return new GroupControl(who, group);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static class Factory {
 | 
			
		||||
    private final GroupCache groupCache;
 | 
			
		||||
    private final Provider<CurrentUser> user;
 | 
			
		||||
 
 | 
			
		||||
@@ -176,6 +176,7 @@ public class GerritGlobalModule extends FactoryModule {
 | 
			
		||||
    DynamicSet.setOf(binder(), AuthBackend.class);
 | 
			
		||||
 | 
			
		||||
    bind(GroupControl.Factory.class).in(SINGLETON);
 | 
			
		||||
    bind(GroupControl.GenericFactory.class).in(SINGLETON);
 | 
			
		||||
    factory(IncludingGroupMembership.Factory.class);
 | 
			
		||||
    bind(GroupBackend.class).to(UniversalGroupBackend.class).in(SINGLETON);
 | 
			
		||||
    DynamicSet.setOf(binder(), GroupBackend.class);
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@ import com.google.common.base.Objects;
 | 
			
		||||
import com.google.common.base.Strings;
 | 
			
		||||
import com.google.common.collect.Lists;
 | 
			
		||||
import com.google.common.collect.Maps;
 | 
			
		||||
import com.google.common.collect.Sets;
 | 
			
		||||
import com.google.gerrit.common.data.GroupDescriptions;
 | 
			
		||||
import com.google.gerrit.common.data.GroupReference;
 | 
			
		||||
import com.google.gerrit.common.errors.NoSuchGroupException;
 | 
			
		||||
@@ -56,6 +57,7 @@ public class ListGroups implements RestReadView<TopLevelResource> {
 | 
			
		||||
  protected final GroupCache groupCache;
 | 
			
		||||
 | 
			
		||||
  private final GroupControl.Factory groupControlFactory;
 | 
			
		||||
  private final GroupControl.GenericFactory genericGroupControlFactory;
 | 
			
		||||
  private final Provider<IdentifiedUser> identifiedUser;
 | 
			
		||||
  private final IdentifiedUser.GenericFactory userFactory;
 | 
			
		||||
  private final Provider<GetGroups> accountGetGroups;
 | 
			
		||||
@@ -74,17 +76,30 @@ public class ListGroups implements RestReadView<TopLevelResource> {
 | 
			
		||||
      usage = "user for which the groups should be listed")
 | 
			
		||||
  private Account.Id user;
 | 
			
		||||
 | 
			
		||||
  @Option(name = "--owned", usage = "to list only groups that are owned by the specified user"
 | 
			
		||||
      + " or by the calling user if no user was specifed")
 | 
			
		||||
  private boolean owned;
 | 
			
		||||
 | 
			
		||||
  private Set<AccountGroup.UUID> groupsToInspect = Sets.newHashSet();
 | 
			
		||||
 | 
			
		||||
  @Option(name = "-q", usage = "group to inspect")
 | 
			
		||||
  void addGroup(final AccountGroup.UUID id) {
 | 
			
		||||
    groupsToInspect.add(id);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Option(name = "-m", metaVar = "MATCH", usage = "match group substring")
 | 
			
		||||
  private String matchSubstring;
 | 
			
		||||
 | 
			
		||||
  @Inject
 | 
			
		||||
  protected ListGroups(final GroupCache groupCache,
 | 
			
		||||
      final GroupControl.Factory groupControlFactory,
 | 
			
		||||
      final GroupControl.GenericFactory genericGroupControlFactory,
 | 
			
		||||
      final Provider<IdentifiedUser> identifiedUser,
 | 
			
		||||
      final IdentifiedUser.GenericFactory userFactory,
 | 
			
		||||
      final Provider<GetGroups> accountGetGroups) {
 | 
			
		||||
    this.groupCache = groupCache;
 | 
			
		||||
    this.groupControlFactory = groupControlFactory;
 | 
			
		||||
    this.genericGroupControlFactory = genericGroupControlFactory;
 | 
			
		||||
    this.identifiedUser = identifiedUser;
 | 
			
		||||
    this.userFactory = userFactory;
 | 
			
		||||
    this.accountGetGroups = accountGetGroups;
 | 
			
		||||
@@ -115,34 +130,58 @@ public class ListGroups implements RestReadView<TopLevelResource> {
 | 
			
		||||
  public List<GroupInfo> get() throws NoSuchGroupException {
 | 
			
		||||
    List<GroupInfo> groupInfos;
 | 
			
		||||
    if (user != null) {
 | 
			
		||||
      groupInfos = accountGetGroups.get().apply(
 | 
			
		||||
          new AccountResource(userFactory.create(user)));
 | 
			
		||||
    } else {
 | 
			
		||||
      List<AccountGroup> groupList;
 | 
			
		||||
      if (!projects.isEmpty()) {
 | 
			
		||||
        Map<AccountGroup.UUID, AccountGroup> groups = Maps.newHashMap();
 | 
			
		||||
        for (final ProjectControl projectControl : projects) {
 | 
			
		||||
          final Set<GroupReference> groupsRefs = projectControl.getAllGroups();
 | 
			
		||||
          for (final GroupReference groupRef : groupsRefs) {
 | 
			
		||||
            final AccountGroup group = groupCache.get(groupRef.getUUID());
 | 
			
		||||
            if (group == null) {
 | 
			
		||||
              throw new NoSuchGroupException(groupRef.getUUID());
 | 
			
		||||
            }
 | 
			
		||||
            groups.put(group.getGroupUUID(), group);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        groupList = filterGroups(groups.values());
 | 
			
		||||
      if (owned) {
 | 
			
		||||
        groupInfos = getGroupsOwnedBy(userFactory.create(user));
 | 
			
		||||
      } else {
 | 
			
		||||
        groupList = filterGroups(groupCache.all());
 | 
			
		||||
        groupInfos = accountGetGroups.get().apply(
 | 
			
		||||
            new AccountResource(userFactory.create(user)));
 | 
			
		||||
      }
 | 
			
		||||
      groupInfos = Lists.newArrayListWithCapacity(groupList.size());
 | 
			
		||||
      for (AccountGroup group : groupList) {
 | 
			
		||||
        groupInfos.add(new GroupInfo(GroupDescriptions.forAccountGroup(group)));
 | 
			
		||||
    } else {
 | 
			
		||||
      if (owned) {
 | 
			
		||||
        groupInfos = getGroupsOwnedBy(identifiedUser.get());
 | 
			
		||||
      } else {
 | 
			
		||||
        List<AccountGroup> groupList;
 | 
			
		||||
        if (!projects.isEmpty()) {
 | 
			
		||||
          Map<AccountGroup.UUID, AccountGroup> groups = Maps.newHashMap();
 | 
			
		||||
          for (final ProjectControl projectControl : projects) {
 | 
			
		||||
            final Set<GroupReference> groupsRefs = projectControl.getAllGroups();
 | 
			
		||||
            for (final GroupReference groupRef : groupsRefs) {
 | 
			
		||||
              final AccountGroup group = groupCache.get(groupRef.getUUID());
 | 
			
		||||
              if (group == null) {
 | 
			
		||||
                throw new NoSuchGroupException(groupRef.getUUID());
 | 
			
		||||
              }
 | 
			
		||||
              groups.put(group.getGroupUUID(), group);
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          groupList = filterGroups(groups.values());
 | 
			
		||||
        } else {
 | 
			
		||||
          groupList = filterGroups(groupCache.all());
 | 
			
		||||
        }
 | 
			
		||||
        groupInfos = Lists.newArrayListWithCapacity(groupList.size());
 | 
			
		||||
        for (AccountGroup group : groupList) {
 | 
			
		||||
          groupInfos.add(new GroupInfo(GroupDescriptions.forAccountGroup(group)));
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return groupInfos;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private List<GroupInfo> getGroupsOwnedBy(IdentifiedUser user) {
 | 
			
		||||
    List<GroupInfo> groups = Lists.newArrayList();
 | 
			
		||||
    for (AccountGroup g : filterGroups(groupCache.all())) {
 | 
			
		||||
      GroupControl ctl = groupControlFactory.controlFor(g);
 | 
			
		||||
      try {
 | 
			
		||||
        if (genericGroupControlFactory.controlFor(user, g.getGroupUUID())
 | 
			
		||||
            .isOwner()) {
 | 
			
		||||
          groups.add(new GroupInfo(ctl.getGroup()));
 | 
			
		||||
        }
 | 
			
		||||
      } catch (NoSuchGroupException e) {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return groups;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private List<AccountGroup> filterGroups(final Iterable<AccountGroup> groups) {
 | 
			
		||||
    final List<AccountGroup> filteredGroups = Lists.newArrayList();
 | 
			
		||||
    final boolean isAdmin =
 | 
			
		||||
@@ -164,6 +203,10 @@ public class ListGroups implements RestReadView<TopLevelResource> {
 | 
			
		||||
          || (groupType != null && !groupType.equals(group.getType()))) {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
      if (!groupsToInspect.isEmpty()
 | 
			
		||||
          && !groupsToInspect.contains(group.getGroupUUID())) {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
      filteredGroups.add(group);
 | 
			
		||||
    }
 | 
			
		||||
    Collections.sort(filteredGroups, new GroupComparator());
 | 
			
		||||
 
 | 
			
		||||
@@ -68,11 +68,12 @@ public class ListGroupsCommand extends BaseCommand {
 | 
			
		||||
    @Inject
 | 
			
		||||
    MyListGroups(final GroupCache groupCache,
 | 
			
		||||
        final GroupControl.Factory groupControlFactory,
 | 
			
		||||
        final GroupControl.GenericFactory genericGroupControlFactory,
 | 
			
		||||
        final Provider<IdentifiedUser> identifiedUser,
 | 
			
		||||
        final IdentifiedUser.GenericFactory userFactory,
 | 
			
		||||
        final Provider<GetGroups> accountGetGroups) {
 | 
			
		||||
      super(groupCache, groupControlFactory, identifiedUser, userFactory,
 | 
			
		||||
          accountGetGroups);
 | 
			
		||||
      super(groupCache, groupControlFactory, genericGroupControlFactory,
 | 
			
		||||
          identifiedUser, userFactory, accountGetGroups);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void display(final PrintWriter out) throws NoSuchGroupException {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user