LCOV - code coverage report
Current view: top level - server/git/receive - BranchCommitValidator.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 38 41 92.7 %
Date: 2022-11-19 15:00:39 Functions: 7 7 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.git.receive;
      16             : 
      17             : import static com.google.gerrit.git.ObjectIds.abbreviateName;
      18             : import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
      19             : 
      20             : import com.google.auto.value.AutoValue;
      21             : import com.google.common.collect.ImmutableList;
      22             : import com.google.common.collect.ImmutableListMultimap;
      23             : import com.google.common.flogger.FluentLogger;
      24             : import com.google.gerrit.common.Nullable;
      25             : import com.google.gerrit.entities.BranchNameKey;
      26             : import com.google.gerrit.entities.Change;
      27             : import com.google.gerrit.entities.Project;
      28             : import com.google.gerrit.server.IdentifiedUser;
      29             : import com.google.gerrit.server.events.CommitReceivedEvent;
      30             : import com.google.gerrit.server.git.validators.CommitValidationException;
      31             : import com.google.gerrit.server.git.validators.CommitValidationMessage;
      32             : import com.google.gerrit.server.git.validators.CommitValidators;
      33             : import com.google.gerrit.server.logging.TraceContext;
      34             : import com.google.gerrit.server.logging.TraceContext.TraceTimer;
      35             : import com.google.gerrit.server.permissions.PermissionBackend;
      36             : import com.google.gerrit.server.project.ProjectState;
      37             : import com.google.gerrit.server.ssh.SshInfo;
      38             : import com.google.inject.Inject;
      39             : import com.google.inject.assistedinject.Assisted;
      40             : import java.io.IOException;
      41             : import org.eclipse.jgit.lib.Config;
      42             : import org.eclipse.jgit.lib.ObjectReader;
      43             : import org.eclipse.jgit.lib.Repository;
      44             : import org.eclipse.jgit.notes.NoteMap;
      45             : import org.eclipse.jgit.revwalk.RevCommit;
      46             : import org.eclipse.jgit.transport.ReceiveCommand;
      47             : 
      48             : /** Validates single commits for a branch. */
      49             : public class BranchCommitValidator {
      50          96 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      51             : 
      52             :   private final CommitValidators.Factory commitValidatorsFactory;
      53             :   private final IdentifiedUser user;
      54             :   private final PermissionBackend.ForProject permissions;
      55             :   private final Project project;
      56             :   private final BranchNameKey branch;
      57             :   private final SshInfo sshInfo;
      58             : 
      59             :   interface Factory {
      60             :     BranchCommitValidator create(
      61             :         ProjectState projectState, BranchNameKey branch, IdentifiedUser user);
      62             :   }
      63             : 
      64             :   /** A boolean validation status and a list of additional messages. */
      65             :   @AutoValue
      66          96 :   abstract static class Result {
      67             :     static Result create(boolean isValid, ImmutableList<CommitValidationMessage> messages) {
      68          96 :       return new AutoValue_BranchCommitValidator_Result(isValid, messages);
      69             :     }
      70             : 
      71             :     /** Whether the commit is valid. */
      72             :     abstract boolean isValid();
      73             : 
      74             :     /**
      75             :      * A list of messages related to the validation. Messages may be present regardless of the
      76             :      * {@link #isValid()} status.
      77             :      */
      78             :     abstract ImmutableList<CommitValidationMessage> messages();
      79             :   }
      80             : 
      81             :   @Inject
      82             :   BranchCommitValidator(
      83             :       CommitValidators.Factory commitValidatorsFactory,
      84             :       PermissionBackend permissionBackend,
      85             :       SshInfo sshInfo,
      86             :       @Assisted ProjectState projectState,
      87             :       @Assisted BranchNameKey branch,
      88          96 :       @Assisted IdentifiedUser user) {
      89          96 :     this.sshInfo = sshInfo;
      90          96 :     this.user = user;
      91          96 :     this.branch = branch;
      92          96 :     this.commitValidatorsFactory = commitValidatorsFactory;
      93          96 :     project = projectState.getProject();
      94          96 :     permissions = permissionBackend.user(user).project(project.getNameKey());
      95          96 :   }
      96             : 
      97             :   /**
      98             :    * Validates a single commit. If the commit does not validate, the command is rejected.
      99             :    *
     100             :    * @param repository the repository
     101             :    * @param objectReader the object reader to use.
     102             :    * @param cmd the ReceiveCommand executing the push.
     103             :    * @param commit the commit being validated.
     104             :    * @param isMerged whether this is a merge commit created by magicBranch --merge option
     105             :    * @param change the change for which this is a new patchset.
     106             :    * @return The validation {@link Result}.
     107             :    */
     108             :   Result validateCommit(
     109             :       Repository repository,
     110             :       ObjectReader objectReader,
     111             :       ReceiveCommand cmd,
     112             :       RevCommit commit,
     113             :       ImmutableListMultimap<String, String> pushOptions,
     114             :       boolean isMerged,
     115             :       NoteMap rejectCommits,
     116             :       @Nullable Change change)
     117             :       throws IOException {
     118          89 :     return validateCommit(
     119             :         repository, objectReader, cmd, commit, pushOptions, isMerged, rejectCommits, change, false);
     120             :   }
     121             : 
     122             :   /**
     123             :    * Validates a single commit. If the commit does not validate, the command is rejected.
     124             :    *
     125             :    * @param repository the repository
     126             :    * @param objectReader the object reader to use.
     127             :    * @param cmd the ReceiveCommand executing the push.
     128             :    * @param commit the commit being validated.
     129             :    * @param isMerged whether this is a merge commit created by magicBranch --merge option
     130             :    * @param change the change for which this is a new patchset.
     131             :    * @param skipValidation whether 'skip-validation' was requested.
     132             :    * @return The validation {@link Result}.
     133             :    */
     134             :   Result validateCommit(
     135             :       Repository repository,
     136             :       ObjectReader objectReader,
     137             :       ReceiveCommand cmd,
     138             :       RevCommit commit,
     139             :       ImmutableListMultimap<String, String> pushOptions,
     140             :       boolean isMerged,
     141             :       NoteMap rejectCommits,
     142             :       @Nullable Change change,
     143             :       boolean skipValidation)
     144             :       throws IOException {
     145          96 :     try (TraceTimer traceTimer = TraceContext.newTimer("BranchCommitValidator#validateCommit")) {
     146          96 :       ImmutableList.Builder<CommitValidationMessage> messages = new ImmutableList.Builder<>();
     147          96 :       try (CommitReceivedEvent receiveEvent =
     148             :           new CommitReceivedEvent(
     149             :               cmd,
     150             :               project,
     151          96 :               branch.branch(),
     152             :               pushOptions,
     153          96 :               new Config(repository.getConfig()),
     154             :               objectReader,
     155             :               commit,
     156             :               user)) {
     157             :         CommitValidators validators;
     158          96 :         if (isMerged) {
     159           3 :           validators =
     160           3 :               commitValidatorsFactory.forMergedCommits(
     161           3 :                   permissions, branch, user.asIdentifiedUser());
     162             :         } else {
     163          96 :           validators =
     164          96 :               commitValidatorsFactory.forReceiveCommits(
     165             :                   permissions,
     166             :                   branch,
     167          96 :                   user.asIdentifiedUser(),
     168             :                   sshInfo,
     169             :                   rejectCommits,
     170             :                   receiveEvent.revWalk,
     171             :                   change,
     172             :                   skipValidation);
     173             :         }
     174             : 
     175          95 :         for (CommitValidationMessage m : validators.validate(receiveEvent)) {
     176           0 :           messages.add(
     177             :               new CommitValidationMessage(
     178           0 :                   messageForCommit(commit, m.getMessage(), objectReader), m.getType()));
     179           0 :         }
     180          13 :       } catch (CommitValidationException e) {
     181          13 :         logger.atFine().log("Commit validation failed on %s", commit.name());
     182          13 :         for (CommitValidationMessage m : e.getMessages()) {
     183             :           // The non-error messages may contain background explanation for the
     184             :           // fatal error, so have to preserve all messages.
     185           9 :           messages.add(
     186             :               new CommitValidationMessage(
     187           9 :                   messageForCommit(commit, m.getMessage(), objectReader), m.getType()));
     188           9 :         }
     189          13 :         cmd.setResult(
     190          13 :             REJECTED_OTHER_REASON, messageForCommit(commit, e.getMessage(), objectReader));
     191          13 :         return Result.create(false, messages.build());
     192          95 :       }
     193          95 :       return Result.create(true, messages.build());
     194          13 :     }
     195             :   }
     196             : 
     197             :   private String messageForCommit(RevCommit c, String msg, ObjectReader objectReader)
     198             :       throws IOException {
     199          13 :     return String.format("commit %s: %s", abbreviateName(c, objectReader), msg);
     200             :   }
     201             : }

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