LCOV - code coverage report
Current view: top level - server/account - AccountControl.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 67 95 70.5 %
Date: 2022-11-19 15:00:39 Functions: 19 19 100.0 %

          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             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750