Line data Source code
1 : // Copyright (C) 2017 The Android Open Source Project
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : package com.google.gerrit.server.permissions;
16 :
17 : import static com.google.gerrit.server.permissions.DefaultPermissionMappings.globalPermission;
18 :
19 : import com.google.common.flogger.FluentLogger;
20 : import com.google.gerrit.common.Nullable;
21 : import com.google.gerrit.common.data.GlobalCapability;
22 : import com.google.gerrit.extensions.annotations.CapabilityScope;
23 : import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
24 : import com.google.gerrit.extensions.annotations.RequiresCapability;
25 : import com.google.gerrit.extensions.api.access.GerritPermission;
26 : import com.google.gerrit.extensions.api.access.GlobalOrPluginPermission;
27 : import com.google.gerrit.extensions.api.access.PluginPermission;
28 : import com.google.gerrit.extensions.registration.PluginName;
29 : import java.lang.annotation.Annotation;
30 : import java.util.Collections;
31 : import java.util.LinkedHashSet;
32 : import java.util.Optional;
33 : import java.util.Set;
34 :
35 : /**
36 : * Global server permissions built into Gerrit.
37 : *
38 : * <p>See also {@link GlobalCapability} which lists the equivalent strings used in the
39 : * refs/meta/config settings in All-Projects.
40 : */
41 150 : public enum GlobalPermission implements GlobalOrPluginPermission {
42 150 : ACCESS_DATABASE,
43 150 : ADMINISTRATE_SERVER,
44 150 : CREATE_ACCOUNT,
45 150 : CREATE_GROUP,
46 150 : CREATE_PROJECT,
47 150 : EMAIL_REVIEWERS,
48 150 : FLUSH_CACHES,
49 150 : KILL_TASK,
50 150 : MAINTAIN_SERVER,
51 150 : MODIFY_ACCOUNT,
52 150 : READ_AS,
53 150 : RUN_AS,
54 150 : RUN_GC,
55 150 : STREAM_EVENTS,
56 150 : VIEW_ACCESS,
57 150 : VIEW_ALL_ACCOUNTS,
58 150 : VIEW_CACHES,
59 150 : VIEW_CONNECTIONS,
60 150 : VIEW_PLUGINS,
61 150 : VIEW_QUEUE;
62 :
63 150 : private static final FluentLogger logger = FluentLogger.forEnclosingClass();
64 :
65 : /**
66 : * Extracts the {@code @RequiresCapability} or {@code @RequiresAnyCapability} annotation.
67 : *
68 : * @param pluginName name of the declaring plugin. May be {@code null} or {@code "gerrit"} for
69 : * classes originating from the core server.
70 : * @param clazz target class to extract annotation from.
71 : * @return empty set if no annotations were found, or a collection of permissions, any of which
72 : * are suitable to enable access.
73 : * @throws PermissionBackendException the annotation could not be parsed.
74 : */
75 : public static Set<GlobalOrPluginPermission> fromAnnotation(
76 : @Nullable String pluginName, Class<?> clazz) throws PermissionBackendException {
77 149 : RequiresCapability rc = findAnnotation(clazz, RequiresCapability.class);
78 149 : RequiresAnyCapability rac = findAnnotation(clazz, RequiresAnyCapability.class);
79 149 : if (rc != null && rac != null) {
80 0 : logger.atSevere().log(
81 : "Class %s uses both @%s and @%s",
82 0 : clazz.getName(),
83 0 : RequiresCapability.class.getSimpleName(),
84 0 : RequiresAnyCapability.class.getSimpleName());
85 0 : throw new PermissionBackendException("cannot extract permission");
86 149 : } else if (rc != null) {
87 148 : return Collections.singleton(
88 148 : resolve(
89 : pluginName,
90 148 : rc.value(),
91 148 : rc.scope(),
92 148 : rc.fallBackToAdmin(),
93 : clazz,
94 : RequiresCapability.class));
95 79 : } else if (rac != null) {
96 2 : Set<GlobalOrPluginPermission> r = new LinkedHashSet<>();
97 2 : for (String capability : rac.value()) {
98 2 : r.add(
99 2 : resolve(
100 : pluginName,
101 : capability,
102 2 : rac.scope(),
103 2 : rac.fallBackToAdmin(),
104 : clazz,
105 : RequiresAnyCapability.class));
106 : }
107 2 : return Collections.unmodifiableSet(r);
108 : } else {
109 79 : return Collections.emptySet();
110 : }
111 : }
112 :
113 : public static Set<GlobalOrPluginPermission> fromAnnotation(Class<?> clazz)
114 : throws PermissionBackendException {
115 148 : return fromAnnotation(null, clazz);
116 : }
117 :
118 : private static GlobalOrPluginPermission resolve(
119 : @Nullable String pluginName,
120 : String capability,
121 : CapabilityScope scope,
122 : boolean fallBackToAdmin,
123 : Class<?> clazz,
124 : Class<?> annotationClass)
125 : throws PermissionBackendException {
126 148 : if (pluginName != null
127 25 : && !PluginName.GERRIT.equals(pluginName)
128 : && (scope == CapabilityScope.PLUGIN || scope == CapabilityScope.CONTEXT)) {
129 1 : return new PluginPermission(pluginName, capability, fallBackToAdmin);
130 : }
131 :
132 148 : if (scope == CapabilityScope.PLUGIN) {
133 0 : logger.atSevere().log(
134 : "Class %s uses @%s(scope=%s), but is not within a plugin",
135 0 : clazz.getName(), annotationClass.getSimpleName(), scope.name());
136 0 : throw new PermissionBackendException("cannot extract permission");
137 : }
138 :
139 148 : Optional<GlobalPermission> perm = globalPermission(capability);
140 148 : if (!perm.isPresent()) {
141 0 : logger.atSevere().log("Class %s requires unknown capability %s", clazz.getName(), capability);
142 0 : throw new PermissionBackendException("cannot extract permission");
143 : }
144 148 : return perm.get();
145 : }
146 :
147 : @Nullable
148 : private static <T extends Annotation> T findAnnotation(Class<?> clazz, Class<T> annotation) {
149 149 : for (; clazz != null; clazz = clazz.getSuperclass()) {
150 149 : T t = clazz.getAnnotation(annotation);
151 149 : if (t != null) {
152 148 : return t;
153 : }
154 : }
155 149 : return null;
156 : }
157 :
158 : @Override
159 : public String describeForException() {
160 25 : return GerritPermission.describeEnumValue(this);
161 : }
162 : }
|