Merge "Support to check via REST if a group is owned by the calling user"

This commit is contained in:
Shawn Pearce
2013-02-08 16:28:10 +00:00
committed by Gerrit Code Review
7 changed files with 153 additions and 28 deletions

View File

@@ -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:
=====

View File

@@ -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) {

View File

@@ -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() {
}
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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());

View File

@@ -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 {