Line data Source code
1 : // Copyright (C) 2012 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.account;
16 :
17 : import static java.util.stream.Collectors.toSet;
18 :
19 : import com.google.common.flogger.FluentLogger;
20 : import com.google.gerrit.common.UsedAt;
21 : import com.google.gerrit.entities.Account;
22 : import com.google.gerrit.entities.AccountGroup;
23 : import com.google.gerrit.entities.AccountsSection;
24 : import com.google.gerrit.entities.PermissionRule;
25 : import com.google.gerrit.exceptions.NoSuchGroupException;
26 : import com.google.gerrit.extensions.common.AccountVisibility;
27 : import com.google.gerrit.server.CurrentUser;
28 : import com.google.gerrit.server.IdentifiedUser;
29 : import com.google.gerrit.server.group.SystemGroupBackend;
30 : import com.google.gerrit.server.permissions.GlobalPermission;
31 : import com.google.gerrit.server.permissions.PermissionBackend;
32 : import com.google.gerrit.server.permissions.PermissionBackendException;
33 : import com.google.gerrit.server.project.ProjectCache;
34 : import com.google.inject.Inject;
35 : import com.google.inject.Provider;
36 : import java.util.Set;
37 :
38 : /** Access control management for one account's access to other accounts. */
39 : public class AccountControl {
40 68 : private static final FluentLogger logger = FluentLogger.forEnclosingClass();
41 :
42 : public static class Factory {
43 : private final PermissionBackend permissionBackend;
44 : private final ProjectCache projectCache;
45 : private final GroupControl.Factory groupControlFactory;
46 : private final Provider<CurrentUser> user;
47 : private final IdentifiedUser.GenericFactory userFactory;
48 : private final AccountVisibility accountVisibility;
49 :
50 : @Inject
51 : Factory(
52 : PermissionBackend permissionBackend,
53 : ProjectCache projectCache,
54 : GroupControl.Factory groupControlFactory,
55 : Provider<CurrentUser> user,
56 : IdentifiedUser.GenericFactory userFactory,
57 151 : AccountVisibility accountVisibility) {
58 151 : this.permissionBackend = permissionBackend;
59 151 : this.projectCache = projectCache;
60 151 : this.groupControlFactory = groupControlFactory;
61 151 : this.user = user;
62 151 : this.userFactory = userFactory;
63 151 : this.accountVisibility = accountVisibility;
64 151 : }
65 :
66 : /**
67 : * Creates a {@link AccountControl} instance that checks whether the current user can see other
68 : * accounts.
69 : */
70 : public AccountControl get() {
71 68 : return new AccountControl(
72 : permissionBackend,
73 : projectCache,
74 : groupControlFactory,
75 68 : user.get(),
76 : userFactory,
77 : accountVisibility);
78 : }
79 :
80 : /**
81 : * Creates a {@link AccountControl} instance that checks whether the given user can see other
82 : * accounts.
83 : */
84 : @UsedAt(UsedAt.Project.PLUGIN_CODE_OWNERS)
85 : public AccountControl get(IdentifiedUser identifiedUser) {
86 1 : return new AccountControl(
87 : permissionBackend,
88 : projectCache,
89 : groupControlFactory,
90 : identifiedUser,
91 : userFactory,
92 : accountVisibility);
93 : }
94 : }
95 :
96 : private final AccountsSection accountsSection;
97 : private final GroupControl.Factory groupControlFactory;
98 : private final PermissionBackend.WithUser perm;
99 : private final CurrentUser user;
100 : private final IdentifiedUser.GenericFactory userFactory;
101 : private final AccountVisibility accountVisibility;
102 :
103 : private Boolean viewAll;
104 :
105 : private AccountControl(
106 : PermissionBackend permissionBackend,
107 : ProjectCache projectCache,
108 : GroupControl.Factory groupControlFactory,
109 : CurrentUser user,
110 : IdentifiedUser.GenericFactory userFactory,
111 68 : AccountVisibility accountVisibility) {
112 68 : this.accountsSection = projectCache.getAllProjects().getConfig().getAccountsSection();
113 68 : this.groupControlFactory = groupControlFactory;
114 68 : this.perm = permissionBackend.user(user);
115 68 : this.user = user;
116 68 : this.userFactory = userFactory;
117 68 : this.accountVisibility = accountVisibility;
118 68 : }
119 :
120 : public CurrentUser getUser() {
121 36 : return user;
122 : }
123 :
124 : /**
125 : * Returns true if the current user is allowed to see the otherUser, based on the account
126 : * visibility policy. Depending on the group membership realms supported, this may not be able to
127 : * determine SAME_GROUP or VISIBLE_GROUP correctly (defaulting to not being visible). This is
128 : * because {@link GroupMembership#getKnownGroups()} may only return a subset of the effective
129 : * groups.
130 : */
131 : public boolean canSee(Account.Id otherUser) {
132 3 : return canSee(
133 3 : new OtherUser() {
134 : @Override
135 : Account.Id getId() {
136 3 : return otherUser;
137 : }
138 :
139 : @Override
140 : IdentifiedUser createUser() {
141 3 : return userFactory.create(otherUser);
142 : }
143 : });
144 : }
145 :
146 : /**
147 : * Returns true if the current user is allowed to see the otherUser, based on the account
148 : * visibility policy. Depending on the group membership realms supported, this may not be able to
149 : * determine SAME_GROUP or VISIBLE_GROUP correctly (defaulting to not being visible). This is
150 : * because {@link GroupMembership#getKnownGroups()} may only return a subset of the effective
151 : * groups.
152 : */
153 : public boolean canSee(AccountState otherUser) {
154 67 : return canSee(
155 67 : new OtherUser() {
156 : @Override
157 : Account.Id getId() {
158 67 : return otherUser.account().id();
159 : }
160 :
161 : @Override
162 : IdentifiedUser createUser() {
163 7 : return userFactory.create(otherUser);
164 : }
165 : });
166 : }
167 :
168 : private boolean canSee(OtherUser otherUser) {
169 67 : if (accountVisibility == AccountVisibility.ALL) {
170 66 : logger.atFine().log(
171 : "user %s can see account %d (accountVisibility = %s)",
172 66 : user.getLoggableName(), otherUser.getId().get(), AccountVisibility.ALL);
173 66 : return true;
174 13 : } else if (user.isIdentifiedUser() && user.getAccountId().equals(otherUser.getId())) {
175 : // I can always see myself.
176 5 : logger.atFine().log(
177 5 : "user %s can see own account %d", user.getLoggableName(), otherUser.getId().get());
178 5 : return true;
179 13 : } else if (canViewAll()) {
180 9 : logger.atFine().log(
181 : "user %s can see account %d (view all accounts = true)",
182 9 : user.getLoggableName(), otherUser.getId().get());
183 9 : return true;
184 : }
185 :
186 13 : switch (accountVisibility) {
187 : case SAME_GROUP:
188 : {
189 9 : Set<AccountGroup.UUID> usersGroups = groupsOf(otherUser.getUser());
190 9 : for (PermissionRule rule : accountsSection.getSameGroupVisibility()) {
191 0 : if (rule.isBlock() || rule.isDeny()) {
192 0 : logger.atFine().log(
193 : "ignoring group %s of user %s for %s account visibility check"
194 : + " because there is a blocked/denied sameGroupVisibility rule: %s",
195 0 : rule.getGroup().getUUID(),
196 0 : otherUser.getUser().getLoggableName(),
197 : AccountVisibility.SAME_GROUP,
198 : rule);
199 0 : usersGroups.remove(rule.getGroup().getUUID());
200 : }
201 0 : }
202 :
203 9 : if (user.getEffectiveGroups().containsAnyOf(usersGroups)) {
204 2 : logger.atFine().log(
205 : "user %s can see account %d because they share a group (accountVisibility = %s)",
206 2 : user.getLoggableName(), otherUser.getId().get(), AccountVisibility.SAME_GROUP);
207 2 : return true;
208 : }
209 :
210 9 : logger.atFine().log(
211 : "user %s cannot see account %d because they don't share a group"
212 : + " (accountVisibility = %s)",
213 9 : user.getLoggableName(), otherUser.getId().get(), AccountVisibility.SAME_GROUP);
214 9 : logger.atFine().log("groups of user %s: %s", user.getLoggableName(), groupsOf(user));
215 9 : logger.atFine().log(
216 9 : "groups of other user %s: %s", otherUser.getUser().getLoggableName(), usersGroups);
217 9 : return false;
218 : }
219 : case VISIBLE_GROUP:
220 : {
221 0 : Set<AccountGroup.UUID> usersGroups = groupsOf(otherUser.getUser());
222 0 : for (AccountGroup.UUID usersGroup : usersGroups) {
223 : try {
224 0 : if (groupControlFactory.controlFor(usersGroup).isVisible()) {
225 0 : logger.atFine().log(
226 : "user %s can see account %d because it is member of the visible group %s"
227 : + " (accountVisibility = %s)",
228 0 : user.getLoggableName(),
229 0 : otherUser.getId().get(),
230 0 : usersGroup.get(),
231 : AccountVisibility.VISIBLE_GROUP);
232 0 : return true;
233 : }
234 0 : } catch (NoSuchGroupException e) {
235 0 : continue;
236 0 : }
237 0 : }
238 :
239 0 : logger.atFine().log(
240 : "user %s cannot see account %d because none of its groups are visible"
241 : + " (accountVisibility = %s)",
242 0 : user.getLoggableName(), otherUser.getId().get(), AccountVisibility.VISIBLE_GROUP);
243 0 : logger.atFine().log(
244 0 : "groups of other user %s: %s", otherUser.getUser().getLoggableName(), usersGroups);
245 0 : return false;
246 : }
247 : case NONE:
248 4 : logger.atFine().log(
249 : "user %s cannot see account %d (accountVisibility = %s)",
250 4 : user.getLoggableName(), otherUser.getId().get(), AccountVisibility.NONE);
251 4 : return false;
252 : case ALL:
253 : default:
254 0 : throw new IllegalStateException("Bad AccountVisibility " + accountVisibility);
255 : }
256 : }
257 :
258 : public boolean canViewAll() {
259 13 : if (viewAll == null) {
260 : try {
261 13 : viewAll = perm.test(GlobalPermission.VIEW_ALL_ACCOUNTS);
262 0 : } catch (PermissionBackendException e) {
263 0 : logger.atFine().withCause(e).log(
264 : "Failed to check %s global capability for user %s",
265 0 : GlobalPermission.VIEW_ALL_ACCOUNTS, user.getLoggableName());
266 0 : viewAll = false;
267 13 : }
268 : }
269 13 : return viewAll;
270 : }
271 :
272 : private Set<AccountGroup.UUID> groupsOf(CurrentUser user) {
273 9 : return user.getEffectiveGroups().getKnownGroups().stream()
274 9 : .filter(a -> !SystemGroupBackend.isSystemGroup(a))
275 9 : .collect(toSet());
276 : }
277 :
278 : private abstract static class OtherUser {
279 : IdentifiedUser user;
280 :
281 : IdentifiedUser getUser() {
282 9 : if (user == null) {
283 9 : user = createUser();
284 : }
285 9 : return user;
286 : }
287 :
288 : abstract IdentifiedUser createUser();
289 :
290 : abstract Account.Id getId();
291 : }
292 : }
|