// Copyright (C) 2012 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.account; import static java.util.stream.Collectors.toSet; import com.google.gerrit.common.data.PermissionRule; import com.google.gerrit.common.errors.NoSuchGroupException; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.git.AccountsSection; import com.google.gerrit.server.group.SystemGroupBackend; import com.google.gerrit.server.project.ProjectCache; import com.google.inject.Inject; import com.google.inject.Provider; import java.util.Set; /** Access control management for one account's access to other accounts. */ public class AccountControl { public static class Factory { private final ProjectCache projectCache; private final GroupControl.Factory groupControlFactory; private final Provider<CurrentUser> user; private final IdentifiedUser.GenericFactory userFactory; private final AccountVisibility accountVisibility; @Inject Factory( final ProjectCache projectCache, final GroupControl.Factory groupControlFactory, final Provider<CurrentUser> user, final IdentifiedUser.GenericFactory userFactory, final AccountVisibility accountVisibility) { this.projectCache = projectCache; this.groupControlFactory = groupControlFactory; this.user = user; this.userFactory = userFactory; this.accountVisibility = accountVisibility; } public AccountControl get() { return new AccountControl( projectCache, groupControlFactory, user.get(), userFactory, accountVisibility); } } private final AccountsSection accountsSection; private final GroupControl.Factory groupControlFactory; private final CurrentUser user; private final IdentifiedUser.GenericFactory userFactory; private final AccountVisibility accountVisibility; AccountControl( final ProjectCache projectCache, final GroupControl.Factory groupControlFactory, final CurrentUser user, final IdentifiedUser.GenericFactory userFactory, final AccountVisibility accountVisibility) { this.accountsSection = projectCache.getAllProjects().getConfig().getAccountsSection(); this.groupControlFactory = groupControlFactory; this.user = user; this.userFactory = userFactory; this.accountVisibility = accountVisibility; } public CurrentUser getUser() { return user; } /** * Returns true if the current user is allowed to see the otherUser, based on the account * visibility policy. Depending on the group membership realms supported, this may not be able to * determine SAME_GROUP or VISIBLE_GROUP correctly (defaulting to not being visible). This is * because {@link GroupMembership#getKnownGroups()} may only return a subset of the effective * groups. */ public boolean canSee(Account otherUser) { return canSee(otherUser.getId()); } /** * Returns true if the current user is allowed to see the otherUser, based on the account * visibility policy. Depending on the group membership realms supported, this may not be able to * determine SAME_GROUP or VISIBLE_GROUP correctly (defaulting to not being visible). This is * because {@link GroupMembership#getKnownGroups()} may only return a subset of the effective * groups. */ public boolean canSee(final Account.Id otherUser) { return canSee( new OtherUser() { @Override Account.Id getId() { return otherUser; } @Override IdentifiedUser createUser() { return userFactory.create(otherUser); } }); } /** * Returns true if the current user is allowed to see the otherUser, based on the account * visibility policy. Depending on the group membership realms supported, this may not be able to * determine SAME_GROUP or VISIBLE_GROUP correctly (defaulting to not being visible). This is * because {@link GroupMembership#getKnownGroups()} may only return a subset of the effective * groups. */ public boolean canSee(final AccountState otherUser) { return canSee( new OtherUser() { @Override Account.Id getId() { return otherUser.getAccount().getId(); } @Override IdentifiedUser createUser() { return userFactory.create(otherUser); } }); } private boolean canSee(OtherUser otherUser) { // Special case: I can always see myself. if (user.isIdentifiedUser() && user.getAccountId().equals(otherUser.getId())) { return true; } if (user.getCapabilities().canViewAllAccounts()) { return true; } switch (accountVisibility) { case ALL: return true; case SAME_GROUP: { Set<AccountGroup.UUID> usersGroups = groupsOf(otherUser.getUser()); for (PermissionRule rule : accountsSection.getSameGroupVisibility()) { if (rule.isBlock() || rule.isDeny()) { usersGroups.remove(rule.getGroup().getUUID()); } } if (user.getEffectiveGroups().containsAnyOf(usersGroups)) { return true; } break; } case VISIBLE_GROUP: { Set<AccountGroup.UUID> usersGroups = groupsOf(otherUser.getUser()); for (AccountGroup.UUID usersGroup : usersGroups) { try { if (groupControlFactory.controlFor(usersGroup).isVisible()) { return true; } } catch (NoSuchGroupException e) { continue; } } break; } case NONE: break; default: throw new IllegalStateException("Bad AccountVisibility " + accountVisibility); } return false; } private Set<AccountGroup.UUID> groupsOf(IdentifiedUser user) { return user.getEffectiveGroups() .getKnownGroups() .stream() .filter(a -> !SystemGroupBackend.isSystemGroup(a)) .collect(toSet()); } private abstract static class OtherUser { IdentifiedUser user; IdentifiedUser getUser() { if (user == null) { user = createUser(); } return user; } abstract IdentifiedUser createUser(); abstract Account.Id getId(); } }