LCOV - code coverage report
Current view: top level - gpg - SignedPushModule.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 48 58 82.8 %
Date: 2022-11-19 15:00:39 Functions: 10 10 100.0 %

          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 static com.google.gerrit.server.project.ProjectCache.illegalState;
      18             : 
      19             : import com.google.common.base.Strings;
      20             : import com.google.common.flogger.FluentLogger;
      21             : import com.google.gerrit.entities.BooleanProjectConfig;
      22             : import com.google.gerrit.entities.Project;
      23             : import com.google.gerrit.extensions.registration.DynamicSet;
      24             : import com.google.gerrit.server.EnableSignedPush;
      25             : import com.google.gerrit.server.config.AllUsersName;
      26             : import com.google.gerrit.server.config.GerritServerConfig;
      27             : import com.google.gerrit.server.git.GitRepositoryManager;
      28             : import com.google.gerrit.server.git.ReceivePackInitializer;
      29             : import com.google.gerrit.server.project.ProjectCache;
      30             : import com.google.gerrit.server.project.ProjectState;
      31             : import com.google.inject.AbstractModule;
      32             : import com.google.inject.Inject;
      33             : import com.google.inject.Provider;
      34             : import com.google.inject.ProvisionException;
      35             : import com.google.inject.Singleton;
      36             : import java.io.IOException;
      37             : import java.security.NoSuchAlgorithmException;
      38             : import java.security.SecureRandom;
      39             : import java.util.ArrayList;
      40             : import java.util.List;
      41             : import java.util.Random;
      42             : import org.eclipse.jgit.lib.Config;
      43             : import org.eclipse.jgit.lib.Repository;
      44             : import org.eclipse.jgit.transport.PreReceiveHook;
      45             : import org.eclipse.jgit.transport.PreReceiveHookChain;
      46             : import org.eclipse.jgit.transport.ReceivePack;
      47             : import org.eclipse.jgit.transport.SignedPushConfig;
      48             : 
      49           7 : class SignedPushModule extends AbstractModule {
      50           7 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      51             : 
      52             :   @Override
      53             :   protected void configure() {
      54           7 :     if (!BouncyCastleUtil.havePGP()) {
      55           0 :       throw new ProvisionException("Bouncy Castle PGP not installed");
      56             :     }
      57           7 :     bind(PublicKeyStore.class).toProvider(StoreProvider.class);
      58           7 :     DynamicSet.bind(binder(), ReceivePackInitializer.class).to(Initializer.class);
      59           7 :   }
      60             : 
      61             :   @Singleton
      62             :   private static class Initializer implements ReceivePackInitializer {
      63             :     private final SignedPushConfig signedPushConfig;
      64             :     private final SignedPushPreReceiveHook hook;
      65             :     private final ProjectCache projectCache;
      66             : 
      67             :     @Inject
      68             :     Initializer(
      69             :         @GerritServerConfig Config cfg,
      70             :         @EnableSignedPush boolean enableSignedPush,
      71             :         SignedPushPreReceiveHook hook,
      72           7 :         ProjectCache projectCache) {
      73           7 :       this.hook = hook;
      74           7 :       this.projectCache = projectCache;
      75             : 
      76           7 :       if (enableSignedPush) {
      77           7 :         String seed = cfg.getString("receive", null, "certNonceSeed");
      78           7 :         if (Strings.isNullOrEmpty(seed)) {
      79           7 :           seed = randomString(64);
      80             :         }
      81           7 :         signedPushConfig = new SignedPushConfig();
      82           7 :         signedPushConfig.setCertNonceSeed(seed);
      83           7 :         signedPushConfig.setCertNonceSlopLimit(
      84           7 :             cfg.getInt("receive", null, "certNonceSlop", 5 * 60));
      85           7 :       } else {
      86           0 :         signedPushConfig = null;
      87             :       }
      88           7 :     }
      89             : 
      90             :     @Override
      91             :     public void init(Project.NameKey project, ReceivePack rp) {
      92           6 :       ProjectState ps = projectCache.get(project).orElseThrow(illegalState(project));
      93           6 :       if (!ps.is(BooleanProjectConfig.ENABLE_SIGNED_PUSH)) {
      94           3 :         rp.setSignedPushConfig(null);
      95           3 :         return;
      96           3 :       } else if (signedPushConfig == null) {
      97           0 :         logger.atSevere().log(
      98             :             "receive.enableSignedPush is true for project %s but"
      99             :                 + " false in gerrit.config, so signed push verification is"
     100             :                 + " disabled",
     101           0 :             project.get());
     102           0 :         rp.setSignedPushConfig(null);
     103           0 :         return;
     104             :       }
     105           3 :       rp.setSignedPushConfig(signedPushConfig);
     106             : 
     107           3 :       List<PreReceiveHook> hooks = new ArrayList<>(3);
     108           3 :       if (ps.is(BooleanProjectConfig.REQUIRE_SIGNED_PUSH)) {
     109           3 :         hooks.add(SignedPushPreReceiveHook.Required.INSTANCE);
     110             :       }
     111           3 :       hooks.add(hook);
     112           3 :       hooks.add(rp.getPreReceiveHook());
     113           3 :       rp.setPreReceiveHook(PreReceiveHookChain.newChain(hooks));
     114           3 :     }
     115             :   }
     116             : 
     117             :   @Singleton
     118             :   private static class StoreProvider implements Provider<PublicKeyStore> {
     119             :     private final GitRepositoryManager repoManager;
     120             :     private final AllUsersName allUsers;
     121             : 
     122             :     @Inject
     123           2 :     StoreProvider(GitRepositoryManager repoManager, AllUsersName allUsers) {
     124           2 :       this.repoManager = repoManager;
     125           2 :       this.allUsers = allUsers;
     126           2 :     }
     127             : 
     128             :     @Override
     129             :     public PublicKeyStore get() {
     130             :       final Repository repo;
     131             :       try {
     132           2 :         repo = repoManager.openRepository(allUsers);
     133           0 :       } catch (IOException e) {
     134           0 :         throw new ProvisionException("Cannot open " + allUsers, e);
     135           2 :       }
     136           2 :       return new PublicKeyStore(repo) {
     137             :         @Override
     138             :         public void close() {
     139             :           try {
     140           2 :             super.close();
     141             :           } finally {
     142           2 :             repo.close();
     143             :           }
     144           2 :         }
     145             :       };
     146             :     }
     147             :   }
     148             : 
     149             :   private static String randomString(int len) {
     150             :     Random random;
     151             :     try {
     152           7 :       random = SecureRandom.getInstance("SHA1PRNG");
     153           0 :     } catch (NoSuchAlgorithmException e) {
     154           0 :       throw new IllegalStateException(e);
     155           7 :     }
     156           7 :     StringBuilder sb = new StringBuilder(len);
     157           7 :     for (int i = 0; i < len; i++) {
     158           7 :       sb.append((char) random.nextInt());
     159             :     }
     160           7 :     return sb.toString();
     161             :   }
     162             : }

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