LCOV - code coverage report
Current view: top level - server/restapi/project - CheckAccess.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 62 63 98.4 %
Date: 2022-11-19 15:00:39 Functions: 4 4 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2018 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.restapi.project;
      16             : 
      17             : import static com.google.gerrit.entities.RefNames.REFS_HEADS;
      18             : 
      19             : import com.google.common.base.Strings;
      20             : import com.google.common.collect.ImmutableList;
      21             : import com.google.gerrit.entities.Account;
      22             : import com.google.gerrit.entities.BranchNameKey;
      23             : import com.google.gerrit.extensions.api.config.AccessCheckInfo;
      24             : import com.google.gerrit.extensions.api.config.AccessCheckInput;
      25             : import com.google.gerrit.extensions.restapi.AuthException;
      26             : import com.google.gerrit.extensions.restapi.BadRequestException;
      27             : import com.google.gerrit.extensions.restapi.Response;
      28             : import com.google.gerrit.extensions.restapi.RestApiException;
      29             : import com.google.gerrit.extensions.restapi.RestReadView;
      30             : import com.google.gerrit.server.account.AccountResolver;
      31             : import com.google.gerrit.server.git.GitRepositoryManager;
      32             : import com.google.gerrit.server.logging.TraceContext;
      33             : import com.google.gerrit.server.permissions.DefaultPermissionMappings;
      34             : import com.google.gerrit.server.permissions.GlobalPermission;
      35             : import com.google.gerrit.server.permissions.PermissionBackend;
      36             : import com.google.gerrit.server.permissions.PermissionBackendException;
      37             : import com.google.gerrit.server.permissions.ProjectPermission;
      38             : import com.google.gerrit.server.permissions.RefPermission;
      39             : import com.google.gerrit.server.project.ProjectResource;
      40             : import com.google.inject.Inject;
      41             : import java.io.IOException;
      42             : import java.util.Optional;
      43             : import javax.servlet.http.HttpServletResponse;
      44             : import org.eclipse.jgit.errors.ConfigInvalidException;
      45             : import org.eclipse.jgit.lib.Repository;
      46             : import org.kohsuke.args4j.Option;
      47             : 
      48             : public class CheckAccess implements RestReadView<ProjectResource> {
      49             :   private final AccountResolver accountResolver;
      50             :   private final PermissionBackend permissionBackend;
      51             :   private final GitRepositoryManager gitRepositoryManager;
      52             : 
      53             :   @Inject
      54             :   CheckAccess(
      55             :       AccountResolver resolver,
      56             :       PermissionBackend permissionBackend,
      57         143 :       GitRepositoryManager gitRepositoryManager) {
      58         143 :     this.accountResolver = resolver;
      59         143 :     this.permissionBackend = permissionBackend;
      60         143 :     this.gitRepositoryManager = gitRepositoryManager;
      61         143 :   }
      62             : 
      63             :   @Option(name = "--ref", usage = "ref name to check permission for")
      64             :   String refName;
      65             : 
      66             :   @Option(name = "--account", usage = "account to check acccess for")
      67             :   String account;
      68             : 
      69             :   @Option(name = "--perm", usage = "permission to check; default: read of any ref.")
      70             :   String permission;
      71             : 
      72             :   public Response<AccessCheckInfo> apply(ProjectResource rsrc, AccessCheckInput input)
      73             :       throws PermissionBackendException, RestApiException, IOException, ConfigInvalidException {
      74           2 :     permissionBackend.user(rsrc.getUser()).check(GlobalPermission.VIEW_ACCESS);
      75             : 
      76           2 :     rsrc.getProjectState().checkStatePermitsRead();
      77             : 
      78           2 :     if (input == null) {
      79           0 :       throw new BadRequestException("input is required");
      80             :     }
      81           2 :     if (Strings.isNullOrEmpty(input.account)) {
      82           2 :       throw new BadRequestException("input requires 'account'");
      83             :     }
      84             : 
      85           1 :     try (TraceContext traceContext = TraceContext.open()) {
      86           1 :       traceContext.enableAclLogging();
      87             : 
      88           1 :       Account.Id match = accountResolver.resolve(input.account).asUnique().account().id();
      89             : 
      90             :       try {
      91           1 :         permissionBackend
      92           1 :             .absentUser(match)
      93           1 :             .project(rsrc.getNameKey())
      94           1 :             .check(ProjectPermission.ACCESS);
      95           1 :       } catch (AuthException e) {
      96           1 :         return Response.ok(
      97           1 :             createInfo(
      98             :                 HttpServletResponse.SC_FORBIDDEN,
      99           1 :                 String.format("user %s cannot see project %s", match, rsrc.getName())));
     100           1 :       }
     101             :       RefPermission refPerm;
     102           1 :       if (!Strings.isNullOrEmpty(input.permission)) {
     103           1 :         if (Strings.isNullOrEmpty(input.ref)) {
     104           1 :           throw new BadRequestException("must set 'ref' when specifying 'permission'");
     105             :         }
     106           1 :         Optional<RefPermission> rp = DefaultPermissionMappings.refPermission(input.permission);
     107           1 :         if (!rp.isPresent()) {
     108           1 :           throw new BadRequestException(
     109           1 :               String.format("'%s' is not recognized as ref permission", input.permission));
     110             :         }
     111             : 
     112           1 :         refPerm = rp.get();
     113           1 :       } else {
     114           1 :         refPerm = RefPermission.READ;
     115             :       }
     116             : 
     117           1 :       String message = null;
     118           1 :       if (!Strings.isNullOrEmpty(input.ref)) {
     119             :         try {
     120           1 :           permissionBackend
     121           1 :               .absentUser(match)
     122           1 :               .ref(BranchNameKey.create(rsrc.getNameKey(), input.ref))
     123           1 :               .check(refPerm);
     124           1 :         } catch (AuthException e) {
     125           1 :           return Response.ok(
     126           1 :               createInfo(
     127             :                   HttpServletResponse.SC_FORBIDDEN,
     128           1 :                   String.format(
     129             :                       "user %s lacks permission %s for %s in project %s",
     130           1 :                       match, input.permission, input.ref, rsrc.getName())));
     131           1 :         }
     132             :       } else {
     133             :         // We say access is okay if there are no refs, but this warrants a warning,
     134             :         // as access denied looks the same as no branches to the user.
     135           1 :         try (Repository repo = gitRepositoryManager.openRepository(rsrc.getNameKey())) {
     136           1 :           if (repo.getRefDatabase().getRefsByPrefix(REFS_HEADS).isEmpty()) {
     137           1 :             message = "access is OK, but repository has no branches under refs/heads/";
     138             :           }
     139             :         }
     140             :       }
     141           1 :       return Response.ok(createInfo(HttpServletResponse.SC_OK, message));
     142           1 :     }
     143             :   }
     144             : 
     145             :   private AccessCheckInfo createInfo(int statusCode, String message) {
     146           1 :     AccessCheckInfo info = new AccessCheckInfo();
     147           1 :     info.status = statusCode;
     148           1 :     info.message = message;
     149           1 :     info.debugLogs = TraceContext.getAclLogRecords();
     150           1 :     if (info.debugLogs.isEmpty()) {
     151           1 :       info.debugLogs =
     152           1 :           ImmutableList.of("Found no rules that apply, so defaulting to no permission");
     153             :     }
     154           1 :     return info;
     155             :   }
     156             : 
     157             :   @Override
     158             :   public Response<AccessCheckInfo> apply(ProjectResource rsrc)
     159             :       throws PermissionBackendException, RestApiException, IOException, ConfigInvalidException {
     160             : 
     161           2 :     AccessCheckInput input = new AccessCheckInput();
     162           2 :     input.ref = refName;
     163           2 :     input.account = account;
     164           2 :     input.permission = permission;
     165             : 
     166           1 :     return apply(rsrc, input);
     167             :   }
     168             : }

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