Allow magic branch pushes with certs from untrusted keys
The traditional way to establish that a GPG key offered by a user
actually belongs to that user in real life is to do some sort of
offline identity verification (checking paper ID, etc.). If a site
requires that level of verification for _any_ push, that may be an
unreasonably high bar for someone to meet before their first
contribution to a project. (But some organizations may still want it,
so it may be worth controlling with an option in the future.)
Allow untrusted keys if such a user is only pushing code for review,
i.e. to a magic branch name like refs/for/*. We now store the push
certificate itself in any PatchSets that are created, so it can be
verified later and/or exposed in the UI.
Change-Id: I0b1e2a13a6228b2ca00a391f005d9baddf08817f
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushPreReceiveHook.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushPreReceiveHook.java
index 50f0642..1911381 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushPreReceiveHook.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushPreReceiveHook.java
@@ -17,6 +17,7 @@
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.util.MagicBranch;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -76,7 +77,7 @@
}
};
CheckResult result = checker.check(cert);
- if (!result.isOk()) {
+ if (!isAllowed(result, commands)) {
for (String problem : result.getProblems()) {
rp.sendMessage(problem);
}
@@ -84,6 +85,28 @@
}
}
+ private static boolean isAllowed(CheckResult result,
+ Collection<ReceiveCommand> commands) {
+ if (onlyMagicBranches(commands)) {
+ // Only pushing magic branches: allow a valid push certificate even if the
+ // key is not ultimately trusted. Assume anyone with Submit permission to
+ // the branch is able to verify during review that the code is legitimate.
+ return result.isOk();
+ } else {
+ // Directly updating one or more refs: require a trusted key.
+ return result.isTrusted();
+ }
+ }
+
+ private static boolean onlyMagicBranches(Iterable<ReceiveCommand> commands) {
+ for (ReceiveCommand c : commands) {
+ if (!MagicBranch.isMagicBranch(c.getRefName())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private static void reject(Collection<ReceiveCommand> commands,
String reason) {
for (ReceiveCommand cmd : commands) {