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

          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.project;
      16             : 
      17             : import com.google.common.collect.ImmutableList;
      18             : import com.google.common.flogger.FluentLogger;
      19             : import com.google.gerrit.entities.Project;
      20             : import com.google.gerrit.server.CurrentUser;
      21             : import com.google.gerrit.server.logging.Metadata;
      22             : import com.google.gerrit.server.logging.TraceContext;
      23             : import com.google.gerrit.server.logging.TraceContext.TraceTimer;
      24             : import com.google.gerrit.server.permissions.PermissionBackend;
      25             : import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
      26             : import com.google.gerrit.server.permissions.PermissionBackendException;
      27             : import com.google.inject.Inject;
      28             : import com.google.inject.Singleton;
      29             : import java.io.IOException;
      30             : import java.util.ArrayList;
      31             : import java.util.Collection;
      32             : import java.util.List;
      33             : import java.util.Optional;
      34             : import org.eclipse.jgit.errors.IncorrectObjectTypeException;
      35             : import org.eclipse.jgit.errors.MissingObjectException;
      36             : import org.eclipse.jgit.lib.Ref;
      37             : import org.eclipse.jgit.lib.Repository;
      38             : import org.eclipse.jgit.revwalk.ReachabilityChecker;
      39             : import org.eclipse.jgit.revwalk.RevCommit;
      40             : import org.eclipse.jgit.revwalk.RevWalk;
      41             : 
      42             : /**
      43             :  * Report whether a commit is reachable from a set of commits. This is used for checking if a user
      44             :  * has read permissions on a commit.
      45             :  */
      46             : @Singleton
      47             : public class Reachable {
      48         149 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      49             : 
      50             :   private final PermissionBackend permissionBackend;
      51             : 
      52             :   @Inject
      53         149 :   Reachable(PermissionBackend permissionBackend) {
      54         149 :     this.permissionBackend = permissionBackend;
      55         149 :   }
      56             : 
      57             :   /**
      58             :    * Returns true if a commit is reachable from a given set of refs. This method enforces
      59             :    * permissions on the given set of refs and performs a reachability check. Tags are not filtered
      60             :    * separately and will only be returned if reachable by a provided ref.
      61             :    */
      62             :   public boolean fromRefs(
      63             :       Project.NameKey project, Repository repo, RevCommit commit, List<Ref> refs) {
      64           7 :     return fromRefs(project, repo, commit, refs, Optional.empty());
      65             :   }
      66             : 
      67             :   boolean fromRefs(
      68             :       Project.NameKey project,
      69             :       Repository repo,
      70             :       RevCommit commit,
      71             :       List<Ref> refs,
      72             :       Optional<CurrentUser> optionalUserProvider) {
      73          24 :     try (RevWalk rw = new RevWalk(repo)) {
      74          24 :       Collection<Ref> filtered =
      75             :           optionalUserProvider
      76          24 :               .map(permissionBackend::user)
      77          24 :               .orElse(permissionBackend.currentUser())
      78          24 :               .project(project)
      79          24 :               .filter(refs, repo, RefFilterOptions.defaults());
      80          24 :       Collection<RevCommit> visible = new ArrayList<>();
      81          24 :       for (Ref r : filtered) {
      82             :         try {
      83          24 :           visible.add(rw.parseCommit(r.getObjectId()));
      84           1 :         } catch (IncorrectObjectTypeException notCommit) {
      85             :           // Its OK for a tag reference to point to a blob or a tree, this
      86             :           // is common in the Linux kernel or git.git repository.
      87           1 :           continue;
      88           0 :         } catch (MissingObjectException notHere) {
      89             :           // Log the problem with this branch, but keep processing.
      90           0 :           logger.atWarning().log(
      91             :               "Reference %s in %s points to dangling object %s",
      92           0 :               r.getName(), repo.getDirectory(), r.getObjectId());
      93           0 :           continue;
      94          24 :         }
      95          24 :       }
      96             : 
      97             :       // The filtering above already produces a voluminous trace. To separate the permission check
      98             :       // from the reachability check, do the trace here:
      99          24 :       try (TraceTimer timer =
     100          24 :           TraceContext.newTimer(
     101             :               "ReachabilityChecker.areAllReachable",
     102          24 :               Metadata.builder().projectName(project.get()).resourceCount(refs.size()).build())) {
     103          24 :         ReachabilityChecker checker = rw.getObjectReader().createReachabilityChecker(rw);
     104          24 :         Optional<RevCommit> unreachable =
     105          24 :             checker.areAllReachable(ImmutableList.of(rw.parseCommit(commit)), visible.stream());
     106          24 :         return !unreachable.isPresent();
     107             :       }
     108           0 :     } catch (IOException | PermissionBackendException e) {
     109           0 :       logger.atSevere().withCause(e).log(
     110           0 :           "Cannot verify permissions to commit object %s in repository %s", commit.name(), project);
     111           0 :       return false;
     112             :     }
     113             :   }
     114             : }

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