Merge changes from topic 'permission-backend'
* changes: Convert RequireCapability checks to PermissionBackend Change capabilities collection to parse using PermissionBackend
This commit is contained in:
@@ -83,10 +83,7 @@ public class GarbageCollectionIT extends AbstractDaemonTest {
|
|||||||
assertThat(userSshSession.hasError()).isTrue();
|
assertThat(userSshSession.hasError()).isTrue();
|
||||||
String error = userSshSession.getError();
|
String error = userSshSession.getError();
|
||||||
assertThat(error).isNotNull();
|
assertThat(error).isNotNull();
|
||||||
assertError(
|
assertError("maintain server not permitted", error);
|
||||||
"One of the following capabilities is required to access this"
|
|
||||||
+ " resource: [runGC, maintainServer]",
|
|
||||||
error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@@ -95,8 +95,10 @@ import com.google.gerrit.server.AnonymousUser;
|
|||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
import com.google.gerrit.server.OptionUtil;
|
import com.google.gerrit.server.OptionUtil;
|
||||||
import com.google.gerrit.server.OutputFormat;
|
import com.google.gerrit.server.OutputFormat;
|
||||||
import com.google.gerrit.server.account.CapabilityUtils;
|
|
||||||
import com.google.gerrit.server.config.GerritServerConfig;
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
|
import com.google.gerrit.server.permissions.GlobalPermission;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||||
import com.google.gerrit.util.http.RequestUtil;
|
import com.google.gerrit.util.http.RequestUtil;
|
||||||
import com.google.gson.ExclusionStrategy;
|
import com.google.gson.ExclusionStrategy;
|
||||||
import com.google.gson.FieldAttributes;
|
import com.google.gson.FieldAttributes;
|
||||||
@@ -188,6 +190,7 @@ public class RestApiServlet extends HttpServlet {
|
|||||||
final Provider<CurrentUser> currentUser;
|
final Provider<CurrentUser> currentUser;
|
||||||
final DynamicItem<WebSession> webSession;
|
final DynamicItem<WebSession> webSession;
|
||||||
final Provider<ParameterParser> paramParser;
|
final Provider<ParameterParser> paramParser;
|
||||||
|
final PermissionBackend permissionBackend;
|
||||||
final AuditService auditService;
|
final AuditService auditService;
|
||||||
final RestApiMetrics metrics;
|
final RestApiMetrics metrics;
|
||||||
final Pattern allowOrigin;
|
final Pattern allowOrigin;
|
||||||
@@ -197,12 +200,14 @@ public class RestApiServlet extends HttpServlet {
|
|||||||
Provider<CurrentUser> currentUser,
|
Provider<CurrentUser> currentUser,
|
||||||
DynamicItem<WebSession> webSession,
|
DynamicItem<WebSession> webSession,
|
||||||
Provider<ParameterParser> paramParser,
|
Provider<ParameterParser> paramParser,
|
||||||
|
PermissionBackend permissionBackend,
|
||||||
AuditService auditService,
|
AuditService auditService,
|
||||||
RestApiMetrics metrics,
|
RestApiMetrics metrics,
|
||||||
@GerritServerConfig Config cfg) {
|
@GerritServerConfig Config cfg) {
|
||||||
this.currentUser = currentUser;
|
this.currentUser = currentUser;
|
||||||
this.webSession = webSession;
|
this.webSession = webSession;
|
||||||
this.paramParser = paramParser;
|
this.paramParser = paramParser;
|
||||||
|
this.permissionBackend = permissionBackend;
|
||||||
this.auditService = auditService;
|
this.auditService = auditService;
|
||||||
this.metrics = metrics;
|
this.metrics = metrics;
|
||||||
allowOrigin = makeAllowOrigin(cfg);
|
allowOrigin = makeAllowOrigin(cfg);
|
||||||
@@ -263,7 +268,10 @@ public class RestApiServlet extends HttpServlet {
|
|||||||
|
|
||||||
List<IdString> path = splitPath(req);
|
List<IdString> path = splitPath(req);
|
||||||
RestCollection<RestResource, RestResource> rc = members.get();
|
RestCollection<RestResource, RestResource> rc = members.get();
|
||||||
CapabilityUtils.checkRequiresCapability(globals.currentUser, null, rc.getClass());
|
globals
|
||||||
|
.permissionBackend
|
||||||
|
.user(globals.currentUser)
|
||||||
|
.checkAny(GlobalPermission.fromAnnotation(rc.getClass()));
|
||||||
|
|
||||||
viewData = new ViewData(null, null);
|
viewData = new ViewData(null, null);
|
||||||
|
|
||||||
@@ -1106,9 +1114,12 @@ public class RestApiServlet extends HttpServlet {
|
|||||||
return "GET".equals(req.getMethod()) || "HEAD".equals(req.getMethod());
|
return "GET".equals(req.getMethod()) || "HEAD".equals(req.getMethod());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkRequiresCapability(ViewData viewData) throws AuthException {
|
private void checkRequiresCapability(ViewData d)
|
||||||
CapabilityUtils.checkRequiresCapability(
|
throws AuthException, PermissionBackendException {
|
||||||
globals.currentUser, viewData.pluginName, viewData.view.getClass());
|
globals
|
||||||
|
.permissionBackend
|
||||||
|
.user(globals.currentUser)
|
||||||
|
.checkAny(GlobalPermission.fromAnnotation(d.pluginName, d.view.getClass()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long handleException(
|
private static long handleException(
|
||||||
|
@@ -14,7 +14,6 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.account;
|
package com.google.gerrit.server.account;
|
||||||
|
|
||||||
import com.google.gerrit.common.data.GlobalCapability;
|
|
||||||
import com.google.gerrit.extensions.registration.DynamicMap;
|
import com.google.gerrit.extensions.registration.DynamicMap;
|
||||||
import com.google.gerrit.extensions.restapi.AuthException;
|
import com.google.gerrit.extensions.restapi.AuthException;
|
||||||
import com.google.gerrit.extensions.restapi.ChildCollection;
|
import com.google.gerrit.extensions.restapi.ChildCollection;
|
||||||
@@ -22,7 +21,13 @@ import com.google.gerrit.extensions.restapi.IdString;
|
|||||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||||
import com.google.gerrit.extensions.restapi.RestView;
|
import com.google.gerrit.extensions.restapi.RestView;
|
||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
import com.google.gerrit.server.account.AccountResource.Capability;
|
import com.google.gerrit.server.account.AccountResource.Capability;
|
||||||
|
import com.google.gerrit.server.permissions.GlobalOrPluginPermission;
|
||||||
|
import com.google.gerrit.server.permissions.GlobalPermission;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||||
|
import com.google.gerrit.server.permissions.PluginPermission;
|
||||||
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;
|
||||||
@@ -30,15 +35,18 @@ import com.google.inject.Singleton;
|
|||||||
@Singleton
|
@Singleton
|
||||||
class Capabilities implements ChildCollection<AccountResource, AccountResource.Capability> {
|
class Capabilities implements ChildCollection<AccountResource, AccountResource.Capability> {
|
||||||
private final Provider<CurrentUser> self;
|
private final Provider<CurrentUser> self;
|
||||||
|
private final PermissionBackend permissionBackend;
|
||||||
private final DynamicMap<RestView<AccountResource.Capability>> views;
|
private final DynamicMap<RestView<AccountResource.Capability>> views;
|
||||||
private final Provider<GetCapabilities> get;
|
private final Provider<GetCapabilities> get;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
Capabilities(
|
Capabilities(
|
||||||
Provider<CurrentUser> self,
|
Provider<CurrentUser> self,
|
||||||
|
PermissionBackend permissionBackend,
|
||||||
DynamicMap<RestView<AccountResource.Capability>> views,
|
DynamicMap<RestView<AccountResource.Capability>> views,
|
||||||
Provider<GetCapabilities> get) {
|
Provider<GetCapabilities> get) {
|
||||||
this.self = self;
|
this.self = self;
|
||||||
|
this.permissionBackend = permissionBackend;
|
||||||
this.views = views;
|
this.views = views;
|
||||||
this.get = get;
|
this.get = get;
|
||||||
}
|
}
|
||||||
@@ -50,20 +58,39 @@ class Capabilities implements ChildCollection<AccountResource, AccountResource.C
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Capability parse(AccountResource parent, IdString id)
|
public Capability parse(AccountResource parent, IdString id)
|
||||||
throws ResourceNotFoundException, AuthException {
|
throws ResourceNotFoundException, AuthException, PermissionBackendException {
|
||||||
if (self.get() != parent.getUser() && !self.get().getCapabilities().canAdministrateServer()) {
|
IdentifiedUser target = parent.getUser();
|
||||||
throw new AuthException("restricted to administrator");
|
if (self.get() != target) {
|
||||||
|
permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
String name = id.get();
|
GlobalOrPluginPermission perm = parse(id);
|
||||||
CapabilityControl cap = parent.getUser().getCapabilities();
|
if (permissionBackend.user(target).test(perm)) {
|
||||||
if (cap.canPerform(name)
|
return new AccountResource.Capability(target, perm.permissionName());
|
||||||
|| (cap.canAdministrateServer() && GlobalCapability.isCapability(name))) {
|
|
||||||
return new AccountResource.Capability(parent.getUser(), name);
|
|
||||||
}
|
}
|
||||||
throw new ResourceNotFoundException(id);
|
throw new ResourceNotFoundException(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private GlobalOrPluginPermission parse(IdString id) throws ResourceNotFoundException {
|
||||||
|
String name = id.get();
|
||||||
|
GlobalOrPluginPermission perm = GlobalPermission.byName(name);
|
||||||
|
if (perm != null) {
|
||||||
|
return perm;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dash = name.lastIndexOf('-');
|
||||||
|
if (dash < 0) {
|
||||||
|
throw new ResourceNotFoundException(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
String pluginName = name.substring(0, dash);
|
||||||
|
String capability = name.substring(dash + 1);
|
||||||
|
if (pluginName.isEmpty() || capability.isEmpty()) {
|
||||||
|
throw new ResourceNotFoundException(id);
|
||||||
|
}
|
||||||
|
return new PluginPermission(pluginName, capability);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DynamicMap<RestView<Capability>> views() {
|
public DynamicMap<RestView<Capability>> views() {
|
||||||
return views;
|
return views;
|
||||||
|
@@ -26,8 +26,10 @@ 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.GlobalOrPluginPermission;
|
||||||
import com.google.gerrit.server.permissions.GlobalPermission;
|
import com.google.gerrit.server.permissions.GlobalPermission;
|
||||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||||
|
import com.google.gerrit.server.permissions.PluginPermission;
|
||||||
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;
|
||||||
@@ -140,11 +142,8 @@ public class CapabilityControl {
|
|||||||
return QueueProvider.QueueType.INTERACTIVE;
|
return QueueProvider.QueueType.INTERACTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** True if the user has this permission. Works only for non labels. */
|
/** @return true if the user has this permission. */
|
||||||
public boolean canPerform(String permissionName) {
|
private boolean canPerform(String permissionName) {
|
||||||
if (GlobalCapability.ADMINISTRATE_SERVER.equals(permissionName)) {
|
|
||||||
return canAdministrateServer();
|
|
||||||
}
|
|
||||||
return !access(permissionName).isEmpty();
|
return !access(permissionName).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,8 +215,17 @@ public class CapabilityControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Do not use unless inside DefaultPermissionBackend. */
|
/** Do not use unless inside DefaultPermissionBackend. */
|
||||||
public boolean doCanForDefaultPermissionBackend(GlobalPermission perm)
|
public boolean doCanForDefaultPermissionBackend(GlobalOrPluginPermission perm)
|
||||||
throws PermissionBackendException {
|
throws PermissionBackendException {
|
||||||
|
if (perm instanceof GlobalPermission) {
|
||||||
|
return can((GlobalPermission) perm);
|
||||||
|
} else if (perm instanceof PluginPermission) {
|
||||||
|
return canPerform(perm.permissionName()) || canAdministrateServer();
|
||||||
|
}
|
||||||
|
throw new PermissionBackendException(perm + " unsupported");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean can(GlobalPermission perm) throws PermissionBackendException {
|
||||||
switch (perm) {
|
switch (perm) {
|
||||||
case ADMINISTRATE_SERVER:
|
case ADMINISTRATE_SERVER:
|
||||||
return canAdministrateServer();
|
return canAdministrateServer();
|
||||||
|
@@ -1,132 +0,0 @@
|
|||||||
// Copyright (C) 2013 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.account;
|
|
||||||
|
|
||||||
import com.google.gerrit.extensions.annotations.CapabilityScope;
|
|
||||||
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
|
|
||||||
import com.google.gerrit.extensions.annotations.RequiresCapability;
|
|
||||||
import com.google.gerrit.extensions.restapi.AuthException;
|
|
||||||
import com.google.gerrit.server.CurrentUser;
|
|
||||||
import com.google.inject.Provider;
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class CapabilityUtils {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(CapabilityUtils.class);
|
|
||||||
|
|
||||||
public static void checkRequiresCapability(
|
|
||||||
Provider<CurrentUser> userProvider, String pluginName, Class<?> clazz) throws AuthException {
|
|
||||||
checkRequiresCapability(userProvider.get(), pluginName, clazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void checkRequiresCapability(CurrentUser user, String pluginName, Class<?> clazz)
|
|
||||||
throws AuthException {
|
|
||||||
RequiresCapability rc = getClassAnnotation(clazz, RequiresCapability.class);
|
|
||||||
RequiresAnyCapability rac = getClassAnnotation(clazz, RequiresAnyCapability.class);
|
|
||||||
if (rc != null && rac != null) {
|
|
||||||
log.error(
|
|
||||||
String.format(
|
|
||||||
"Class %s uses both @%s and @%s",
|
|
||||||
clazz.getName(),
|
|
||||||
RequiresCapability.class.getSimpleName(),
|
|
||||||
RequiresAnyCapability.class.getSimpleName()));
|
|
||||||
throw new AuthException("cannot check capability");
|
|
||||||
}
|
|
||||||
CapabilityControl ctl = user.getCapabilities();
|
|
||||||
if (ctl.canAdministrateServer()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
checkRequiresCapability(ctl, pluginName, clazz, rc);
|
|
||||||
checkRequiresAnyCapability(ctl, pluginName, clazz, rac);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void checkRequiresCapability(
|
|
||||||
CapabilityControl ctl, String pluginName, Class<?> clazz, RequiresCapability rc)
|
|
||||||
throws AuthException {
|
|
||||||
if (rc == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String capability = resolveCapability(pluginName, rc.value(), rc.scope(), clazz);
|
|
||||||
if (!ctl.canPerform(capability)) {
|
|
||||||
throw new AuthException(
|
|
||||||
String.format("Capability %s is required to access this resource", capability));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void checkRequiresAnyCapability(
|
|
||||||
CapabilityControl ctl, String pluginName, Class<?> clazz, RequiresAnyCapability rac)
|
|
||||||
throws AuthException {
|
|
||||||
if (rac == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (rac.value().length == 0) {
|
|
||||||
log.error(
|
|
||||||
String.format(
|
|
||||||
"Class %s uses @%s with no capabilities listed",
|
|
||||||
clazz.getName(), RequiresAnyCapability.class.getSimpleName()));
|
|
||||||
throw new AuthException("cannot check capability");
|
|
||||||
}
|
|
||||||
for (String capability : rac.value()) {
|
|
||||||
capability = resolveCapability(pluginName, capability, rac.scope(), clazz);
|
|
||||||
if (ctl.canPerform(capability)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new AuthException(
|
|
||||||
"One of the following capabilities is required to access this"
|
|
||||||
+ " resource: "
|
|
||||||
+ Arrays.asList(rac.value()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String resolveCapability(
|
|
||||||
String pluginName, String capability, CapabilityScope scope, Class<?> clazz)
|
|
||||||
throws AuthException {
|
|
||||||
if (pluginName != null
|
|
||||||
&& !"gerrit".equals(pluginName)
|
|
||||||
&& (scope == CapabilityScope.PLUGIN || scope == CapabilityScope.CONTEXT)) {
|
|
||||||
capability = String.format("%s-%s", pluginName, capability);
|
|
||||||
} else if (scope == CapabilityScope.PLUGIN) {
|
|
||||||
log.error(
|
|
||||||
String.format(
|
|
||||||
"Class %s uses @%s(scope=%s), but is not within a plugin",
|
|
||||||
clazz.getName(),
|
|
||||||
RequiresCapability.class.getSimpleName(),
|
|
||||||
CapabilityScope.PLUGIN.name()));
|
|
||||||
throw new AuthException("cannot check capability");
|
|
||||||
}
|
|
||||||
return capability;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find an instance of the specified annotation, walking up the inheritance tree if necessary.
|
|
||||||
*
|
|
||||||
* @param <T> Annotation type to search for
|
|
||||||
* @param clazz root class to search, may be null
|
|
||||||
* @param annotationClass class object of Annotation subclass to search for
|
|
||||||
* @return the requested annotation or null if none
|
|
||||||
*/
|
|
||||||
private static <T extends Annotation> T getClassAnnotation(
|
|
||||||
Class<?> clazz, Class<T> annotationClass) {
|
|
||||||
for (; clazz != null; clazz = clazz.getSuperclass()) {
|
|
||||||
T t = clazz.getAnnotation(annotationClass);
|
|
||||||
if (t != null) {
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -29,14 +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.GlobalOrPluginPermission;
|
||||||
import com.google.gerrit.server.permissions.GlobalPermission;
|
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.gerrit.server.permissions.PermissionBackendException;
|
||||||
|
import com.google.gerrit.server.permissions.PluginPermission;
|
||||||
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.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -77,11 +78,10 @@ class GetCapabilities implements RestReadView<AccountResource> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Object> have = new LinkedHashMap<>();
|
Map<String, Object> have = new LinkedHashMap<>();
|
||||||
for (GlobalPermission p : testGlobalPermissions(perm)) {
|
for (GlobalOrPluginPermission p : perm.test(permissionsToTest())) {
|
||||||
have.put(p.permissionName(), true);
|
have.put(p.permissionName(), true);
|
||||||
}
|
}
|
||||||
addRanges(have, rsrc);
|
addRanges(have, rsrc);
|
||||||
addPluginCapabilities(have, rsrc);
|
|
||||||
addPriority(have, rsrc);
|
addPriority(have, rsrc);
|
||||||
|
|
||||||
return OutputFormat.JSON
|
return OutputFormat.JSON
|
||||||
@@ -89,20 +89,23 @@ class GetCapabilities implements RestReadView<AccountResource> {
|
|||||||
.toJsonTree(have, new TypeToken<Map<String, Object>>() {}.getType());
|
.toJsonTree(have, new TypeToken<Map<String, Object>>() {}.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<GlobalPermission> testGlobalPermissions(PermissionBackend.WithUser perm)
|
private Set<GlobalOrPluginPermission> permissionsToTest() {
|
||||||
throws PermissionBackendException {
|
Set<GlobalOrPluginPermission> toTest = new HashSet<>();
|
||||||
EnumSet<GlobalPermission> toTest;
|
for (GlobalPermission p : GlobalPermission.values()) {
|
||||||
if (query != null) {
|
if (want(p.permissionName())) {
|
||||||
toTest = EnumSet.noneOf(GlobalPermission.class);
|
toTest.add(p);
|
||||||
for (GlobalPermission p : GlobalPermission.values()) {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String pluginName : pluginCapabilities.plugins()) {
|
||||||
|
for (String capability : pluginCapabilities.byPlugin(pluginName).keySet()) {
|
||||||
|
PluginPermission p = new PluginPermission(pluginName, capability);
|
||||||
if (want(p.permissionName())) {
|
if (want(p.permissionName())) {
|
||||||
toTest.add(p);
|
toTest.add(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
toTest = EnumSet.allOf(GlobalPermission.class);
|
|
||||||
}
|
}
|
||||||
return perm.test(toTest);
|
return toTest;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean want(String name) {
|
private boolean want(String name) {
|
||||||
@@ -118,18 +121,6 @@ class GetCapabilities implements RestReadView<AccountResource> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPluginCapabilities(Map<String, Object> have, AccountResource rsrc) {
|
|
||||||
CapabilityControl cc = rsrc.getUser().getCapabilities();
|
|
||||||
for (String pluginName : pluginCapabilities.plugins()) {
|
|
||||||
for (String capability : pluginCapabilities.byPlugin(pluginName).keySet()) {
|
|
||||||
String name = String.format("%s-%s", pluginName, capability);
|
|
||||||
if (want(name) && cc.canPerform(name)) {
|
|
||||||
have.put(name, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addPriority(Map<String, Object> have, AccountResource rsrc) {
|
private void addPriority(Map<String, Object> have, AccountResource rsrc) {
|
||||||
QueueProvider.QueueType queue = rsrc.getUser().getCapabilities().getQueueType();
|
QueueProvider.QueueType queue = rsrc.getUser().getCapabilities().getQueueType();
|
||||||
if (queue != QueueProvider.QueueType.INTERACTIVE
|
if (queue != QueueProvider.QueueType.INTERACTIVE
|
||||||
|
@@ -15,7 +15,6 @@
|
|||||||
package com.google.gerrit.server.api.accounts;
|
package com.google.gerrit.server.api.accounts;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static com.google.gerrit.server.account.CapabilityUtils.checkRequiresCapability;
|
|
||||||
|
|
||||||
import com.google.gerrit.extensions.api.accounts.AccountApi;
|
import com.google.gerrit.extensions.api.accounts.AccountApi;
|
||||||
import com.google.gerrit.extensions.api.accounts.AccountInput;
|
import com.google.gerrit.extensions.api.accounts.AccountInput;
|
||||||
@@ -32,6 +31,9 @@ import com.google.gerrit.server.account.AccountResource;
|
|||||||
import com.google.gerrit.server.account.AccountsCollection;
|
import com.google.gerrit.server.account.AccountsCollection;
|
||||||
import com.google.gerrit.server.account.CreateAccount;
|
import com.google.gerrit.server.account.CreateAccount;
|
||||||
import com.google.gerrit.server.account.QueryAccounts;
|
import com.google.gerrit.server.account.QueryAccounts;
|
||||||
|
import com.google.gerrit.server.permissions.GlobalPermission;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
@@ -44,6 +46,7 @@ import org.eclipse.jgit.errors.ConfigInvalidException;
|
|||||||
public class AccountsImpl implements Accounts {
|
public class AccountsImpl implements Accounts {
|
||||||
private final AccountsCollection accounts;
|
private final AccountsCollection accounts;
|
||||||
private final AccountApiImpl.Factory api;
|
private final AccountApiImpl.Factory api;
|
||||||
|
private final PermissionBackend permissionBackend;
|
||||||
private final Provider<CurrentUser> self;
|
private final Provider<CurrentUser> self;
|
||||||
private final CreateAccount.Factory createAccount;
|
private final CreateAccount.Factory createAccount;
|
||||||
private final Provider<QueryAccounts> queryAccountsProvider;
|
private final Provider<QueryAccounts> queryAccountsProvider;
|
||||||
@@ -52,11 +55,13 @@ public class AccountsImpl implements Accounts {
|
|||||||
AccountsImpl(
|
AccountsImpl(
|
||||||
AccountsCollection accounts,
|
AccountsCollection accounts,
|
||||||
AccountApiImpl.Factory api,
|
AccountApiImpl.Factory api,
|
||||||
|
PermissionBackend permissionBackend,
|
||||||
Provider<CurrentUser> self,
|
Provider<CurrentUser> self,
|
||||||
CreateAccount.Factory createAccount,
|
CreateAccount.Factory createAccount,
|
||||||
Provider<QueryAccounts> queryAccountsProvider) {
|
Provider<QueryAccounts> queryAccountsProvider) {
|
||||||
this.accounts = accounts;
|
this.accounts = accounts;
|
||||||
this.api = api;
|
this.api = api;
|
||||||
|
this.permissionBackend = permissionBackend;
|
||||||
this.self = self;
|
this.self = self;
|
||||||
this.createAccount = createAccount;
|
this.createAccount = createAccount;
|
||||||
this.queryAccountsProvider = queryAccountsProvider;
|
this.queryAccountsProvider = queryAccountsProvider;
|
||||||
@@ -96,12 +101,12 @@ public class AccountsImpl implements Accounts {
|
|||||||
if (checkNotNull(in, "AccountInput").username == null) {
|
if (checkNotNull(in, "AccountInput").username == null) {
|
||||||
throw new BadRequestException("AccountInput must specify username");
|
throw new BadRequestException("AccountInput must specify username");
|
||||||
}
|
}
|
||||||
checkRequiresCapability(self, null, CreateAccount.class);
|
|
||||||
try {
|
try {
|
||||||
AccountInfo info =
|
CreateAccount impl = createAccount.create(in.username);
|
||||||
createAccount.create(in.username).apply(TopLevelResource.INSTANCE, in).value();
|
permissionBackend.user(self).checkAny(GlobalPermission.fromAnnotation(impl.getClass()));
|
||||||
|
AccountInfo info = impl.apply(TopLevelResource.INSTANCE, in).value();
|
||||||
return id(info._accountId);
|
return id(info._accountId);
|
||||||
} catch (OrmException | IOException | ConfigInvalidException e) {
|
} catch (OrmException | IOException | ConfigInvalidException | PermissionBackendException e) {
|
||||||
throw new RestApiException("Cannot create account " + in.username, e);
|
throw new RestApiException("Cannot create account " + in.username, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,6 @@
|
|||||||
package com.google.gerrit.server.api.groups;
|
package com.google.gerrit.server.api.groups;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static com.google.gerrit.server.account.CapabilityUtils.checkRequiresCapability;
|
|
||||||
|
|
||||||
import com.google.gerrit.extensions.api.groups.GroupApi;
|
import com.google.gerrit.extensions.api.groups.GroupApi;
|
||||||
import com.google.gerrit.extensions.api.groups.GroupInput;
|
import com.google.gerrit.extensions.api.groups.GroupInput;
|
||||||
@@ -32,6 +31,9 @@ import com.google.gerrit.server.group.CreateGroup;
|
|||||||
import com.google.gerrit.server.group.GroupsCollection;
|
import com.google.gerrit.server.group.GroupsCollection;
|
||||||
import com.google.gerrit.server.group.ListGroups;
|
import com.google.gerrit.server.group.ListGroups;
|
||||||
import com.google.gerrit.server.group.QueryGroups;
|
import com.google.gerrit.server.group.QueryGroups;
|
||||||
|
import com.google.gerrit.server.permissions.GlobalPermission;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||||
import com.google.gerrit.server.project.ProjectsCollection;
|
import com.google.gerrit.server.project.ProjectsCollection;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
@@ -49,6 +51,7 @@ class GroupsImpl implements Groups {
|
|||||||
private final Provider<ListGroups> listGroups;
|
private final Provider<ListGroups> listGroups;
|
||||||
private final Provider<QueryGroups> queryGroups;
|
private final Provider<QueryGroups> queryGroups;
|
||||||
private final Provider<CurrentUser> user;
|
private final Provider<CurrentUser> user;
|
||||||
|
private final PermissionBackend permissionBackend;
|
||||||
private final CreateGroup.Factory createGroup;
|
private final CreateGroup.Factory createGroup;
|
||||||
private final GroupApiImpl.Factory api;
|
private final GroupApiImpl.Factory api;
|
||||||
|
|
||||||
@@ -60,6 +63,7 @@ class GroupsImpl implements Groups {
|
|||||||
Provider<ListGroups> listGroups,
|
Provider<ListGroups> listGroups,
|
||||||
Provider<QueryGroups> queryGroups,
|
Provider<QueryGroups> queryGroups,
|
||||||
Provider<CurrentUser> user,
|
Provider<CurrentUser> user,
|
||||||
|
PermissionBackend permissionBackend,
|
||||||
CreateGroup.Factory createGroup,
|
CreateGroup.Factory createGroup,
|
||||||
GroupApiImpl.Factory api) {
|
GroupApiImpl.Factory api) {
|
||||||
this.accounts = accounts;
|
this.accounts = accounts;
|
||||||
@@ -68,6 +72,7 @@ class GroupsImpl implements Groups {
|
|||||||
this.listGroups = listGroups;
|
this.listGroups = listGroups;
|
||||||
this.queryGroups = queryGroups;
|
this.queryGroups = queryGroups;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
|
this.permissionBackend = permissionBackend;
|
||||||
this.createGroup = createGroup;
|
this.createGroup = createGroup;
|
||||||
this.api = api;
|
this.api = api;
|
||||||
}
|
}
|
||||||
@@ -89,11 +94,12 @@ class GroupsImpl implements Groups {
|
|||||||
if (checkNotNull(in, "GroupInput").name == null) {
|
if (checkNotNull(in, "GroupInput").name == null) {
|
||||||
throw new BadRequestException("GroupInput must specify name");
|
throw new BadRequestException("GroupInput must specify name");
|
||||||
}
|
}
|
||||||
checkRequiresCapability(user, null, CreateGroup.class);
|
|
||||||
try {
|
try {
|
||||||
GroupInfo info = createGroup.create(in.name).apply(TopLevelResource.INSTANCE, in);
|
CreateGroup impl = createGroup.create(in.name);
|
||||||
|
permissionBackend.user(user).checkAny(GlobalPermission.fromAnnotation(impl.getClass()));
|
||||||
|
GroupInfo info = impl.apply(TopLevelResource.INSTANCE, in);
|
||||||
return id(info.id);
|
return id(info.id);
|
||||||
} catch (OrmException | IOException e) {
|
} catch (OrmException | IOException | PermissionBackendException e) {
|
||||||
throw new RestApiException("Cannot create group " + in.name, e);
|
throw new RestApiException("Cannot create group " + in.name, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,8 +14,6 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.api.projects;
|
package com.google.gerrit.server.api.projects;
|
||||||
|
|
||||||
import static com.google.gerrit.server.account.CapabilityUtils.checkRequiresCapability;
|
|
||||||
|
|
||||||
import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
|
import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
|
||||||
import com.google.gerrit.extensions.api.access.ProjectAccessInput;
|
import com.google.gerrit.extensions.api.access.ProjectAccessInput;
|
||||||
import com.google.gerrit.extensions.api.projects.BranchApi;
|
import com.google.gerrit.extensions.api.projects.BranchApi;
|
||||||
@@ -39,6 +37,9 @@ import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
|||||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||||
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
|
import com.google.gerrit.server.permissions.GlobalPermission;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||||
import com.google.gerrit.server.project.ChildProjectsCollection;
|
import com.google.gerrit.server.project.ChildProjectsCollection;
|
||||||
import com.google.gerrit.server.project.CommitsCollection;
|
import com.google.gerrit.server.project.CommitsCollection;
|
||||||
import com.google.gerrit.server.project.CreateProject;
|
import com.google.gerrit.server.project.CreateProject;
|
||||||
@@ -71,6 +72,7 @@ public class ProjectApiImpl implements ProjectApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final CurrentUser user;
|
private final CurrentUser user;
|
||||||
|
private final PermissionBackend permissionBackend;
|
||||||
private final CreateProject.Factory createProjectFactory;
|
private final CreateProject.Factory createProjectFactory;
|
||||||
private final ProjectApiImpl.Factory projectApi;
|
private final ProjectApiImpl.Factory projectApi;
|
||||||
private final ProjectsCollection projects;
|
private final ProjectsCollection projects;
|
||||||
@@ -97,6 +99,7 @@ public class ProjectApiImpl implements ProjectApi {
|
|||||||
@AssistedInject
|
@AssistedInject
|
||||||
ProjectApiImpl(
|
ProjectApiImpl(
|
||||||
CurrentUser user,
|
CurrentUser user,
|
||||||
|
PermissionBackend permissionBackend,
|
||||||
CreateProject.Factory createProjectFactory,
|
CreateProject.Factory createProjectFactory,
|
||||||
ProjectApiImpl.Factory projectApi,
|
ProjectApiImpl.Factory projectApi,
|
||||||
ProjectsCollection projects,
|
ProjectsCollection projects,
|
||||||
@@ -120,6 +123,7 @@ public class ProjectApiImpl implements ProjectApi {
|
|||||||
@Assisted ProjectResource project) {
|
@Assisted ProjectResource project) {
|
||||||
this(
|
this(
|
||||||
user,
|
user,
|
||||||
|
permissionBackend,
|
||||||
createProjectFactory,
|
createProjectFactory,
|
||||||
projectApi,
|
projectApi,
|
||||||
projects,
|
projects,
|
||||||
@@ -147,6 +151,7 @@ public class ProjectApiImpl implements ProjectApi {
|
|||||||
@AssistedInject
|
@AssistedInject
|
||||||
ProjectApiImpl(
|
ProjectApiImpl(
|
||||||
CurrentUser user,
|
CurrentUser user,
|
||||||
|
PermissionBackend permissionBackend,
|
||||||
CreateProject.Factory createProjectFactory,
|
CreateProject.Factory createProjectFactory,
|
||||||
ProjectApiImpl.Factory projectApi,
|
ProjectApiImpl.Factory projectApi,
|
||||||
ProjectsCollection projects,
|
ProjectsCollection projects,
|
||||||
@@ -170,6 +175,7 @@ public class ProjectApiImpl implements ProjectApi {
|
|||||||
@Assisted String name) {
|
@Assisted String name) {
|
||||||
this(
|
this(
|
||||||
user,
|
user,
|
||||||
|
permissionBackend,
|
||||||
createProjectFactory,
|
createProjectFactory,
|
||||||
projectApi,
|
projectApi,
|
||||||
projects,
|
projects,
|
||||||
@@ -196,6 +202,7 @@ public class ProjectApiImpl implements ProjectApi {
|
|||||||
|
|
||||||
private ProjectApiImpl(
|
private ProjectApiImpl(
|
||||||
CurrentUser user,
|
CurrentUser user,
|
||||||
|
PermissionBackend permissionBackend,
|
||||||
CreateProject.Factory createProjectFactory,
|
CreateProject.Factory createProjectFactory,
|
||||||
ProjectApiImpl.Factory projectApi,
|
ProjectApiImpl.Factory projectApi,
|
||||||
ProjectsCollection projects,
|
ProjectsCollection projects,
|
||||||
@@ -219,6 +226,7 @@ public class ProjectApiImpl implements ProjectApi {
|
|||||||
CommitApiImpl.Factory commitApi,
|
CommitApiImpl.Factory commitApi,
|
||||||
String name) {
|
String name) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
|
this.permissionBackend = permissionBackend;
|
||||||
this.createProjectFactory = createProjectFactory;
|
this.createProjectFactory = createProjectFactory;
|
||||||
this.projectApi = projectApi;
|
this.projectApi = projectApi;
|
||||||
this.projects = projects;
|
this.projects = projects;
|
||||||
@@ -257,10 +265,11 @@ public class ProjectApiImpl implements ProjectApi {
|
|||||||
if (in.name != null && !name.equals(in.name)) {
|
if (in.name != null && !name.equals(in.name)) {
|
||||||
throw new BadRequestException("name must match input.name");
|
throw new BadRequestException("name must match input.name");
|
||||||
}
|
}
|
||||||
checkRequiresCapability(user, null, CreateProject.class);
|
CreateProject impl = createProjectFactory.create(name);
|
||||||
createProjectFactory.create(name).apply(TopLevelResource.INSTANCE, in);
|
permissionBackend.user(user).checkAny(GlobalPermission.fromAnnotation(impl.getClass()));
|
||||||
|
impl.apply(TopLevelResource.INSTANCE, in);
|
||||||
return projectApi.create(projects.parse(name));
|
return projectApi.create(projects.parse(name));
|
||||||
} catch (IOException | ConfigInvalidException e) {
|
} catch (IOException | ConfigInvalidException | PermissionBackendException e) {
|
||||||
throw new RestApiException("Cannot create project: " + e.getMessage(), e);
|
throw new RestApiException("Cannot create project: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -30,14 +30,11 @@ import com.google.gerrit.extensions.registration.DynamicSet;
|
|||||||
import com.google.gerrit.extensions.restapi.RestView;
|
import com.google.gerrit.extensions.restapi.RestView;
|
||||||
import com.google.gerrit.extensions.webui.PrivateInternals_UiActionDescription;
|
import com.google.gerrit.extensions.webui.PrivateInternals_UiActionDescription;
|
||||||
import com.google.gerrit.extensions.webui.UiAction;
|
import com.google.gerrit.extensions.webui.UiAction;
|
||||||
import com.google.gerrit.server.CurrentUser;
|
|
||||||
import com.google.gerrit.server.extensions.webui.UiActions;
|
import com.google.gerrit.server.extensions.webui.UiActions;
|
||||||
import com.google.gerrit.server.project.ChangeControl;
|
import com.google.gerrit.server.project.ChangeControl;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import com.google.inject.util.Providers;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -48,6 +45,7 @@ public class ActionJson {
|
|||||||
private final Revisions revisions;
|
private final Revisions revisions;
|
||||||
private final ChangeJson.Factory changeJsonFactory;
|
private final ChangeJson.Factory changeJsonFactory;
|
||||||
private final ChangeResource.Factory changeResourceFactory;
|
private final ChangeResource.Factory changeResourceFactory;
|
||||||
|
private final UiActions uiActions;
|
||||||
private final DynamicMap<RestView<ChangeResource>> changeViews;
|
private final DynamicMap<RestView<ChangeResource>> changeViews;
|
||||||
private final DynamicSet<ActionVisitor> visitorSet;
|
private final DynamicSet<ActionVisitor> visitorSet;
|
||||||
|
|
||||||
@@ -56,11 +54,13 @@ public class ActionJson {
|
|||||||
Revisions revisions,
|
Revisions revisions,
|
||||||
ChangeJson.Factory changeJsonFactory,
|
ChangeJson.Factory changeJsonFactory,
|
||||||
ChangeResource.Factory changeResourceFactory,
|
ChangeResource.Factory changeResourceFactory,
|
||||||
|
UiActions uiActions,
|
||||||
DynamicMap<RestView<ChangeResource>> changeViews,
|
DynamicMap<RestView<ChangeResource>> changeViews,
|
||||||
DynamicSet<ActionVisitor> visitorSet) {
|
DynamicSet<ActionVisitor> visitorSet) {
|
||||||
this.revisions = revisions;
|
this.revisions = revisions;
|
||||||
this.changeJsonFactory = changeJsonFactory;
|
this.changeJsonFactory = changeJsonFactory;
|
||||||
this.changeResourceFactory = changeResourceFactory;
|
this.changeResourceFactory = changeResourceFactory;
|
||||||
|
this.uiActions = uiActions;
|
||||||
this.changeViews = changeViews;
|
this.changeViews = changeViews;
|
||||||
this.visitorSet = visitorSet;
|
this.visitorSet = visitorSet;
|
||||||
}
|
}
|
||||||
@@ -162,9 +162,9 @@ public class ActionJson {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
Provider<CurrentUser> userProvider = Providers.of(ctl.getUser());
|
|
||||||
FluentIterable<UiAction.Description> descs =
|
FluentIterable<UiAction.Description> descs =
|
||||||
UiActions.from(changeViews, changeResourceFactory.create(ctl), userProvider);
|
uiActions.from(changeViews, changeResourceFactory.create(ctl));
|
||||||
|
|
||||||
// The followup action is a client-side only operation that does not
|
// The followup action is a client-side only operation that does not
|
||||||
// have a server side handler. It must be manually registered into the
|
// have a server side handler. It must be manually registered into the
|
||||||
// resulting action map.
|
// resulting action map.
|
||||||
@@ -198,10 +198,10 @@ public class ActionJson {
|
|||||||
if (!rsrc.getControl().getUser().isIdentifiedUser()) {
|
if (!rsrc.getControl().getUser().isIdentifiedUser()) {
|
||||||
return ImmutableMap.of();
|
return ImmutableMap.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, ActionInfo> out = new LinkedHashMap<>();
|
Map<String, ActionInfo> out = new LinkedHashMap<>();
|
||||||
Provider<CurrentUser> userProvider = Providers.of(rsrc.getControl().getUser());
|
|
||||||
ACTION:
|
ACTION:
|
||||||
for (UiAction.Description d : UiActions.from(revisions, rsrc, userProvider)) {
|
for (UiAction.Description d : uiActions.from(revisions, rsrc)) {
|
||||||
ActionInfo actionInfo = new ActionInfo(d);
|
ActionInfo actionInfo = new ActionInfo(d);
|
||||||
for (ActionVisitor visitor : visitors) {
|
for (ActionVisitor visitor : visitors) {
|
||||||
if (!visitor.visit(d.getId(), actionInfo, changeInfo, revisionInfo)) {
|
if (!visitor.visit(d.getId(), actionInfo, changeInfo, revisionInfo)) {
|
||||||
|
@@ -107,6 +107,7 @@ import com.google.gerrit.server.change.ReviewerSuggestion;
|
|||||||
import com.google.gerrit.server.events.EventFactory;
|
import com.google.gerrit.server.events.EventFactory;
|
||||||
import com.google.gerrit.server.events.EventsMetrics;
|
import com.google.gerrit.server.events.EventsMetrics;
|
||||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||||
|
import com.google.gerrit.server.extensions.webui.UiActions;
|
||||||
import com.google.gerrit.server.git.AbandonOp;
|
import com.google.gerrit.server.git.AbandonOp;
|
||||||
import com.google.gerrit.server.git.ChangeMessageModifier;
|
import com.google.gerrit.server.git.ChangeMessageModifier;
|
||||||
import com.google.gerrit.server.git.EmailMerge;
|
import com.google.gerrit.server.git.EmailMerge;
|
||||||
@@ -293,6 +294,7 @@ public class GerritGlobalModule extends FactoryModule {
|
|||||||
bind(AccountControl.Factory.class);
|
bind(AccountControl.Factory.class);
|
||||||
|
|
||||||
install(new AuditModule());
|
install(new AuditModule());
|
||||||
|
bind(UiActions.class);
|
||||||
install(new com.google.gerrit.server.access.Module());
|
install(new com.google.gerrit.server.access.Module());
|
||||||
install(new com.google.gerrit.server.account.Module());
|
install(new com.google.gerrit.server.account.Module());
|
||||||
install(new com.google.gerrit.server.api.Module());
|
install(new com.google.gerrit.server.api.Module());
|
||||||
|
@@ -16,20 +16,27 @@ package com.google.gerrit.server.extensions.webui;
|
|||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.FluentIterable;
|
import com.google.common.collect.FluentIterable;
|
||||||
|
import com.google.gerrit.common.Nullable;
|
||||||
import com.google.gerrit.extensions.registration.DynamicMap;
|
import com.google.gerrit.extensions.registration.DynamicMap;
|
||||||
import com.google.gerrit.extensions.restapi.AuthException;
|
|
||||||
import com.google.gerrit.extensions.restapi.RestCollection;
|
import com.google.gerrit.extensions.restapi.RestCollection;
|
||||||
import com.google.gerrit.extensions.restapi.RestResource;
|
import com.google.gerrit.extensions.restapi.RestResource;
|
||||||
import com.google.gerrit.extensions.restapi.RestView;
|
import com.google.gerrit.extensions.restapi.RestView;
|
||||||
import com.google.gerrit.extensions.webui.PrivateInternals_UiActionDescription;
|
import com.google.gerrit.extensions.webui.PrivateInternals_UiActionDescription;
|
||||||
import com.google.gerrit.extensions.webui.UiAction;
|
import com.google.gerrit.extensions.webui.UiAction;
|
||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
import com.google.gerrit.server.account.CapabilityUtils;
|
import com.google.gerrit.server.permissions.GlobalOrPluginPermission;
|
||||||
|
import com.google.gerrit.server.permissions.GlobalPermission;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||||
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.Singleton;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
public class UiActions {
|
public class UiActions {
|
||||||
private static final Logger log = LoggerFactory.getLogger(UiActions.class);
|
private static final Logger log = LoggerFactory.getLogger(UiActions.class);
|
||||||
|
|
||||||
@@ -37,57 +44,70 @@ public class UiActions {
|
|||||||
return UiAction.Description::isEnabled;
|
return UiAction.Description::isEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <R extends RestResource> FluentIterable<UiAction.Description> from(
|
private final PermissionBackend permissionBackend;
|
||||||
RestCollection<?, R> collection, R resource, Provider<CurrentUser> userProvider) {
|
private final Provider<CurrentUser> userProvider;
|
||||||
return from(collection.views(), resource, userProvider);
|
|
||||||
|
@Inject
|
||||||
|
UiActions(PermissionBackend permissionBackend, Provider<CurrentUser> userProvider) {
|
||||||
|
this.permissionBackend = permissionBackend;
|
||||||
|
this.userProvider = userProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <R extends RestResource> FluentIterable<UiAction.Description> from(
|
public <R extends RestResource> FluentIterable<UiAction.Description> from(
|
||||||
DynamicMap<RestView<R>> views, R resource, Provider<CurrentUser> userProvider) {
|
RestCollection<?, R> collection, R resource) {
|
||||||
|
return from(collection.views(), resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <R extends RestResource> FluentIterable<UiAction.Description> from(
|
||||||
|
DynamicMap<RestView<R>> views, R resource) {
|
||||||
return FluentIterable.from(views)
|
return FluentIterable.from(views)
|
||||||
.transform(
|
.transform((e) -> describe(e, resource))
|
||||||
(DynamicMap.Entry<RestView<R>> e) -> {
|
|
||||||
int d = e.getExportName().indexOf('.');
|
|
||||||
if (d < 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
RestView<R> view;
|
|
||||||
try {
|
|
||||||
view = e.getProvider().get();
|
|
||||||
} catch (RuntimeException err) {
|
|
||||||
log.error(
|
|
||||||
String.format(
|
|
||||||
"error creating view %s.%s", e.getPluginName(), e.getExportName()),
|
|
||||||
err);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(view instanceof UiAction)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
CapabilityUtils.checkRequiresCapability(
|
|
||||||
userProvider, e.getPluginName(), view.getClass());
|
|
||||||
} catch (AuthException exc) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
UiAction.Description dsc = ((UiAction<R>) view).getDescription(resource);
|
|
||||||
if (dsc == null || !dsc.isVisible()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
String name = e.getExportName().substring(d + 1);
|
|
||||||
PrivateInternals_UiActionDescription.setMethod(
|
|
||||||
dsc, e.getExportName().substring(0, d));
|
|
||||||
PrivateInternals_UiActionDescription.setId(
|
|
||||||
dsc, "gerrit".equals(e.getPluginName()) ? name : e.getPluginName() + '~' + name);
|
|
||||||
return dsc;
|
|
||||||
})
|
|
||||||
.filter(Objects::nonNull);
|
.filter(Objects::nonNull);
|
||||||
}
|
}
|
||||||
|
|
||||||
private UiActions() {}
|
@Nullable
|
||||||
|
private <R extends RestResource> UiAction.Description describe(
|
||||||
|
DynamicMap.Entry<RestView<R>> e, R resource) {
|
||||||
|
int d = e.getExportName().indexOf('.');
|
||||||
|
if (d < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
RestView<R> view;
|
||||||
|
try {
|
||||||
|
view = e.getProvider().get();
|
||||||
|
} catch (RuntimeException err) {
|
||||||
|
log.error(
|
||||||
|
String.format("error creating view %s.%s", e.getPluginName(), e.getExportName()), err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(view instanceof UiAction)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Set<GlobalOrPluginPermission> need =
|
||||||
|
GlobalPermission.fromAnnotation(e.getPluginName(), view.getClass());
|
||||||
|
if (!need.isEmpty() && permissionBackend.user(userProvider).test(need).isEmpty()) {
|
||||||
|
// A permission is required, but test returned no candidates.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (PermissionBackendException err) {
|
||||||
|
log.error(
|
||||||
|
String.format("exception testing view %s.%s", e.getPluginName(), e.getExportName()), err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
UiAction.Description dsc = ((UiAction<R>) view).getDescription(resource);
|
||||||
|
if (dsc == null || !dsc.isVisible()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = e.getExportName().substring(d + 1);
|
||||||
|
PrivateInternals_UiActionDescription.setMethod(dsc, e.getExportName().substring(0, d));
|
||||||
|
PrivateInternals_UiActionDescription.setId(
|
||||||
|
dsc, "gerrit".equals(e.getPluginName()) ? name : e.getPluginName() + '~' + name);
|
||||||
|
return dsc;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,24 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
/** A {@link GlobalPermission} or a {@link PluginPermission}. */
|
||||||
|
public interface GlobalOrPluginPermission {
|
||||||
|
/** @return name used in {@code project.config} permissions. */
|
||||||
|
public String permissionName();
|
||||||
|
|
||||||
|
/** @return readable identifier of this permission for exception message. */
|
||||||
|
public String describeForException();
|
||||||
|
}
|
@@ -14,10 +14,22 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.permissions;
|
package com.google.gerrit.server.permissions;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.gerrit.common.Nullable;
|
||||||
import com.google.gerrit.common.data.GlobalCapability;
|
import com.google.gerrit.common.data.GlobalCapability;
|
||||||
|
import com.google.gerrit.extensions.annotations.CapabilityScope;
|
||||||
|
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
|
||||||
|
import com.google.gerrit.extensions.annotations.RequiresCapability;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public enum GlobalPermission {
|
/** Global server permissions built into Gerrit. */
|
||||||
|
public enum GlobalPermission implements GlobalOrPluginPermission {
|
||||||
ACCESS_DATABASE(GlobalCapability.ACCESS_DATABASE),
|
ACCESS_DATABASE(GlobalCapability.ACCESS_DATABASE),
|
||||||
ADMINISTRATE_SERVER(GlobalCapability.ADMINISTRATE_SERVER),
|
ADMINISTRATE_SERVER(GlobalCapability.ADMINISTRATE_SERVER),
|
||||||
CREATE_ACCOUNT(GlobalCapability.CREATE_ACCOUNT),
|
CREATE_ACCOUNT(GlobalCapability.CREATE_ACCOUNT),
|
||||||
@@ -37,6 +49,63 @@ public enum GlobalPermission {
|
|||||||
VIEW_PLUGINS(GlobalCapability.VIEW_PLUGINS),
|
VIEW_PLUGINS(GlobalCapability.VIEW_PLUGINS),
|
||||||
VIEW_QUEUE(GlobalCapability.VIEW_QUEUE);
|
VIEW_QUEUE(GlobalCapability.VIEW_QUEUE);
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(GlobalPermission.class);
|
||||||
|
private static final ImmutableMap<String, GlobalPermission> BY_NAME;
|
||||||
|
|
||||||
|
static {
|
||||||
|
ImmutableMap.Builder<String, GlobalPermission> m = ImmutableMap.builder();
|
||||||
|
for (GlobalPermission p : values()) {
|
||||||
|
m.put(p.permissionName(), p);
|
||||||
|
}
|
||||||
|
BY_NAME = m.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static GlobalPermission byName(String name) {
|
||||||
|
return BY_NAME.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the {@code @RequiresCapability} or {@code @RequiresAnyCapability} annotation.
|
||||||
|
*
|
||||||
|
* @param pluginName name of the declaring plugin. May be {@code null} or {@code "gerrit"} for
|
||||||
|
* classes originating from the core server.
|
||||||
|
* @param clazz target class to extract annotation from.
|
||||||
|
* @return empty set if no annotations were found, or a collection of permissions, any of which
|
||||||
|
* are suitable to enable access.
|
||||||
|
* @throws PermissionBackendException the annotation could not be parsed.
|
||||||
|
*/
|
||||||
|
public static Set<GlobalOrPluginPermission> fromAnnotation(
|
||||||
|
@Nullable String pluginName, Class<?> clazz) throws PermissionBackendException {
|
||||||
|
RequiresCapability rc = findAnnotation(clazz, RequiresCapability.class);
|
||||||
|
RequiresAnyCapability rac = findAnnotation(clazz, RequiresAnyCapability.class);
|
||||||
|
if (rc != null && rac != null) {
|
||||||
|
log.error(
|
||||||
|
String.format(
|
||||||
|
"Class %s uses both @%s and @%s",
|
||||||
|
clazz.getName(),
|
||||||
|
RequiresCapability.class.getSimpleName(),
|
||||||
|
RequiresAnyCapability.class.getSimpleName()));
|
||||||
|
throw new PermissionBackendException("cannot extract permission");
|
||||||
|
} else if (rc != null) {
|
||||||
|
return Collections.singleton(
|
||||||
|
resolve(pluginName, rc.value(), rc.scope(), clazz, RequiresCapability.class));
|
||||||
|
} else if (rac != null) {
|
||||||
|
Set<GlobalOrPluginPermission> r = new LinkedHashSet<>();
|
||||||
|
for (String capability : rac.value()) {
|
||||||
|
r.add(resolve(pluginName, capability, rac.scope(), clazz, RequiresAnyCapability.class));
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableSet(r);
|
||||||
|
} else {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<GlobalOrPluginPermission> fromAnnotation(Class<?> clazz)
|
||||||
|
throws PermissionBackendException {
|
||||||
|
return fromAnnotation(null, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
GlobalPermission(String name) {
|
GlobalPermission(String name) {
|
||||||
@@ -44,11 +113,54 @@ public enum GlobalPermission {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @return name used in {@code project.config} permissions. */
|
/** @return name used in {@code project.config} permissions. */
|
||||||
|
@Override
|
||||||
public String permissionName() {
|
public String permissionName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String describeForException() {
|
public String describeForException() {
|
||||||
return toString().toLowerCase(Locale.US).replace('_', ' ');
|
return toString().toLowerCase(Locale.US).replace('_', ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static GlobalOrPluginPermission resolve(
|
||||||
|
@Nullable String pluginName,
|
||||||
|
String capability,
|
||||||
|
CapabilityScope scope,
|
||||||
|
Class<?> clazz,
|
||||||
|
Class<?> annotationClass)
|
||||||
|
throws PermissionBackendException {
|
||||||
|
if (pluginName != null
|
||||||
|
&& !"gerrit".equals(pluginName)
|
||||||
|
&& (scope == CapabilityScope.PLUGIN || scope == CapabilityScope.CONTEXT)) {
|
||||||
|
return new PluginPermission(pluginName, capability);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scope == CapabilityScope.PLUGIN) {
|
||||||
|
log.error(
|
||||||
|
String.format(
|
||||||
|
"Class %s uses @%s(scope=%s), but is not within a plugin",
|
||||||
|
clazz.getName(), annotationClass.getSimpleName(), scope.name()));
|
||||||
|
throw new PermissionBackendException("cannot extract permission");
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalPermission perm = byName(capability);
|
||||||
|
if (perm == null) {
|
||||||
|
log.error(
|
||||||
|
String.format("Class %s requires unknown capability %s", clazz.getName(), capability));
|
||||||
|
throw new PermissionBackendException("cannot extract permission");
|
||||||
|
}
|
||||||
|
return perm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static <T extends Annotation> T findAnnotation(Class<?> clazz, Class<T> annotation) {
|
||||||
|
for (; clazz != null; clazz = clazz.getSuperclass()) {
|
||||||
|
T t = clazz.getAnnotation(annotation);
|
||||||
|
if (t != null) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -31,6 +31,7 @@ import com.google.inject.util.Providers;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -134,18 +135,47 @@ public abstract class PermissionBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Verify scoped user can {@code perm}, throwing if denied. */
|
/** Verify scoped user can {@code perm}, throwing if denied. */
|
||||||
public abstract void check(GlobalPermission perm)
|
public abstract void check(GlobalOrPluginPermission perm)
|
||||||
throws AuthException, PermissionBackendException;
|
throws AuthException, PermissionBackendException;
|
||||||
|
|
||||||
/** Filter {@code permSet} to permissions scoped user might be able to perform. */
|
/**
|
||||||
public abstract Set<GlobalPermission> test(Collection<GlobalPermission> permSet)
|
* Verify scoped user can perform at least one listed permission.
|
||||||
throws PermissionBackendException;
|
*
|
||||||
|
* <p>If {@code any} is empty, the method completes normally and allows the caller to continue.
|
||||||
public boolean test(GlobalPermission perm) throws PermissionBackendException {
|
* Since no permissions were supplied to check, its assumed no permissions are necessary to
|
||||||
return test(EnumSet.of(perm)).contains(perm);
|
* continue with the caller's operation.
|
||||||
|
*
|
||||||
|
* <p>If the user has at least one of the permissions in {@code any}, the method completes
|
||||||
|
* normally, possibly without checking all listed permissions.
|
||||||
|
*
|
||||||
|
* <p>If {@code any} is non-empty and the user has none, {@link AuthException} is thrown for one
|
||||||
|
* of the failed permissions.
|
||||||
|
*
|
||||||
|
* @param any set of permissions to check.
|
||||||
|
*/
|
||||||
|
public void checkAny(Set<GlobalOrPluginPermission> any)
|
||||||
|
throws PermissionBackendException, AuthException {
|
||||||
|
for (Iterator<GlobalOrPluginPermission> itr = any.iterator(); itr.hasNext(); ) {
|
||||||
|
try {
|
||||||
|
check(itr.next());
|
||||||
|
return;
|
||||||
|
} catch (AuthException err) {
|
||||||
|
if (!itr.hasNext()) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean testOrFalse(GlobalPermission perm) {
|
/** Filter {@code permSet} to permissions scoped user might be able to perform. */
|
||||||
|
public abstract <T extends GlobalOrPluginPermission> Set<T> test(Collection<T> permSet)
|
||||||
|
throws PermissionBackendException;
|
||||||
|
|
||||||
|
public boolean test(GlobalOrPluginPermission perm) throws PermissionBackendException {
|
||||||
|
return test(Collections.singleton(perm)).contains(perm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean testOrFalse(GlobalOrPluginPermission perm) {
|
||||||
try {
|
try {
|
||||||
return test(perm);
|
return test(perm);
|
||||||
} catch (PermissionBackendException e) {
|
} catch (PermissionBackendException e) {
|
||||||
|
@@ -0,0 +1,67 @@
|
|||||||
|
// 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 static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/** A global capability type permission used by a plugin. */
|
||||||
|
public class PluginPermission implements GlobalOrPluginPermission {
|
||||||
|
private final String pluginName;
|
||||||
|
private final String capability;
|
||||||
|
|
||||||
|
public PluginPermission(String pluginName, String capability) {
|
||||||
|
this.pluginName = checkNotNull(pluginName, "pluginName");
|
||||||
|
this.capability = checkNotNull(capability, "capability");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String pluginName() {
|
||||||
|
return pluginName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String capability() {
|
||||||
|
return capability;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String permissionName() {
|
||||||
|
return pluginName + '-' + capability;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String describeForException() {
|
||||||
|
return capability + " for plugin " + pluginName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(pluginName, capability);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (other instanceof PluginPermission) {
|
||||||
|
PluginPermission b = (PluginPermission) other;
|
||||||
|
return pluginName.equals(b.pluginName) && capability.equals(b.capability);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "PluginPermission[plugin=" + pluginName + ", capability=" + capability + ']';
|
||||||
|
}
|
||||||
|
}
|
@@ -31,7 +31,6 @@ import com.google.gerrit.server.config.PluginConfigFactory;
|
|||||||
import com.google.gerrit.server.config.ProjectConfigEntry;
|
import com.google.gerrit.server.config.ProjectConfigEntry;
|
||||||
import com.google.gerrit.server.extensions.webui.UiActions;
|
import com.google.gerrit.server.extensions.webui.UiActions;
|
||||||
import com.google.gerrit.server.git.TransferConfig;
|
import com.google.gerrit.server.git.TransferConfig;
|
||||||
import com.google.inject.util.Providers;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -45,6 +44,7 @@ public class ConfigInfoImpl extends ConfigInfo {
|
|||||||
DynamicMap<ProjectConfigEntry> pluginConfigEntries,
|
DynamicMap<ProjectConfigEntry> pluginConfigEntries,
|
||||||
PluginConfigFactory cfgFactory,
|
PluginConfigFactory cfgFactory,
|
||||||
AllProjectsName allProjects,
|
AllProjectsName allProjects,
|
||||||
|
UiActions uiActions,
|
||||||
DynamicMap<RestView<ProjectResource>> views) {
|
DynamicMap<RestView<ProjectResource>> views) {
|
||||||
ProjectState projectState = control.getProjectState();
|
ProjectState projectState = control.getProjectState();
|
||||||
Project p = control.getProject();
|
Project p = control.getProject();
|
||||||
@@ -126,8 +126,7 @@ public class ConfigInfoImpl extends ConfigInfo {
|
|||||||
getPluginConfig(control.getProjectState(), pluginConfigEntries, cfgFactory, allProjects);
|
getPluginConfig(control.getProjectState(), pluginConfigEntries, cfgFactory, allProjects);
|
||||||
|
|
||||||
actions = new TreeMap<>();
|
actions = new TreeMap<>();
|
||||||
for (UiAction.Description d :
|
for (UiAction.Description d : uiActions.from(views, new ProjectResource(control))) {
|
||||||
UiActions.from(views, new ProjectResource(control), Providers.of(control.getUser()))) {
|
|
||||||
actions.put(d.getId(), new ActionInfo(d));
|
actions.put(d.getId(), new ActionInfo(d));
|
||||||
}
|
}
|
||||||
this.theme = projectState.getTheme();
|
this.theme = projectState.getTheme();
|
||||||
|
@@ -16,11 +16,12 @@ 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.common.collect.Sets;
|
||||||
import com.google.gerrit.extensions.restapi.AuthException;
|
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.GlobalOrPluginPermission;
|
||||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
@@ -65,17 +66,18 @@ class DefaultPermissionBackend extends PermissionBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void check(GlobalPermission perm) throws AuthException, PermissionBackendException {
|
public void check(GlobalOrPluginPermission perm)
|
||||||
|
throws AuthException, PermissionBackendException {
|
||||||
if (!can(perm)) {
|
if (!can(perm)) {
|
||||||
throw new AuthException(perm.describeForException() + " not permitted");
|
throw new AuthException(perm.describeForException() + " not permitted");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<GlobalPermission> test(Collection<GlobalPermission> permSet)
|
public <T extends GlobalOrPluginPermission> Set<T> test(Collection<T> permSet)
|
||||||
throws PermissionBackendException {
|
throws PermissionBackendException {
|
||||||
EnumSet<GlobalPermission> ok = EnumSet.noneOf(GlobalPermission.class);
|
Set<T> ok = newSet(permSet);
|
||||||
for (GlobalPermission perm : permSet) {
|
for (T perm : permSet) {
|
||||||
if (can(perm)) {
|
if (can(perm)) {
|
||||||
ok.add(perm);
|
ok.add(perm);
|
||||||
}
|
}
|
||||||
@@ -83,8 +85,18 @@ class DefaultPermissionBackend extends PermissionBackend {
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean can(GlobalPermission perm) throws PermissionBackendException {
|
private boolean can(GlobalOrPluginPermission perm) throws PermissionBackendException {
|
||||||
return user.getCapabilities().doCanForDefaultPermissionBackend(perm);
|
return user.getCapabilities().doCanForDefaultPermissionBackend(perm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static <T extends GlobalOrPluginPermission> Set<T> newSet(Collection<T> permSet) {
|
||||||
|
if (permSet instanceof EnumSet) {
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
|
Set<T> s = ((EnumSet) permSet).clone();
|
||||||
|
s.clear();
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return Sets.newHashSetWithExpectedSize(permSet.size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -22,6 +22,7 @@ import com.google.gerrit.server.EnableSignedPush;
|
|||||||
import com.google.gerrit.server.config.AllProjectsName;
|
import com.google.gerrit.server.config.AllProjectsName;
|
||||||
import com.google.gerrit.server.config.PluginConfigFactory;
|
import com.google.gerrit.server.config.PluginConfigFactory;
|
||||||
import com.google.gerrit.server.config.ProjectConfigEntry;
|
import com.google.gerrit.server.config.ProjectConfigEntry;
|
||||||
|
import com.google.gerrit.server.extensions.webui.UiActions;
|
||||||
import com.google.gerrit.server.git.TransferConfig;
|
import com.google.gerrit.server.git.TransferConfig;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
@@ -33,6 +34,7 @@ public class GetConfig implements RestReadView<ProjectResource> {
|
|||||||
private final DynamicMap<ProjectConfigEntry> pluginConfigEntries;
|
private final DynamicMap<ProjectConfigEntry> pluginConfigEntries;
|
||||||
private final PluginConfigFactory cfgFactory;
|
private final PluginConfigFactory cfgFactory;
|
||||||
private final AllProjectsName allProjects;
|
private final AllProjectsName allProjects;
|
||||||
|
private final UiActions uiActions;
|
||||||
private final DynamicMap<RestView<ProjectResource>> views;
|
private final DynamicMap<RestView<ProjectResource>> views;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@@ -42,12 +44,14 @@ public class GetConfig implements RestReadView<ProjectResource> {
|
|||||||
DynamicMap<ProjectConfigEntry> pluginConfigEntries,
|
DynamicMap<ProjectConfigEntry> pluginConfigEntries,
|
||||||
PluginConfigFactory cfgFactory,
|
PluginConfigFactory cfgFactory,
|
||||||
AllProjectsName allProjects,
|
AllProjectsName allProjects,
|
||||||
|
UiActions uiActions,
|
||||||
DynamicMap<RestView<ProjectResource>> views) {
|
DynamicMap<RestView<ProjectResource>> views) {
|
||||||
this.serverEnableSignedPush = serverEnableSignedPush;
|
this.serverEnableSignedPush = serverEnableSignedPush;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.pluginConfigEntries = pluginConfigEntries;
|
this.pluginConfigEntries = pluginConfigEntries;
|
||||||
this.allProjects = allProjects;
|
this.allProjects = allProjects;
|
||||||
this.cfgFactory = cfgFactory;
|
this.cfgFactory = cfgFactory;
|
||||||
|
this.uiActions = uiActions;
|
||||||
this.views = views;
|
this.views = views;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +64,7 @@ public class GetConfig implements RestReadView<ProjectResource> {
|
|||||||
pluginConfigEntries,
|
pluginConfigEntries,
|
||||||
cfgFactory,
|
cfgFactory,
|
||||||
allProjects,
|
allProjects,
|
||||||
|
uiActions,
|
||||||
views);
|
views);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,6 @@ import com.google.gerrit.server.WebLinks;
|
|||||||
import com.google.gerrit.server.extensions.webui.UiActions;
|
import com.google.gerrit.server.extensions.webui.UiActions;
|
||||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.util.Providers;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -48,6 +47,7 @@ import org.kohsuke.args4j.Option;
|
|||||||
public class ListBranches implements RestReadView<ProjectResource> {
|
public class ListBranches implements RestReadView<ProjectResource> {
|
||||||
private final GitRepositoryManager repoManager;
|
private final GitRepositoryManager repoManager;
|
||||||
private final DynamicMap<RestView<BranchResource>> branchViews;
|
private final DynamicMap<RestView<BranchResource>> branchViews;
|
||||||
|
private final UiActions uiActions;
|
||||||
private final WebLinks webLinks;
|
private final WebLinks webLinks;
|
||||||
|
|
||||||
@Option(
|
@Option(
|
||||||
@@ -99,9 +99,11 @@ public class ListBranches implements RestReadView<ProjectResource> {
|
|||||||
public ListBranches(
|
public ListBranches(
|
||||||
GitRepositoryManager repoManager,
|
GitRepositoryManager repoManager,
|
||||||
DynamicMap<RestView<BranchResource>> branchViews,
|
DynamicMap<RestView<BranchResource>> branchViews,
|
||||||
|
UiActions uiActions,
|
||||||
WebLinks webLinks) {
|
WebLinks webLinks) {
|
||||||
this.repoManager = repoManager;
|
this.repoManager = repoManager;
|
||||||
this.branchViews = branchViews;
|
this.branchViews = branchViews;
|
||||||
|
this.uiActions = uiActions;
|
||||||
this.webLinks = webLinks;
|
this.webLinks = webLinks;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,16 +199,15 @@ public class ListBranches implements RestReadView<ProjectResource> {
|
|||||||
info.ref = ref.getName();
|
info.ref = ref.getName();
|
||||||
info.revision = ref.getObjectId() != null ? ref.getObjectId().name() : null;
|
info.revision = ref.getObjectId() != null ? ref.getObjectId().name() : null;
|
||||||
info.canDelete = !targets.contains(ref.getName()) && refControl.canDelete() ? true : null;
|
info.canDelete = !targets.contains(ref.getName()) && refControl.canDelete() ? true : null;
|
||||||
for (UiAction.Description d :
|
|
||||||
UiActions.from(
|
BranchResource rsrc = new BranchResource(refControl.getProjectControl(), info);
|
||||||
branchViews,
|
for (UiAction.Description d : uiActions.from(branchViews, rsrc)) {
|
||||||
new BranchResource(refControl.getProjectControl(), info),
|
|
||||||
Providers.of(refControl.getUser()))) {
|
|
||||||
if (info.actions == null) {
|
if (info.actions == null) {
|
||||||
info.actions = new TreeMap<>();
|
info.actions = new TreeMap<>();
|
||||||
}
|
}
|
||||||
info.actions.put(d.getId(), new ActionInfo(d));
|
info.actions.put(d.getId(), new ActionInfo(d));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<WebLinkInfo> links =
|
List<WebLinkInfo> links =
|
||||||
webLinks.getBranchLinks(
|
webLinks.getBranchLinks(
|
||||||
refControl.getProjectControl().getProject().getName(), ref.getName());
|
refControl.getProjectControl().getProject().getName(), ref.getName());
|
||||||
|
@@ -36,6 +36,7 @@ import com.google.gerrit.server.config.AllProjectsName;
|
|||||||
import com.google.gerrit.server.config.PluginConfig;
|
import com.google.gerrit.server.config.PluginConfig;
|
||||||
import com.google.gerrit.server.config.PluginConfigFactory;
|
import com.google.gerrit.server.config.PluginConfigFactory;
|
||||||
import com.google.gerrit.server.config.ProjectConfigEntry;
|
import com.google.gerrit.server.config.ProjectConfigEntry;
|
||||||
|
import com.google.gerrit.server.extensions.webui.UiActions;
|
||||||
import com.google.gerrit.server.git.MetaDataUpdate;
|
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||||
import com.google.gerrit.server.git.ProjectConfig;
|
import com.google.gerrit.server.git.ProjectConfig;
|
||||||
import com.google.gerrit.server.git.TransferConfig;
|
import com.google.gerrit.server.git.TransferConfig;
|
||||||
@@ -64,6 +65,7 @@ public class PutConfig implements RestModifyView<ProjectResource, ConfigInput> {
|
|||||||
private final DynamicMap<ProjectConfigEntry> pluginConfigEntries;
|
private final DynamicMap<ProjectConfigEntry> pluginConfigEntries;
|
||||||
private final PluginConfigFactory cfgFactory;
|
private final PluginConfigFactory cfgFactory;
|
||||||
private final AllProjectsName allProjects;
|
private final AllProjectsName allProjects;
|
||||||
|
private final UiActions uiActions;
|
||||||
private final DynamicMap<RestView<ProjectResource>> views;
|
private final DynamicMap<RestView<ProjectResource>> views;
|
||||||
private final Provider<CurrentUser> user;
|
private final Provider<CurrentUser> user;
|
||||||
|
|
||||||
@@ -77,6 +79,7 @@ public class PutConfig implements RestModifyView<ProjectResource, ConfigInput> {
|
|||||||
DynamicMap<ProjectConfigEntry> pluginConfigEntries,
|
DynamicMap<ProjectConfigEntry> pluginConfigEntries,
|
||||||
PluginConfigFactory cfgFactory,
|
PluginConfigFactory cfgFactory,
|
||||||
AllProjectsName allProjects,
|
AllProjectsName allProjects,
|
||||||
|
UiActions uiActions,
|
||||||
DynamicMap<RestView<ProjectResource>> views,
|
DynamicMap<RestView<ProjectResource>> views,
|
||||||
Provider<CurrentUser> user) {
|
Provider<CurrentUser> user) {
|
||||||
this.serverEnableSignedPush = serverEnableSignedPush;
|
this.serverEnableSignedPush = serverEnableSignedPush;
|
||||||
@@ -87,6 +90,7 @@ public class PutConfig implements RestModifyView<ProjectResource, ConfigInput> {
|
|||||||
this.pluginConfigEntries = pluginConfigEntries;
|
this.pluginConfigEntries = pluginConfigEntries;
|
||||||
this.cfgFactory = cfgFactory;
|
this.cfgFactory = cfgFactory;
|
||||||
this.allProjects = allProjects;
|
this.allProjects = allProjects;
|
||||||
|
this.uiActions = uiActions;
|
||||||
this.views = views;
|
this.views = views;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
}
|
}
|
||||||
@@ -185,6 +189,7 @@ public class PutConfig implements RestModifyView<ProjectResource, ConfigInput> {
|
|||||||
pluginConfigEntries,
|
pluginConfigEntries,
|
||||||
cfgFactory,
|
cfgFactory,
|
||||||
allProjects,
|
allProjects,
|
||||||
|
uiActions,
|
||||||
views);
|
views);
|
||||||
} catch (RepositoryNotFoundException notFound) {
|
} catch (RepositoryNotFoundException notFound) {
|
||||||
throw new ResourceNotFoundException(projectName.get());
|
throw new ResourceNotFoundException(projectName.get());
|
||||||
|
@@ -16,12 +16,16 @@ package com.google.gerrit.sshd;
|
|||||||
|
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.util.concurrent.Atomics;
|
import com.google.common.util.concurrent.Atomics;
|
||||||
import com.google.gerrit.extensions.annotations.RequiresCapability;
|
import com.google.gerrit.extensions.restapi.AuthException;
|
||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
import com.google.gerrit.server.account.CapabilityControl;
|
import com.google.gerrit.server.permissions.GlobalOrPluginPermission;
|
||||||
|
import com.google.gerrit.server.permissions.GlobalPermission;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import org.apache.sshd.server.Command;
|
import org.apache.sshd.server.Command;
|
||||||
import org.apache.sshd.server.Environment;
|
import org.apache.sshd.server.Environment;
|
||||||
@@ -30,14 +34,17 @@ import org.apache.sshd.server.Environment;
|
|||||||
public class AliasCommand extends BaseCommand {
|
public class AliasCommand extends BaseCommand {
|
||||||
private final DispatchCommandProvider root;
|
private final DispatchCommandProvider root;
|
||||||
private final CurrentUser currentUser;
|
private final CurrentUser currentUser;
|
||||||
|
private final PermissionBackend permissionBackend;
|
||||||
private final CommandName command;
|
private final CommandName command;
|
||||||
private final AtomicReference<Command> atomicCmd;
|
private final AtomicReference<Command> atomicCmd;
|
||||||
|
|
||||||
AliasCommand(
|
AliasCommand(
|
||||||
@CommandName(Commands.ROOT) DispatchCommandProvider root,
|
@CommandName(Commands.ROOT) DispatchCommandProvider root,
|
||||||
|
PermissionBackend permissionBackend,
|
||||||
CurrentUser currentUser,
|
CurrentUser currentUser,
|
||||||
CommandName command) {
|
CommandName command) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
|
this.permissionBackend = permissionBackend;
|
||||||
this.currentUser = currentUser;
|
this.currentUser = currentUser;
|
||||||
this.command = command;
|
this.command = command;
|
||||||
this.atomicCmd = Atomics.newReference();
|
this.atomicCmd = Atomics.newReference();
|
||||||
@@ -47,7 +54,7 @@ public class AliasCommand extends BaseCommand {
|
|||||||
public void start(Environment env) throws IOException {
|
public void start(Environment env) throws IOException {
|
||||||
try {
|
try {
|
||||||
begin(env);
|
begin(env);
|
||||||
} catch (UnloggedFailure e) {
|
} catch (Failure e) {
|
||||||
String msg = e.getMessage();
|
String msg = e.getMessage();
|
||||||
if (!msg.endsWith("\n")) {
|
if (!msg.endsWith("\n")) {
|
||||||
msg += "\n";
|
msg += "\n";
|
||||||
@@ -58,7 +65,7 @@ public class AliasCommand extends BaseCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void begin(Environment env) throws UnloggedFailure, IOException {
|
private void begin(Environment env) throws IOException, Failure {
|
||||||
Map<String, CommandProvider> map = root.getMap();
|
Map<String, CommandProvider> map = root.getMap();
|
||||||
for (String name : chain(command)) {
|
for (String name : chain(command)) {
|
||||||
CommandProvider p = map.get(name);
|
CommandProvider p = map.get(name);
|
||||||
@@ -103,17 +110,16 @@ public class AliasCommand extends BaseCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkRequiresCapability(Command cmd) throws UnloggedFailure {
|
private void checkRequiresCapability(Command cmd) throws Failure {
|
||||||
RequiresCapability rc = cmd.getClass().getAnnotation(RequiresCapability.class);
|
try {
|
||||||
if (rc != null) {
|
Set<GlobalOrPluginPermission> check = GlobalPermission.fromAnnotation(cmd.getClass());
|
||||||
CapabilityControl ctl = currentUser.getCapabilities();
|
try {
|
||||||
if (!ctl.canPerform(rc.value()) && !ctl.canAdministrateServer()) {
|
permissionBackend.user(currentUser).checkAny(check);
|
||||||
String msg =
|
} catch (AuthException err) {
|
||||||
String.format(
|
throw new UnloggedFailure(BaseCommand.STATUS_NOT_ADMIN, "fatal: " + err.getMessage());
|
||||||
"fatal: %s does not have \"%s\" capability.",
|
|
||||||
currentUser.getUserName(), rc.value());
|
|
||||||
throw new UnloggedFailure(BaseCommand.STATUS_NOT_ADMIN, msg);
|
|
||||||
}
|
}
|
||||||
|
} catch (PermissionBackendException err) {
|
||||||
|
throw new Failure(1, "fatal: permissions unavailable", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
package com.google.gerrit.sshd;
|
package com.google.gerrit.sshd;
|
||||||
|
|
||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import org.apache.sshd.server.Command;
|
import org.apache.sshd.server.Command;
|
||||||
@@ -27,6 +28,7 @@ public class AliasCommandProvider implements Provider<Command> {
|
|||||||
@CommandName(Commands.ROOT)
|
@CommandName(Commands.ROOT)
|
||||||
private DispatchCommandProvider root;
|
private DispatchCommandProvider root;
|
||||||
|
|
||||||
|
@Inject private PermissionBackend permissionBackend;
|
||||||
@Inject private CurrentUser currentUser;
|
@Inject private CurrentUser currentUser;
|
||||||
|
|
||||||
public AliasCommandProvider(CommandName command) {
|
public AliasCommandProvider(CommandName command) {
|
||||||
@@ -35,6 +37,6 @@ public class AliasCommandProvider implements Provider<Command> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Command get() {
|
public Command get() {
|
||||||
return new AliasCommand(root, currentUser, command);
|
return new AliasCommand(root, permissionBackend, currentUser, command);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,8 +20,10 @@ import com.google.common.collect.Sets;
|
|||||||
import com.google.common.util.concurrent.Atomics;
|
import com.google.common.util.concurrent.Atomics;
|
||||||
import com.google.gerrit.extensions.restapi.AuthException;
|
import com.google.gerrit.extensions.restapi.AuthException;
|
||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
import com.google.gerrit.server.account.CapabilityUtils;
|
|
||||||
import com.google.gerrit.server.args4j.SubcommandHandler;
|
import com.google.gerrit.server.args4j.SubcommandHandler;
|
||||||
|
import com.google.gerrit.server.permissions.GlobalPermission;
|
||||||
|
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.assistedinject.Assisted;
|
import com.google.inject.assistedinject.Assisted;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -41,6 +43,7 @@ final class DispatchCommand extends BaseCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final CurrentUser currentUser;
|
private final CurrentUser currentUser;
|
||||||
|
private final PermissionBackend permissionBackend;
|
||||||
private final Map<String, CommandProvider> commands;
|
private final Map<String, CommandProvider> commands;
|
||||||
private final AtomicReference<Command> atomicCmd;
|
private final AtomicReference<Command> atomicCmd;
|
||||||
|
|
||||||
@@ -51,8 +54,12 @@ final class DispatchCommand extends BaseCommand {
|
|||||||
private List<String> args = new ArrayList<>();
|
private List<String> args = new ArrayList<>();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
DispatchCommand(CurrentUser cu, @Assisted final Map<String, CommandProvider> all) {
|
DispatchCommand(
|
||||||
currentUser = cu;
|
CurrentUser user,
|
||||||
|
PermissionBackend permissionBackend,
|
||||||
|
@Assisted Map<String, CommandProvider> all) {
|
||||||
|
this.currentUser = user;
|
||||||
|
this.permissionBackend = permissionBackend;
|
||||||
commands = all;
|
commands = all;
|
||||||
atomicCmd = Atomics.newReference();
|
atomicCmd = Atomics.newReference();
|
||||||
}
|
}
|
||||||
@@ -117,9 +124,13 @@ final class DispatchCommand extends BaseCommand {
|
|||||||
pluginName = ((BaseCommand) cmd).getPluginName();
|
pluginName = ((BaseCommand) cmd).getPluginName();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
CapabilityUtils.checkRequiresCapability(currentUser, pluginName, cmd.getClass());
|
permissionBackend
|
||||||
|
.user(currentUser)
|
||||||
|
.checkAny(GlobalPermission.fromAnnotation(pluginName, cmd.getClass()));
|
||||||
} catch (AuthException e) {
|
} catch (AuthException e) {
|
||||||
throw new UnloggedFailure(BaseCommand.STATUS_NOT_ADMIN, e.getMessage());
|
throw new UnloggedFailure(BaseCommand.STATUS_NOT_ADMIN, e.getMessage());
|
||||||
|
} catch (PermissionBackendException e) {
|
||||||
|
throw new UnloggedFailure(1, "fatal: permission check unavailable", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user