Support to check via REST if a group is owned by the calling user
It is now possible to list the groups that are owned by a user by GET on '/groups/?owned&user=<user>'. If the 'user' parameter is omitted the owned groups of the calling user are listed. Groups can be added by the 'q' parameter to limit the result to those groups that are of interest to the caller, e.g. if a user wants to check if he owns group 'MyGroup' he can send this request: GET /groups/?owned&q=MyGroup If the group is returned the user owns 'MyGroup'. Change-Id: I09bc99ce0fb60a40320a26d592257408ef2d9c75 Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
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,8 +130,15 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
||||
public List<GroupInfo> get() throws NoSuchGroupException {
|
||||
List<GroupInfo> groupInfos;
|
||||
if (user != null) {
|
||||
if (owned) {
|
||||
groupInfos = getGroupsOwnedBy(userFactory.create(user));
|
||||
} else {
|
||||
groupInfos = accountGetGroups.get().apply(
|
||||
new AccountResource(userFactory.create(user)));
|
||||
}
|
||||
} else {
|
||||
if (owned) {
|
||||
groupInfos = getGroupsOwnedBy(identifiedUser.get());
|
||||
} else {
|
||||
List<AccountGroup> groupList;
|
||||
if (!projects.isEmpty()) {
|
||||
@@ -140,9 +162,26 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
||||
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