LCOV - code coverage report
Current view: top level - gpg - SignedPushPreReceiveHook.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 17 33 51.5 %
Date: 2022-11-19 15:00:39 Functions: 5 7 71.4 %

          Line data    Source code
       1             : // Copyright (C) 2015 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.gpg;
      16             : 
      17             : import com.google.gerrit.server.IdentifiedUser;
      18             : import com.google.gerrit.server.util.MagicBranch;
      19             : import com.google.inject.Inject;
      20             : import com.google.inject.Provider;
      21             : import com.google.inject.Singleton;
      22             : import java.util.Collection;
      23             : import org.eclipse.jgit.transport.PreReceiveHook;
      24             : import org.eclipse.jgit.transport.PushCertificate;
      25             : import org.eclipse.jgit.transport.ReceiveCommand;
      26             : import org.eclipse.jgit.transport.ReceivePack;
      27             : 
      28             : /**
      29             :  * Pre-receive hook to check signed pushes.
      30             :  *
      31             :  * <p>If configured, prior to processing any push using {@code ReceiveCommits}, requires that any
      32             :  * push certificate present must be valid.
      33             :  */
      34             : @Singleton
      35             : public class SignedPushPreReceiveHook implements PreReceiveHook {
      36             :   public static class Required implements PreReceiveHook {
      37           3 :     public static final Required INSTANCE = new Required();
      38             : 
      39             :     @Override
      40             :     public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
      41           3 :       if (rp.getPushCertificate() == null) {
      42           3 :         rp.sendMessage("ERROR: Signed push is required");
      43           3 :         reject(commands, "push cert error");
      44             :       }
      45           3 :     }
      46             : 
      47             :     private Required() {}
      48             :   }
      49             : 
      50             :   private final Provider<IdentifiedUser> user;
      51             :   private final GerritPushCertificateCheckerFactory checkerFactory;
      52             : 
      53             :   @Inject
      54             :   public SignedPushPreReceiveHook(
      55           7 :       Provider<IdentifiedUser> user, GerritPushCertificateCheckerFactory checkerFactory) {
      56           7 :     this.user = user;
      57           7 :     this.checkerFactory = checkerFactory;
      58           7 :   }
      59             : 
      60             :   @Override
      61             :   public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
      62           3 :     PushCertificate cert = rp.getPushCertificate();
      63           3 :     if (cert == null) {
      64           3 :       return;
      65             :     }
      66           0 :     CheckResult result =
      67           0 :         checkerFactory.create(user.get()).setCheckNonce(true).check(cert).getCheckResult();
      68           0 :     if (!isAllowed(result, commands)) {
      69           0 :       for (String problem : result.getProblems()) {
      70           0 :         rp.sendMessage(problem);
      71           0 :       }
      72           0 :       reject(commands, "invalid push cert");
      73             :     }
      74           0 :   }
      75             : 
      76             :   private static boolean isAllowed(CheckResult result, Collection<ReceiveCommand> commands) {
      77           0 :     if (onlyMagicBranches(commands)) {
      78             :       // Only pushing magic branches: allow a valid push certificate even if the
      79             :       // key is not ultimately trusted. Assume anyone with Submit permission to
      80             :       // the branch is able to verify during review that the code is legitimate.
      81           0 :       return result.isOk();
      82             :     }
      83             :     // Directly updating one or more refs: require a trusted key.
      84           0 :     return result.isTrusted();
      85             :   }
      86             : 
      87             :   private static boolean onlyMagicBranches(Iterable<ReceiveCommand> commands) {
      88           0 :     for (ReceiveCommand c : commands) {
      89           0 :       if (!MagicBranch.isMagicBranch(c.getRefName())) {
      90           0 :         return false;
      91             :       }
      92           0 :     }
      93           0 :     return true;
      94             :   }
      95             : 
      96             :   private static void reject(Collection<ReceiveCommand> commands, String reason) {
      97           3 :     for (ReceiveCommand cmd : commands) {
      98           3 :       if (cmd.getResult() == ReceiveCommand.Result.NOT_ATTEMPTED) {
      99           3 :         cmd.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, reason);
     100             :       }
     101           3 :     }
     102           3 :   }
     103             : }

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