diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityControl.java index 03ffa67687..496ea1bd7b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityControl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityControl.java @@ -14,37 +14,20 @@ package com.google.gerrit.server.account; -import static com.google.common.base.Predicates.not; - -import com.google.common.base.Predicate; -import com.google.common.collect.FluentIterable; import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.common.data.PermissionRange; import com.google.gerrit.common.data.PermissionRule; -import com.google.gerrit.common.data.PermissionRule.Action; -import com.google.gerrit.extensions.api.access.GlobalOrPluginPermission; -import com.google.gerrit.extensions.api.access.PluginPermission; import com.google.gerrit.server.CurrentUser; -import com.google.gerrit.server.PeerDaemonUser; import com.google.gerrit.server.git.QueueProvider; 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.inject.Inject; import com.google.inject.Singleton; import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; /** Access control management for server-wide capabilities. */ public class CapabilityControl { - private static final CurrentUser.PropertyKey SELF = - CurrentUser.PropertyKey.create(); - @Singleton public static class Factory { private final ProjectCache projectCache; @@ -55,43 +38,16 @@ public class CapabilityControl { } public CapabilityControl create(CurrentUser user) { - CapabilityControl ctl = user.get(SELF); - if (ctl == null) { - ctl = new CapabilityControl(projectCache, user); - user.put(SELF, ctl); - } - return ctl; + return new CapabilityControl(projectCache, user); } } private final CapabilityCollection capabilities; private final CurrentUser user; - private final Map> effective; - private Boolean canAdministrateServer; private CapabilityControl(ProjectCache projectCache, CurrentUser currentUser) { capabilities = projectCache.getAllProjects().getCapabilityCollection(); user = currentUser; - effective = new HashMap<>(); - } - - private boolean isAdmin() { - if (canAdministrateServer == null) { - if (user.getRealUser() != user) { - canAdministrateServer = false; - } else { - canAdministrateServer = - user instanceof PeerDaemonUser - || matchAny(capabilities.administrateServer, ALLOWED_RULE); - } - } - return canAdministrateServer; - } - - /** @return true if the user can email reviewers. */ - private boolean canEmailReviewers() { - return matchAny(capabilities.emailReviewers, ALLOWED_RULE) - || !matchAny(capabilities.emailReviewers, not(ALLOWED_RULE)); } /** @return which priority queue the user's tasks should be submitted to. */ @@ -133,20 +89,15 @@ public class CapabilityControl { return QueueProvider.QueueType.INTERACTIVE; } - /** @return true if the user has this permission. */ - private boolean canPerform(String permissionName) { - return !access(permissionName).isEmpty(); - } - /** @return true if the user has a permission rule specifying the range. */ public boolean hasExplicitRange(String permission) { - return GlobalCapability.hasRange(permission) && !access(permission).isEmpty(); + return GlobalCapability.hasRange(permission) && !getRules(permission).isEmpty(); } /** The range of permitted values associated with a label permission. */ public PermissionRange getRange(String permission) { if (GlobalCapability.hasRange(permission)) { - return toRange(permission, access(permission)); + return toRange(permission, getRules(permission)); } return null; } @@ -169,14 +120,8 @@ public class CapabilityControl { return new PermissionRange(permissionName, min, max); } - /** Rules for the given permission, or the empty list. */ - private List access(String permissionName) { - List rules = effective.get(permissionName); - if (rules != null) { - return rules; - } - - rules = capabilities.getPermission(permissionName); + private List getRules(String permissionName) { + List rules = capabilities.getPermission(permissionName); GroupMembership groups = user.getEffectiveGroups(); List mine = new ArrayList<>(rules.size()); @@ -185,70 +130,10 @@ public class CapabilityControl { mine.add(rule); } } - - if (mine.isEmpty()) { - mine = Collections.emptyList(); - } - effective.put(permissionName, mine); return mine; } - private static final Predicate ALLOWED_RULE = r -> r.getAction() == Action.ALLOW; - - private boolean matchAny(Collection rules, Predicate predicate) { - return user.getEffectiveGroups() - .containsAnyOf( - FluentIterable.from(rules).filter(predicate).transform(r -> r.getGroup().getUUID())); - } - private static boolean match(GroupMembership groups, PermissionRule rule) { return groups.contains(rule.getGroup().getUUID()); } - - /** Do not use unless inside DefaultPermissionBackend. */ - public boolean doCanForDefaultPermissionBackend(GlobalOrPluginPermission perm) - throws PermissionBackendException { - if (perm instanceof GlobalPermission) { - return can((GlobalPermission) perm); - } else if (perm instanceof PluginPermission) { - PluginPermission pluginPermission = (PluginPermission) perm; - return canPerform(pluginPermission.permissionName()) - || (pluginPermission.fallBackToAdmin() && isAdmin()); - } - throw new PermissionBackendException(perm + " unsupported"); - } - - private boolean can(GlobalPermission perm) throws PermissionBackendException { - switch (perm) { - case ADMINISTRATE_SERVER: - return isAdmin(); - case EMAIL_REVIEWERS: - return canEmailReviewers(); - - case FLUSH_CACHES: - case KILL_TASK: - case RUN_GC: - case VIEW_CACHES: - case VIEW_QUEUE: - return canPerform(perm.permissionName()) - || canPerform(GlobalCapability.MAINTAIN_SERVER) - || isAdmin(); - - case CREATE_ACCOUNT: - case CREATE_GROUP: - case CREATE_PROJECT: - case MAINTAIN_SERVER: - case MODIFY_ACCOUNT: - case STREAM_EVENTS: - case VIEW_ALL_ACCOUNTS: - case VIEW_CONNECTIONS: - case VIEW_PLUGINS: - return canPerform(perm.permissionName()) || isAdmin(); - - case ACCESS_DATABASE: - case RUN_AS: - return canPerform(perm.permissionName()); - } - throw new PermissionBackendException(perm + " unsupported"); - } } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/DefaultPermissionBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/DefaultPermissionBackend.java index d263a2cd30..ef5e41da96 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/DefaultPermissionBackend.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/DefaultPermissionBackend.java @@ -15,14 +15,21 @@ package com.google.gerrit.server.project; import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.stream.Collectors.toSet; import com.google.common.collect.Sets; +import com.google.gerrit.common.data.PermissionRule; +import com.google.gerrit.common.data.PermissionRule.Action; import com.google.gerrit.extensions.api.access.GlobalOrPluginPermission; +import com.google.gerrit.extensions.api.access.PluginPermission; import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.server.CurrentUser; -import com.google.gerrit.server.account.CapabilityControl; +import com.google.gerrit.server.PeerDaemonUser; +import com.google.gerrit.server.account.CapabilityCollection; 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.PermissionBackendException; import com.google.inject.Inject; @@ -30,17 +37,22 @@ import com.google.inject.Singleton; import java.io.IOException; import java.util.Collection; import java.util.EnumSet; +import java.util.List; import java.util.Set; @Singleton public class DefaultPermissionBackend extends PermissionBackend { + private static final CurrentUser.PropertyKey IS_ADMIN = CurrentUser.PropertyKey.create(); + private final ProjectCache projectCache; - private final CapabilityControl.Factory capabilityFactory; @Inject - DefaultPermissionBackend(ProjectCache projectCache, CapabilityControl.Factory capabilityFactory) { + DefaultPermissionBackend(ProjectCache projectCache) { this.projectCache = projectCache; - this.capabilityFactory = capabilityFactory; + } + + private CapabilityCollection capabilities() { + return projectCache.getAllProjects().getCapabilityCollection(); } @Override @@ -50,7 +62,7 @@ public class DefaultPermissionBackend extends PermissionBackend { class WithUserImpl extends WithUser { private final CurrentUser user; - private CapabilityControl cap; + private Boolean admin; WithUserImpl(CurrentUser user) { this.user = checkNotNull(user, "user"); @@ -90,10 +102,97 @@ public class DefaultPermissionBackend extends PermissionBackend { } private boolean can(GlobalOrPluginPermission perm) throws PermissionBackendException { - if (cap == null) { - cap = capabilityFactory.create(user); + if (perm instanceof GlobalPermission) { + return can((GlobalPermission) perm); + } else if (perm instanceof PluginPermission) { + PluginPermission pluginPermission = (PluginPermission) perm; + return has(pluginPermission.permissionName()) + || (pluginPermission.fallBackToAdmin() && isAdmin()); } - return cap.doCanForDefaultPermissionBackend(perm); + throw new PermissionBackendException(perm + " unsupported"); + } + + private boolean can(GlobalPermission perm) throws PermissionBackendException { + switch (perm) { + case ADMINISTRATE_SERVER: + return isAdmin(); + case EMAIL_REVIEWERS: + return canEmailReviewers(); + + case FLUSH_CACHES: + case KILL_TASK: + case RUN_GC: + case VIEW_CACHES: + case VIEW_QUEUE: + return has(perm.permissionName()) || can(GlobalPermission.MAINTAIN_SERVER); + + case CREATE_ACCOUNT: + case CREATE_GROUP: + case CREATE_PROJECT: + case MAINTAIN_SERVER: + case MODIFY_ACCOUNT: + case STREAM_EVENTS: + case VIEW_ALL_ACCOUNTS: + case VIEW_CONNECTIONS: + case VIEW_PLUGINS: + return has(perm.permissionName()) || isAdmin(); + + case ACCESS_DATABASE: + case RUN_AS: + return has(perm.permissionName()); + } + throw new PermissionBackendException(perm + " unsupported"); + } + + private boolean isAdmin() { + if (admin == null) { + admin = computeAdmin(); + } + return admin; + } + + private Boolean computeAdmin() { + Boolean r = user.get(IS_ADMIN); + if (r == null) { + if (user.getRealUser() != user) { + r = false; + } else if (user instanceof PeerDaemonUser) { + r = true; + } else { + r = allow(capabilities().administrateServer); + } + user.put(IS_ADMIN, r); + } + return r; + } + + private boolean canEmailReviewers() { + List email = capabilities().emailReviewers; + return allow(email) || notDenied(email); + } + + private boolean has(String permissionName) { + return allow(capabilities().getPermission(permissionName)); + } + + private boolean allow(Collection rules) { + return user.getEffectiveGroups() + .containsAnyOf( + rules + .stream() + .filter(r -> r.getAction() == Action.ALLOW) + .map(r -> r.getGroup().getUUID()) + .collect(toSet())); + } + + private boolean notDenied(Collection rules) { + Set denied = + rules + .stream() + .filter(r -> r.getAction() != Action.ALLOW) + .map(r -> r.getGroup().getUUID()) + .collect(toSet()); + return denied.isEmpty() || !user.getEffectiveGroups().containsAnyOf(denied); } }