diff options
Diffstat (limited to 'java/com/google/gerrit/server/permissions/GlobalPermission.java')
-rw-r--r-- | java/com/google/gerrit/server/permissions/GlobalPermission.java | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/java/com/google/gerrit/server/permissions/GlobalPermission.java b/java/com/google/gerrit/server/permissions/GlobalPermission.java new file mode 100644 index 0000000000..07c9e84e4e --- /dev/null +++ b/java/com/google/gerrit/server/permissions/GlobalPermission.java @@ -0,0 +1,156 @@ +// 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.gerrit.server.permissions.DefaultPermissionMappings.globalPermission; + +import com.google.common.flogger.FluentLogger; +import com.google.gerrit.common.Nullable; +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.api.access.GerritPermission; +import com.google.gerrit.extensions.api.access.GlobalOrPluginPermission; +import com.google.gerrit.extensions.api.access.PluginPermission; +import com.google.gerrit.extensions.registration.PluginName; +import java.lang.annotation.Annotation; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Optional; +import java.util.Set; + +/** Global server permissions built into Gerrit. */ +public enum GlobalPermission implements GlobalOrPluginPermission { + ACCESS_DATABASE, + ADMINISTRATE_SERVER, + CREATE_ACCOUNT, + CREATE_GROUP, + CREATE_PROJECT, + EMAIL_REVIEWERS, + FLUSH_CACHES, + KILL_TASK, + MAINTAIN_SERVER, + MODIFY_ACCOUNT, + READ_AS, + RUN_AS, + RUN_GC, + STREAM_EVENTS, + VIEW_ALL_ACCOUNTS, + VIEW_CACHES, + VIEW_CONNECTIONS, + VIEW_PLUGINS, + VIEW_QUEUE, + VIEW_ACCESS; + + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + + /** + * 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) { + logger.atSevere().log( + "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(), + rc.fallBackToAdmin(), + clazz, + RequiresCapability.class)); + } else if (rac != null) { + Set<GlobalOrPluginPermission> r = new LinkedHashSet<>(); + for (String capability : rac.value()) { + r.add( + resolve( + pluginName, + capability, + rac.scope(), + rac.fallBackToAdmin(), + 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 static GlobalOrPluginPermission resolve( + @Nullable String pluginName, + String capability, + CapabilityScope scope, + boolean fallBackToAdmin, + Class<?> clazz, + Class<?> annotationClass) + throws PermissionBackendException { + if (pluginName != null + && !PluginName.GERRIT.equals(pluginName) + && (scope == CapabilityScope.PLUGIN || scope == CapabilityScope.CONTEXT)) { + return new PluginPermission(pluginName, capability, fallBackToAdmin); + } + + if (scope == CapabilityScope.PLUGIN) { + logger.atSevere().log( + "Class %s uses @%s(scope=%s), but is not within a plugin", + clazz.getName(), annotationClass.getSimpleName(), scope.name()); + throw new PermissionBackendException("cannot extract permission"); + } + + Optional<GlobalPermission> perm = globalPermission(capability); + if (!perm.isPresent()) { + logger.atSevere().log("Class %s requires unknown capability %s", clazz.getName(), capability); + throw new PermissionBackendException("cannot extract permission"); + } + return perm.get(); + } + + @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; + } + + @Override + public String describeForException() { + return GerritPermission.describeEnumValue(this); + } +} |