summaryrefslogtreecommitdiffstats
path: root/java/com/google/gerrit/gpg/SignedPushModule.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/google/gerrit/gpg/SignedPushModule.java')
-rw-r--r--java/com/google/gerrit/gpg/SignedPushModule.java160
1 files changed, 160 insertions, 0 deletions
diff --git a/java/com/google/gerrit/gpg/SignedPushModule.java b/java/com/google/gerrit/gpg/SignedPushModule.java
new file mode 100644
index 0000000000..a05186105d
--- /dev/null
+++ b/java/com/google/gerrit/gpg/SignedPushModule.java
@@ -0,0 +1,160 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.gpg;
+
+import com.google.common.base.Strings;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.EnableSignedPush;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.ReceivePackInitializer;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+import com.google.inject.Singleton;
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.PreReceiveHook;
+import org.eclipse.jgit.transport.PreReceiveHookChain;
+import org.eclipse.jgit.transport.ReceivePack;
+import org.eclipse.jgit.transport.SignedPushConfig;
+
+class SignedPushModule extends AbstractModule {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ @Override
+ protected void configure() {
+ if (!BouncyCastleUtil.havePGP()) {
+ throw new ProvisionException("Bouncy Castle PGP not installed");
+ }
+ bind(PublicKeyStore.class).toProvider(StoreProvider.class);
+ DynamicSet.bind(binder(), ReceivePackInitializer.class).to(Initializer.class);
+ }
+
+ @Singleton
+ private static class Initializer implements ReceivePackInitializer {
+ private final SignedPushConfig signedPushConfig;
+ private final SignedPushPreReceiveHook hook;
+ private final ProjectCache projectCache;
+
+ @Inject
+ Initializer(
+ @GerritServerConfig Config cfg,
+ @EnableSignedPush boolean enableSignedPush,
+ SignedPushPreReceiveHook hook,
+ ProjectCache projectCache) {
+ this.hook = hook;
+ this.projectCache = projectCache;
+
+ if (enableSignedPush) {
+ String seed = cfg.getString("receive", null, "certNonceSeed");
+ if (Strings.isNullOrEmpty(seed)) {
+ seed = randomString(64);
+ }
+ signedPushConfig = new SignedPushConfig();
+ signedPushConfig.setCertNonceSeed(seed);
+ signedPushConfig.setCertNonceSlopLimit(
+ cfg.getInt("receive", null, "certNonceSlop", 5 * 60));
+ } else {
+ signedPushConfig = null;
+ }
+ }
+
+ @Override
+ public void init(Project.NameKey project, ReceivePack rp) {
+ ProjectState ps = projectCache.get(project);
+ if (!ps.is(BooleanProjectConfig.ENABLE_SIGNED_PUSH)) {
+ rp.setSignedPushConfig(null);
+ return;
+ } else if (signedPushConfig == null) {
+ logger.atSevere().log(
+ "receive.enableSignedPush is true for project %s but"
+ + " false in gerrit.config, so signed push verification is"
+ + " disabled",
+ project.get());
+ rp.setSignedPushConfig(null);
+ return;
+ }
+ rp.setSignedPushConfig(signedPushConfig);
+
+ List<PreReceiveHook> hooks = new ArrayList<>(3);
+ if (ps.is(BooleanProjectConfig.REQUIRE_SIGNED_PUSH)) {
+ hooks.add(SignedPushPreReceiveHook.Required.INSTANCE);
+ }
+ hooks.add(hook);
+ hooks.add(rp.getPreReceiveHook());
+ rp.setPreReceiveHook(PreReceiveHookChain.newChain(hooks));
+ }
+ }
+
+ @Singleton
+ private static class StoreProvider implements Provider<PublicKeyStore> {
+ private final GitRepositoryManager repoManager;
+ private final AllUsersName allUsers;
+
+ @Inject
+ StoreProvider(GitRepositoryManager repoManager, AllUsersName allUsers) {
+ this.repoManager = repoManager;
+ this.allUsers = allUsers;
+ }
+
+ @Override
+ public PublicKeyStore get() {
+ final Repository repo;
+ try {
+ repo = repoManager.openRepository(allUsers);
+ } catch (IOException e) {
+ throw new ProvisionException("Cannot open " + allUsers, e);
+ }
+ return new PublicKeyStore(repo) {
+ @Override
+ public void close() {
+ try {
+ super.close();
+ } finally {
+ repo.close();
+ }
+ }
+ };
+ }
+ }
+
+ private static String randomString(int len) {
+ Random random;
+ try {
+ random = SecureRandom.getInstance("SHA1PRNG");
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException(e);
+ }
+ StringBuilder sb = new StringBuilder(len);
+ for (int i = 0; i < len; i++) {
+ sb.append((char) random.nextInt());
+ }
+ return sb.toString();
+ }
+}