Test GlobalCapabilities with PermissionBackend
Add GlobalPermission enum mapping the simple boolean GlobalCapabilities to be checked by PermissionBackend. Rewrite GetCapabilities handler in terms of this API. Change-Id: Ie06a4f6b18b7bdfabbaa58c6aa9becbc1d9b6136
This commit is contained in:

committed by
David Pursehouse

parent
cd84f3bb83
commit
625049c020
@@ -15,6 +15,7 @@
|
|||||||
package com.google.gerrit.common.data;
|
package com.google.gerrit.common.data;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -115,6 +116,9 @@ public class GlobalCapability {
|
|||||||
|
|
||||||
private static final List<String> NAMES_ALL;
|
private static final List<String> NAMES_ALL;
|
||||||
private static final List<String> NAMES_LC;
|
private static final List<String> NAMES_LC;
|
||||||
|
private static final String[] RANGE_NAMES = {
|
||||||
|
QUERY_LIMIT, BATCH_CHANGES_LIMIT,
|
||||||
|
};
|
||||||
|
|
||||||
static {
|
static {
|
||||||
NAMES_ALL = new ArrayList<>();
|
NAMES_ALL = new ArrayList<>();
|
||||||
@@ -158,7 +162,16 @@ public class GlobalCapability {
|
|||||||
|
|
||||||
/** @return true if the capability should have a range attached. */
|
/** @return true if the capability should have a range attached. */
|
||||||
public static boolean hasRange(String varName) {
|
public static boolean hasRange(String varName) {
|
||||||
return QUERY_LIMIT.equalsIgnoreCase(varName) || BATCH_CHANGES_LIMIT.equalsIgnoreCase(varName);
|
for (String n : RANGE_NAMES) {
|
||||||
|
if (n.equalsIgnoreCase(varName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> getRangeNames() {
|
||||||
|
return Collections.unmodifiableList(Arrays.asList(RANGE_NAMES));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the valid range for the capability if it has one, otherwise null. */
|
/** @return the valid range for the capability if it has one, otherwise null. */
|
||||||
|
@@ -26,6 +26,8 @@ import com.google.gerrit.server.CurrentUser;
|
|||||||
import com.google.gerrit.server.PeerDaemonUser;
|
import com.google.gerrit.server.PeerDaemonUser;
|
||||||
import com.google.gerrit.server.git.QueueProvider;
|
import com.google.gerrit.server.git.QueueProvider;
|
||||||
import com.google.gerrit.server.group.SystemGroupBackend;
|
import com.google.gerrit.server.group.SystemGroupBackend;
|
||||||
|
import com.google.gerrit.server.permissions.GlobalPermission;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||||
import com.google.gerrit.server.project.ProjectCache;
|
import com.google.gerrit.server.project.ProjectCache;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.assistedinject.Assisted;
|
import com.google.inject.assistedinject.Assisted;
|
||||||
@@ -75,21 +77,6 @@ public class CapabilityControl {
|
|||||||
return canAdministrateServer;
|
return canAdministrateServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return true if the user can create an account for another user. */
|
|
||||||
public boolean canCreateAccount() {
|
|
||||||
return canPerform(GlobalCapability.CREATE_ACCOUNT) || canAdministrateServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return true if the user can create a group. */
|
|
||||||
public boolean canCreateGroup() {
|
|
||||||
return canPerform(GlobalCapability.CREATE_GROUP) || canAdministrateServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return true if the user can create a project. */
|
|
||||||
public boolean canCreateProject() {
|
|
||||||
return canPerform(GlobalCapability.CREATE_PROJECT) || canAdministrateServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return true if the user can email reviewers. */
|
/** @return true if the user can email reviewers. */
|
||||||
public boolean canEmailReviewers() {
|
public boolean canEmailReviewers() {
|
||||||
if (canEmailReviewers == null) {
|
if (canEmailReviewers == null) {
|
||||||
@@ -100,11 +87,6 @@ public class CapabilityControl {
|
|||||||
return canEmailReviewers;
|
return canEmailReviewers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return true if the user can kill any running task. */
|
|
||||||
public boolean canKillTask() {
|
|
||||||
return canPerform(GlobalCapability.KILL_TASK) || canMaintainServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return true if the user can modify an account for another user. */
|
/** @return true if the user can modify an account for another user. */
|
||||||
public boolean canModifyAccount() {
|
public boolean canModifyAccount() {
|
||||||
return canPerform(GlobalCapability.MODIFY_ACCOUNT) || canAdministrateServer();
|
return canPerform(GlobalCapability.MODIFY_ACCOUNT) || canAdministrateServer();
|
||||||
@@ -120,26 +102,11 @@ public class CapabilityControl {
|
|||||||
return canPerform(GlobalCapability.VIEW_CACHES) || canMaintainServer();
|
return canPerform(GlobalCapability.VIEW_CACHES) || canMaintainServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return true if the user can flush the server's caches. */
|
|
||||||
public boolean canFlushCaches() {
|
|
||||||
return canPerform(GlobalCapability.FLUSH_CACHES) || canMaintainServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return true if the user can perform basic server maintenance. */
|
/** @return true if the user can perform basic server maintenance. */
|
||||||
public boolean canMaintainServer() {
|
public boolean canMaintainServer() {
|
||||||
return canPerform(GlobalCapability.MAINTAIN_SERVER) || canAdministrateServer();
|
return canPerform(GlobalCapability.MAINTAIN_SERVER) || canAdministrateServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return true if the user can view open connections. */
|
|
||||||
public boolean canViewConnections() {
|
|
||||||
return canPerform(GlobalCapability.VIEW_CONNECTIONS) || canAdministrateServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return true if the user can view the installed plugins. */
|
|
||||||
public boolean canViewPlugins() {
|
|
||||||
return canPerform(GlobalCapability.VIEW_PLUGINS) || canAdministrateServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return true if the user can view the entire queue. */
|
/** @return true if the user can view the entire queue. */
|
||||||
public boolean canViewQueue() {
|
public boolean canViewQueue() {
|
||||||
return canPerform(GlobalCapability.VIEW_QUEUE) || canMaintainServer();
|
return canPerform(GlobalCapability.VIEW_QUEUE) || canMaintainServer();
|
||||||
@@ -150,16 +117,6 @@ public class CapabilityControl {
|
|||||||
return canPerform(GlobalCapability.ACCESS_DATABASE);
|
return canPerform(GlobalCapability.ACCESS_DATABASE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return true if the user can stream Gerrit events. */
|
|
||||||
public boolean canStreamEvents() {
|
|
||||||
return canPerform(GlobalCapability.STREAM_EVENTS) || canAdministrateServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return true if the user can run the Git garbage collection. */
|
|
||||||
public boolean canRunGC() {
|
|
||||||
return canPerform(GlobalCapability.RUN_GC) || canMaintainServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return true if the user can impersonate another user. */
|
/** @return true if the user can impersonate another user. */
|
||||||
public boolean canRunAs() {
|
public boolean canRunAs() {
|
||||||
return canPerform(GlobalCapability.RUN_AS);
|
return canPerform(GlobalCapability.RUN_AS);
|
||||||
@@ -278,4 +235,43 @@ public class CapabilityControl {
|
|||||||
private static boolean match(GroupMembership groups, PermissionRule rule) {
|
private static boolean match(GroupMembership groups, PermissionRule rule) {
|
||||||
return groups.contains(rule.getGroup().getUUID());
|
return groups.contains(rule.getGroup().getUUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Do not use unless inside DefaultPermissionBackend. */
|
||||||
|
public boolean doCanForDefaultPermissionBackend(GlobalPermission perm)
|
||||||
|
throws PermissionBackendException {
|
||||||
|
switch (perm) {
|
||||||
|
case ACCESS_DATABASE:
|
||||||
|
return canAccessDatabase();
|
||||||
|
case ADMINISTRATE_SERVER:
|
||||||
|
return canAdministrateServer();
|
||||||
|
case EMAIL_REVIEWERS:
|
||||||
|
return canEmailReviewers();
|
||||||
|
case MAINTAIN_SERVER:
|
||||||
|
return canMaintainServer();
|
||||||
|
case MODIFY_ACCOUNT:
|
||||||
|
return canModifyAccount();
|
||||||
|
case RUN_AS:
|
||||||
|
return canRunAs();
|
||||||
|
case VIEW_ALL_ACCOUNTS:
|
||||||
|
return canViewAllAccounts();
|
||||||
|
case VIEW_CACHES:
|
||||||
|
return canViewCaches();
|
||||||
|
case VIEW_QUEUE:
|
||||||
|
return canViewQueue();
|
||||||
|
|
||||||
|
case FLUSH_CACHES:
|
||||||
|
case KILL_TASK:
|
||||||
|
case RUN_GC:
|
||||||
|
return canPerform(perm.permissionName()) || canMaintainServer();
|
||||||
|
|
||||||
|
case CREATE_ACCOUNT:
|
||||||
|
case CREATE_GROUP:
|
||||||
|
case CREATE_PROJECT:
|
||||||
|
case STREAM_EVENTS:
|
||||||
|
case VIEW_CONNECTIONS:
|
||||||
|
case VIEW_PLUGINS:
|
||||||
|
return canPerform(perm.permissionName()) || canAdministrateServer();
|
||||||
|
}
|
||||||
|
throw new PermissionBackendException(perm + " unsupported");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,23 +14,7 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.account;
|
package com.google.gerrit.server.account;
|
||||||
|
|
||||||
import static com.google.gerrit.common.data.GlobalCapability.ACCESS_DATABASE;
|
|
||||||
import static com.google.gerrit.common.data.GlobalCapability.CREATE_ACCOUNT;
|
|
||||||
import static com.google.gerrit.common.data.GlobalCapability.CREATE_GROUP;
|
|
||||||
import static com.google.gerrit.common.data.GlobalCapability.CREATE_PROJECT;
|
|
||||||
import static com.google.gerrit.common.data.GlobalCapability.EMAIL_REVIEWERS;
|
|
||||||
import static com.google.gerrit.common.data.GlobalCapability.FLUSH_CACHES;
|
|
||||||
import static com.google.gerrit.common.data.GlobalCapability.KILL_TASK;
|
|
||||||
import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
|
|
||||||
import static com.google.gerrit.common.data.GlobalCapability.MODIFY_ACCOUNT;
|
|
||||||
import static com.google.gerrit.common.data.GlobalCapability.PRIORITY;
|
import static com.google.gerrit.common.data.GlobalCapability.PRIORITY;
|
||||||
import static com.google.gerrit.common.data.GlobalCapability.RUN_GC;
|
|
||||||
import static com.google.gerrit.common.data.GlobalCapability.STREAM_EVENTS;
|
|
||||||
import static com.google.gerrit.common.data.GlobalCapability.VIEW_ALL_ACCOUNTS;
|
|
||||||
import static com.google.gerrit.common.data.GlobalCapability.VIEW_CACHES;
|
|
||||||
import static com.google.gerrit.common.data.GlobalCapability.VIEW_CONNECTIONS;
|
|
||||||
import static com.google.gerrit.common.data.GlobalCapability.VIEW_PLUGINS;
|
|
||||||
import static com.google.gerrit.common.data.GlobalCapability.VIEW_QUEUE;
|
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.gerrit.common.data.GlobalCapability;
|
import com.google.gerrit.common.data.GlobalCapability;
|
||||||
@@ -45,12 +29,15 @@ import com.google.gerrit.server.OptionUtil;
|
|||||||
import com.google.gerrit.server.OutputFormat;
|
import com.google.gerrit.server.OutputFormat;
|
||||||
import com.google.gerrit.server.account.AccountResource.Capability;
|
import com.google.gerrit.server.account.AccountResource.Capability;
|
||||||
import com.google.gerrit.server.git.QueueProvider;
|
import com.google.gerrit.server.git.QueueProvider;
|
||||||
|
import com.google.gerrit.server.permissions.GlobalPermission;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
import java.util.EnumSet;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -67,34 +54,72 @@ class GetCapabilities implements RestReadView<AccountResource> {
|
|||||||
|
|
||||||
private Set<String> query;
|
private Set<String> query;
|
||||||
|
|
||||||
|
private final PermissionBackend permissionBackend;
|
||||||
private final Provider<CurrentUser> self;
|
private final Provider<CurrentUser> self;
|
||||||
private final DynamicMap<CapabilityDefinition> pluginCapabilities;
|
private final DynamicMap<CapabilityDefinition> pluginCapabilities;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
GetCapabilities(Provider<CurrentUser> self, DynamicMap<CapabilityDefinition> pluginCapabilities) {
|
GetCapabilities(
|
||||||
|
PermissionBackend permissionBackend,
|
||||||
|
Provider<CurrentUser> self,
|
||||||
|
DynamicMap<CapabilityDefinition> pluginCapabilities) {
|
||||||
|
this.permissionBackend = permissionBackend;
|
||||||
this.self = self;
|
this.self = self;
|
||||||
this.pluginCapabilities = pluginCapabilities;
|
this.pluginCapabilities = pluginCapabilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object apply(AccountResource resource) throws AuthException {
|
public Object apply(AccountResource rsrc) throws AuthException, PermissionBackendException {
|
||||||
if (self.get() != resource.getUser() && !self.get().getCapabilities().canAdministrateServer()) {
|
PermissionBackend.WithUser perm = permissionBackend.user(self);
|
||||||
throw new AuthException("restricted to administrator");
|
if (self.get() != rsrc.getUser()) {
|
||||||
|
perm.check(GlobalPermission.ADMINISTRATE_SERVER);
|
||||||
|
perm = permissionBackend.user(rsrc.getUser());
|
||||||
}
|
}
|
||||||
|
|
||||||
CapabilityControl cc = resource.getUser().getCapabilities();
|
|
||||||
Map<String, Object> have = new LinkedHashMap<>();
|
Map<String, Object> have = new LinkedHashMap<>();
|
||||||
for (String name : GlobalCapability.getAllNames()) {
|
for (GlobalPermission p : testGlobalPermissions(perm)) {
|
||||||
if (want(name)) {
|
have.put(p.permissionName(), true);
|
||||||
if (GlobalCapability.hasRange(name)) {
|
}
|
||||||
if (cc.hasExplicitRange(name)) {
|
addRanges(have, rsrc);
|
||||||
have.put(name, new Range(cc.getRange(name)));
|
addPluginCapabilities(have, rsrc);
|
||||||
}
|
addPriority(have, rsrc);
|
||||||
} else if (!name.equals(PRIORITY) && cc.canPerform(name)) {
|
|
||||||
have.put(name, true);
|
return OutputFormat.JSON
|
||||||
|
.newGson()
|
||||||
|
.toJsonTree(have, new TypeToken<Map<String, Object>>() {}.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<GlobalPermission> testGlobalPermissions(PermissionBackend.WithUser perm)
|
||||||
|
throws PermissionBackendException {
|
||||||
|
EnumSet<GlobalPermission> toTest;
|
||||||
|
if (query != null) {
|
||||||
|
toTest = EnumSet.noneOf(GlobalPermission.class);
|
||||||
|
for (GlobalPermission p : GlobalPermission.values()) {
|
||||||
|
if (want(p.permissionName())) {
|
||||||
|
toTest.add(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
toTest = EnumSet.allOf(GlobalPermission.class);
|
||||||
}
|
}
|
||||||
|
return perm.test(toTest);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean want(String name) {
|
||||||
|
return query == null || query.contains(name.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRanges(Map<String, Object> have, AccountResource rsrc) {
|
||||||
|
CapabilityControl cc = rsrc.getUser().getCapabilities();
|
||||||
|
for (String name : GlobalCapability.getRangeNames()) {
|
||||||
|
if (want(name) && cc.hasExplicitRange(name)) {
|
||||||
|
have.put(name, new Range(cc.getRange(name)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addPluginCapabilities(Map<String, Object> have, AccountResource rsrc) {
|
||||||
|
CapabilityControl cc = rsrc.getUser().getCapabilities();
|
||||||
for (String pluginName : pluginCapabilities.plugins()) {
|
for (String pluginName : pluginCapabilities.plugins()) {
|
||||||
for (String capability : pluginCapabilities.byPlugin(pluginName).keySet()) {
|
for (String capability : pluginCapabilities.byPlugin(pluginName).keySet()) {
|
||||||
String name = String.format("%s-%s", pluginName, capability);
|
String name = String.format("%s-%s", pluginName, capability);
|
||||||
@@ -103,47 +128,14 @@ class GetCapabilities implements RestReadView<AccountResource> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
have.put(ACCESS_DATABASE, cc.canAccessDatabase());
|
private void addPriority(Map<String, Object> have, AccountResource rsrc) {
|
||||||
have.put(CREATE_ACCOUNT, cc.canCreateAccount());
|
QueueProvider.QueueType queue = rsrc.getUser().getCapabilities().getQueueType();
|
||||||
have.put(CREATE_GROUP, cc.canCreateGroup());
|
|
||||||
have.put(CREATE_PROJECT, cc.canCreateProject());
|
|
||||||
have.put(EMAIL_REVIEWERS, cc.canEmailReviewers());
|
|
||||||
have.put(FLUSH_CACHES, cc.canFlushCaches());
|
|
||||||
have.put(KILL_TASK, cc.canKillTask());
|
|
||||||
have.put(MAINTAIN_SERVER, cc.canMaintainServer());
|
|
||||||
have.put(MODIFY_ACCOUNT, cc.canModifyAccount());
|
|
||||||
have.put(RUN_GC, cc.canRunGC());
|
|
||||||
have.put(STREAM_EVENTS, cc.canStreamEvents());
|
|
||||||
have.put(VIEW_ALL_ACCOUNTS, cc.canViewAllAccounts());
|
|
||||||
have.put(VIEW_CACHES, cc.canViewCaches());
|
|
||||||
have.put(VIEW_CONNECTIONS, cc.canViewConnections());
|
|
||||||
have.put(VIEW_PLUGINS, cc.canViewPlugins());
|
|
||||||
have.put(VIEW_QUEUE, cc.canViewQueue());
|
|
||||||
|
|
||||||
QueueProvider.QueueType queue = cc.getQueueType();
|
|
||||||
if (queue != QueueProvider.QueueType.INTERACTIVE
|
if (queue != QueueProvider.QueueType.INTERACTIVE
|
||||||
|| (query != null && query.contains(PRIORITY))) {
|
|| (query != null && query.contains(PRIORITY))) {
|
||||||
have.put(PRIORITY, queue);
|
have.put(PRIORITY, queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator<Map.Entry<String, Object>> itr = have.entrySet().iterator();
|
|
||||||
while (itr.hasNext()) {
|
|
||||||
Map.Entry<String, Object> e = itr.next();
|
|
||||||
if (!want(e.getKey())) {
|
|
||||||
itr.remove();
|
|
||||||
} else if (e.getValue() instanceof Boolean && !((Boolean) e.getValue())) {
|
|
||||||
itr.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return OutputFormat.JSON
|
|
||||||
.newGson()
|
|
||||||
.toJsonTree(have, new TypeToken<Map<String, Object>>() {}.getType());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean want(String name) {
|
|
||||||
return query == null || query.contains(name.toLowerCase());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Range {
|
private static class Range {
|
||||||
|
@@ -0,0 +1,54 @@
|
|||||||
|
// Copyright (C) 2017 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package com.google.gerrit.server.permissions;
|
||||||
|
|
||||||
|
import com.google.gerrit.common.data.GlobalCapability;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public enum GlobalPermission {
|
||||||
|
ACCESS_DATABASE(GlobalCapability.ACCESS_DATABASE),
|
||||||
|
ADMINISTRATE_SERVER(GlobalCapability.ADMINISTRATE_SERVER),
|
||||||
|
CREATE_ACCOUNT(GlobalCapability.CREATE_ACCOUNT),
|
||||||
|
CREATE_GROUP(GlobalCapability.CREATE_GROUP),
|
||||||
|
CREATE_PROJECT(GlobalCapability.CREATE_PROJECT),
|
||||||
|
EMAIL_REVIEWERS(GlobalCapability.EMAIL_REVIEWERS),
|
||||||
|
FLUSH_CACHES(GlobalCapability.FLUSH_CACHES),
|
||||||
|
KILL_TASK(GlobalCapability.KILL_TASK),
|
||||||
|
MAINTAIN_SERVER(GlobalCapability.MAINTAIN_SERVER),
|
||||||
|
MODIFY_ACCOUNT(GlobalCapability.MODIFY_ACCOUNT),
|
||||||
|
RUN_AS(GlobalCapability.RUN_AS),
|
||||||
|
RUN_GC(GlobalCapability.RUN_GC),
|
||||||
|
STREAM_EVENTS(GlobalCapability.STREAM_EVENTS),
|
||||||
|
VIEW_ALL_ACCOUNTS(GlobalCapability.VIEW_ALL_ACCOUNTS),
|
||||||
|
VIEW_CACHES(GlobalCapability.VIEW_CACHES),
|
||||||
|
VIEW_CONNECTIONS(GlobalCapability.VIEW_CONNECTIONS),
|
||||||
|
VIEW_PLUGINS(GlobalCapability.VIEW_PLUGINS),
|
||||||
|
VIEW_QUEUE(GlobalCapability.VIEW_QUEUE);
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
GlobalPermission(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return name used in {@code project.config} permissions. */
|
||||||
|
public String permissionName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String describeForException() {
|
||||||
|
return toString().toLowerCase(Locale.US).replace('_', ' ');
|
||||||
|
}
|
||||||
|
}
|
@@ -36,10 +36,7 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks authorization to perform an action on project, ref, or change.
|
* Checks authorization to perform an action on a project, reference, or change.
|
||||||
*
|
|
||||||
* <p>{@code PermissionBackend} should be a singleton for the server, acting as a factory for
|
|
||||||
* lightweight request instances.
|
|
||||||
*
|
*
|
||||||
* <p>{@code check} methods should be used during action handlers to verify the user is allowed to
|
* <p>{@code check} methods should be used during action handlers to verify the user is allowed to
|
||||||
* exercise the specified permission. For convenience in implementation {@code check} methods throw
|
* exercise the specified permission. For convenience in implementation {@code check} methods throw
|
||||||
@@ -50,6 +47,13 @@ import org.slf4j.LoggerFactory;
|
|||||||
* permission. This is suitable for configuring UI button state, but should not be relied upon to
|
* permission. This is suitable for configuring UI button state, but should not be relied upon to
|
||||||
* guard handlers before making state changes.
|
* guard handlers before making state changes.
|
||||||
*
|
*
|
||||||
|
* <p>{@code PermissionBackend} is a singleton for the server, acting as a factory for lightweight
|
||||||
|
* request instances. Implementation classes may cache supporting data inside of {@link WithUser},
|
||||||
|
* {@link ForProject}, {@link ForRef}, and {@link ForChange} instances, in addition to storing
|
||||||
|
* within {@link CurrentUser} using a {@link com.google.gerrit.server.CurrentUser.PropertyKey}.
|
||||||
|
* {@link GlobalPermission} caching for {@link WithUser} may best cached inside {@link CurrentUser}
|
||||||
|
* as {@link WithUser} instances are frequently created.
|
||||||
|
*
|
||||||
* <p>Example use:
|
* <p>Example use:
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
@@ -128,6 +132,27 @@ public abstract class PermissionBackend {
|
|||||||
public ForChange change(ChangeNotes notes) {
|
public ForChange change(ChangeNotes notes) {
|
||||||
return ref(notes.getChange().getDest()).change(notes);
|
return ref(notes.getChange().getDest()).change(notes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Verify scoped user can {@code perm}, throwing if denied. */
|
||||||
|
public abstract void check(GlobalPermission perm)
|
||||||
|
throws AuthException, PermissionBackendException;
|
||||||
|
|
||||||
|
/** Filter {@code permSet} to permissions scoped user might be able to perform. */
|
||||||
|
public abstract Set<GlobalPermission> test(Collection<GlobalPermission> permSet)
|
||||||
|
throws PermissionBackendException;
|
||||||
|
|
||||||
|
public boolean test(GlobalPermission perm) throws PermissionBackendException {
|
||||||
|
return test(EnumSet.of(perm)).contains(perm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean testOrFalse(GlobalPermission perm) {
|
||||||
|
try {
|
||||||
|
return test(perm);
|
||||||
|
} catch (PermissionBackendException e) {
|
||||||
|
logger.warn("Cannot test " + perm + "; assuming false", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** PermissionBackend scoped to a user and project. */
|
/** PermissionBackend scoped to a user and project. */
|
||||||
|
@@ -16,13 +16,19 @@ package com.google.gerrit.server.project;
|
|||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.gerrit.extensions.restapi.AuthException;
|
||||||
import com.google.gerrit.reviewdb.client.Project;
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
import com.google.gerrit.server.permissions.FailedPermissionBackend;
|
import com.google.gerrit.server.permissions.FailedPermissionBackend;
|
||||||
|
import com.google.gerrit.server.permissions.GlobalPermission;
|
||||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class DefaultPermissionBackend extends PermissionBackend {
|
class DefaultPermissionBackend extends PermissionBackend {
|
||||||
@@ -57,5 +63,28 @@ class DefaultPermissionBackend extends PermissionBackend {
|
|||||||
return FailedPermissionBackend.project("unavailable", e);
|
return FailedPermissionBackend.project("unavailable", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void check(GlobalPermission perm) throws AuthException, PermissionBackendException {
|
||||||
|
if (!can(perm)) {
|
||||||
|
throw new AuthException(perm.describeForException() + " not permitted");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<GlobalPermission> test(Collection<GlobalPermission> permSet)
|
||||||
|
throws PermissionBackendException {
|
||||||
|
EnumSet<GlobalPermission> ok = EnumSet.noneOf(GlobalPermission.class);
|
||||||
|
for (GlobalPermission perm : permSet) {
|
||||||
|
if (can(perm)) {
|
||||||
|
ok.add(perm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean can(GlobalPermission perm) throws PermissionBackendException {
|
||||||
|
return user.getCapabilities().doCanForDefaultPermissionBackend(perm);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user