blob: c0b44e5fb7be4280551e0632be1d7f1754e43f8d [file] [log] [blame]
// 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.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 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.
*
* <p>See also {@link GlobalCapability} which lists the equivalent strings used in the
* refs/meta/config settings in All-Projects.
*/
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_ACCESS,
VIEW_ALL_ACCOUNTS,
VIEW_CACHES,
VIEW_CONNECTIONS,
VIEW_PLUGINS,
VIEW_QUEUE;
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);
}
}