aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Wolf <thomas.wolf@paranor.ch>2021-01-24 01:57:09 +0100
committerMatthias Sohn <matthias.sohn@sap.com>2021-02-07 17:32:30 -0500
commit6d462e5fe95e20245656aaac4ead6f4c48e319d5 (patch)
treea02b202c4799185e8db49a1668865ecf193de17b
parent19bed3399de12654481f8072cab60cb4b1796d8a (diff)
downloadjgit-6d462e5fe95e20245656aaac4ead6f4c48e319d5.tar.gz
jgit-6d462e5fe95e20245656aaac4ead6f4c48e319d5.zip
GPG: support git config gpg.program
Add it to the GpgConfig. Change GpgConfig to load the values once only. Add a parameter to the GpgObjectSigner interface's operations to pass in a GpgConfig. Update CommitCommand and TagCommand to pass the value to the signer. Let the signer decide whether it can actually produce the wanted signature type (openpgp or x509). No behavior change. But this makes it possible to implement different signers that might support x509 signatures, or use gpg.program and shell out to an external GPG executable for signing. Change-Id: I427f83eb1ece81c310e1cddd85315f6f88cc99ea Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java36
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java55
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java56
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java85
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgObjectSigner.java38
6 files changed, 241 insertions, 37 deletions
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java
index 449c4a487b..f448d5e9e8 100644
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java
@@ -34,13 +34,17 @@ import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.errors.CanceledException;
import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.api.errors.UnsupportedSigningFormatException;
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.GpgConfig;
import org.eclipse.jgit.lib.GpgSignature;
import org.eclipse.jgit.lib.GpgSigner;
import org.eclipse.jgit.lib.GpgObjectSigner;
import org.eclipse.jgit.lib.ObjectBuilder;
import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.util.StringUtils;
@@ -70,6 +74,24 @@ public class BouncyCastleGpgSigner extends GpgSigner
public boolean canLocateSigningKey(@Nullable String gpgSigningKey,
PersonIdent committer, CredentialsProvider credentialsProvider)
throws CanceledException {
+ try {
+ return canLocateSigningKey(gpgSigningKey, committer,
+ credentialsProvider, null);
+ } catch (UnsupportedSigningFormatException e) {
+ // Cannot occur with a null config
+ return false;
+ }
+ }
+
+ @Override
+ public boolean canLocateSigningKey(@Nullable String gpgSigningKey,
+ PersonIdent committer, CredentialsProvider credentialsProvider,
+ GpgConfig config)
+ throws CanceledException, UnsupportedSigningFormatException {
+ if (config != null && config.getKeyFormat() != GpgFormat.OPENPGP) {
+ throw new UnsupportedSigningFormatException(
+ JGitText.get().onlyOpenPgpSupportedForSigning);
+ }
try (BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt = new BouncyCastleGpgKeyPassphrasePrompt(
credentialsProvider)) {
BouncyCastleGpgKey gpgKey = locateSigningKey(gpgSigningKey,
@@ -101,13 +123,23 @@ public class BouncyCastleGpgSigner extends GpgSigner
public void sign(@NonNull CommitBuilder commit,
@Nullable String gpgSigningKey, @NonNull PersonIdent committer,
CredentialsProvider credentialsProvider) throws CanceledException {
- signObject(commit, gpgSigningKey, committer, credentialsProvider);
+ try {
+ signObject(commit, gpgSigningKey, committer, credentialsProvider,
+ null);
+ } catch (UnsupportedSigningFormatException e) {
+ // Cannot occur with a null config
+ }
}
@Override
public void signObject(@NonNull ObjectBuilder object,
@Nullable String gpgSigningKey, @NonNull PersonIdent committer,
- CredentialsProvider credentialsProvider) throws CanceledException {
+ CredentialsProvider credentialsProvider, GpgConfig config)
+ throws CanceledException, UnsupportedSigningFormatException {
+ if (config != null && config.getKeyFormat() != GpgFormat.OPENPGP) {
+ throw new UnsupportedSigningFormatException(
+ JGitText.get().onlyOpenPgpSupportedForSigning);
+ }
try (BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt = new BouncyCastleGpgKeyPassphrasePrompt(
credentialsProvider)) {
BouncyCastleGpgKey gpgKey = locateSigningKey(gpgSigningKey,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
index b4f7175036..31f6a31c75 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -47,6 +47,7 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.GpgConfig;
import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
+import org.eclipse.jgit.lib.GpgObjectSigner;
import org.eclipse.jgit.lib.GpgSigner;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
@@ -120,6 +121,8 @@ public class CommitCommand extends GitCommand<RevCommit> {
private GpgSigner gpgSigner;
+ private GpgConfig gpgConfig;
+
private CredentialsProvider credentialsProvider;
/**
@@ -247,8 +250,18 @@ public class CommitCommand extends GitCommand<RevCommit> {
throw new ServiceUnavailableException(
JGitText.get().signingServiceUnavailable);
}
- gpgSigner.sign(commit, signingKey, committer,
- credentialsProvider);
+ if (gpgSigner instanceof GpgObjectSigner) {
+ ((GpgObjectSigner) gpgSigner).signObject(commit,
+ signingKey, committer, credentialsProvider,
+ gpgConfig);
+ } else {
+ if (gpgConfig.getKeyFormat() != GpgFormat.OPENPGP) {
+ throw new UnsupportedSigningFormatException(JGitText
+ .get().onlyOpenPgpSupportedForSigning);
+ }
+ gpgSigner.sign(commit, signingKey, committer,
+ credentialsProvider);
+ }
}
ObjectId commitId = odi.insert(commit);
@@ -576,7 +589,9 @@ public class CommitCommand extends GitCommand<RevCommit> {
// an explicit message
throw new NoMessageException(JGitText.get().commitMessageNotSpecified);
- GpgConfig gpgConfig = new GpgConfig(repo.getConfig());
+ if (gpgConfig == null) {
+ gpgConfig = new GpgConfig(repo.getConfig());
+ }
if (signCommit == null) {
signCommit = gpgConfig.isSignCommits() ? Boolean.TRUE
: Boolean.FALSE;
@@ -585,10 +600,6 @@ public class CommitCommand extends GitCommand<RevCommit> {
signingKey = gpgConfig.getSigningKey();
}
if (gpgSigner == null) {
- if (gpgConfig.getKeyFormat() != GpgFormat.OPENPGP) {
- throw new UnsupportedSigningFormatException(
- JGitText.get().onlyOpenPgpSupportedForSigning);
- }
gpgSigner = GpgSigner.getDefault();
}
}
@@ -973,6 +984,36 @@ public class CommitCommand extends GitCommand<RevCommit> {
}
/**
+ * Sets the {@link GpgSigner} to use if the commit is to be signed.
+ *
+ * @param signer
+ * to use; if {@code null}, the default signer will be used
+ * @return {@code this}
+ * @since 5.11
+ */
+ public CommitCommand setGpgSigner(GpgSigner signer) {
+ checkCallable();
+ this.gpgSigner = signer;
+ return this;
+ }
+
+ /**
+ * Sets an external {@link GpgConfig} to use. Whether it will be used is at
+ * the discretion of the {@link #setGpgSigner(GpgSigner)}.
+ *
+ * @param config
+ * to set; if {@code null}, the config will be loaded from the
+ * git config of the repository
+ * @return {@code this}
+ * @since 5.11
+ */
+ public CommitCommand setGpgConfig(GpgConfig config) {
+ checkCallable();
+ this.gpgConfig = config;
+ return this;
+ }
+
+ /**
* Sets a {@link CredentialsProvider}
*
* @param credentialsProvider
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
index 75f942d2e0..58c18b38d1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
@@ -23,6 +23,7 @@ import org.eclipse.jgit.api.errors.UnsupportedSigningFormatException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
import org.eclipse.jgit.lib.GpgObjectSigner;
import org.eclipse.jgit.lib.GpgSigner;
import org.eclipse.jgit.lib.ObjectId;
@@ -34,7 +35,6 @@ import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.lib.TagBuilder;
-import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.CredentialsProvider;
@@ -80,6 +80,8 @@ public class TagCommand extends GitCommand<Ref> {
private String signingKey;
+ private GpgConfig gpgConfig;
+
private GpgObjectSigner gpgSigner;
private CredentialsProvider credentialsProvider;
@@ -138,7 +140,7 @@ public class TagCommand extends GitCommand<Ref> {
if (gpgSigner != null) {
gpgSigner.signObject(newTag, signingKey, tagger,
- credentialsProvider);
+ credentialsProvider, gpgConfig);
}
// write the tag object
@@ -228,7 +230,9 @@ public class TagCommand extends GitCommand<Ref> {
}
// Figure out whether to sign.
if (!(Boolean.FALSE.equals(signed) && signingKey == null)) {
- GpgConfig gpgConfig = new GpgConfig(repo.getConfig());
+ if (gpgConfig == null) {
+ gpgConfig = new GpgConfig(repo.getConfig());
+ }
boolean doSign = isSigned() || gpgConfig.isSignAllTags();
if (!Boolean.TRUE.equals(annotated) && !doSign) {
doSign = gpgConfig.isSignAnnotated();
@@ -237,16 +241,14 @@ public class TagCommand extends GitCommand<Ref> {
if (signingKey == null) {
signingKey = gpgConfig.getSigningKey();
}
- if (gpgConfig.getKeyFormat() != GpgFormat.OPENPGP) {
- throw new UnsupportedSigningFormatException(
- JGitText.get().onlyOpenPgpSupportedForSigning);
- }
- GpgSigner signer = GpgSigner.getDefault();
- if (!(signer instanceof GpgObjectSigner)) {
- throw new ServiceUnavailableException(
- JGitText.get().signingServiceUnavailable);
+ if (gpgSigner == null) {
+ GpgSigner signer = GpgSigner.getDefault();
+ if (!(signer instanceof GpgObjectSigner)) {
+ throw new ServiceUnavailableException(
+ JGitText.get().signingServiceUnavailable);
+ }
+ gpgSigner = (GpgObjectSigner) signer;
}
- gpgSigner = (GpgObjectSigner) signer;
// The message of a signed tag must end in a newline because
// the signature will be appended.
if (message != null && !message.isEmpty()
@@ -332,6 +334,36 @@ public class TagCommand extends GitCommand<Ref> {
}
/**
+ * Sets the {@link GpgSigner} to use if the commit is to be signed.
+ *
+ * @param signer
+ * to use; if {@code null}, the default signer will be used
+ * @return {@code this}
+ * @since 5.11
+ */
+ public TagCommand setGpgSigner(GpgObjectSigner signer) {
+ checkCallable();
+ this.gpgSigner = signer;
+ return this;
+ }
+
+ /**
+ * Sets an external {@link GpgConfig} to use. Whether it will be used is at
+ * the discretion of the {@link #setGpgSigner(GpgObjectSigner)}.
+ *
+ * @param config
+ * to set; if {@code null}, the config will be loaded from the
+ * git config of the repository
+ * @return {@code this}
+ * @since 5.11
+ */
+ public TagCommand setGpgConfig(GpgConfig config) {
+ checkCallable();
+ this.gpgConfig = config;
+ return this;
+ }
+
+ /**
* Sets the tagger of the tag. If the tagger is null, a PersonIdent will be
* created from the info in the repository.
*
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
index 954a75cb2c..7381c905b0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -105,7 +105,15 @@ public final class ConfigConstants {
public static final String CONFIG_KEY_FORMAT = "format";
/**
+ * The "program" key
+ *
+ * @since 5.11
+ */
+ public static final String CONFIG_KEY_PROGRAM = "program";
+
+ /**
* The "signingKey" key
+ *
* @since 5.2
*/
public static final String CONFIG_KEY_SIGNINGKEY = "signingKey";
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java
index 5b43729739..427a235f3b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, Salesforce. and others
+ * Copyright (C) 2018, 2021 Salesforce and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -43,16 +43,65 @@ public class GpgConfig {
}
}
- private final Config config;
+ private final GpgFormat keyFormat;
+
+ private final String signingKey;
+
+ private final String program;
+
+ private final boolean signCommits;
+
+ private final boolean signAllTags;
+
+ private final boolean forceAnnotated;
/**
- * Create a new GPG config, which will read configuration from config.
+ * Create a {@link GpgConfig} with the given parameters and default
+ * {@code true} for signing commits and {@code false} for tags.
+ *
+ * @param keySpec
+ * to use
+ * @param format
+ * to use
+ * @param gpgProgram
+ * to use
+ * @since 5.11
+ */
+ public GpgConfig(String keySpec, GpgFormat format, String gpgProgram) {
+ keyFormat = format;
+ signingKey = keySpec;
+ program = gpgProgram;
+ signCommits = true;
+ signAllTags = false;
+ forceAnnotated = false;
+ }
+
+ /**
+ * Create a new GPG config that reads the configuration from config.
*
* @param config
* the config to read from
*/
public GpgConfig(Config config) {
- this.config = config;
+ keyFormat = config.getEnum(GpgFormat.values(),
+ ConfigConstants.CONFIG_GPG_SECTION, null,
+ ConfigConstants.CONFIG_KEY_FORMAT, GpgFormat.OPENPGP);
+ signingKey = config.getString(ConfigConstants.CONFIG_USER_SECTION, null,
+ ConfigConstants.CONFIG_KEY_SIGNINGKEY);
+
+ String exe = config.getString(ConfigConstants.CONFIG_GPG_SECTION,
+ keyFormat.toConfigValue(), ConfigConstants.CONFIG_KEY_PROGRAM);
+ if (exe == null) {
+ exe = config.getString(ConfigConstants.CONFIG_GPG_SECTION, null,
+ ConfigConstants.CONFIG_KEY_PROGRAM);
+ }
+ program = exe;
+ signCommits = config.getBoolean(ConfigConstants.CONFIG_COMMIT_SECTION,
+ ConfigConstants.CONFIG_KEY_GPGSIGN, false);
+ signAllTags = config.getBoolean(ConfigConstants.CONFIG_TAG_SECTION,
+ ConfigConstants.CONFIG_KEY_GPGSIGN, false);
+ forceAnnotated = config.getBoolean(ConfigConstants.CONFIG_TAG_SECTION,
+ ConfigConstants.CONFIG_KEY_FORCE_SIGN_ANNOTATED, false);
}
/**
@@ -61,9 +110,19 @@ public class GpgConfig {
* @return the {@link org.eclipse.jgit.lib.GpgConfig.GpgFormat}
*/
public GpgFormat getKeyFormat() {
- return config.getEnum(GpgFormat.values(),
- ConfigConstants.CONFIG_GPG_SECTION, null,
- ConfigConstants.CONFIG_KEY_FORMAT, GpgFormat.OPENPGP);
+ return keyFormat;
+ }
+
+ /**
+ * Retrieves the value of the configured GPG program to use, as defined by
+ * gpg.openpgp.program, gpg.x509.program (depending on the defined
+ * {@link #getKeyFormat() format}), or gpg.program.
+ *
+ * @return the program string configured, or {@code null} if none
+ * @since 5.11
+ */
+ public String getProgram() {
+ return program;
}
/**
@@ -72,8 +131,7 @@ public class GpgConfig {
* @return the value of user.signingKey (may be <code>null</code>)
*/
public String getSigningKey() {
- return config.getString(ConfigConstants.CONFIG_USER_SECTION, null,
- ConfigConstants.CONFIG_KEY_SIGNINGKEY);
+ return signingKey;
}
/**
@@ -82,8 +140,7 @@ public class GpgConfig {
* @return the value of commit.gpgSign (defaults to <code>false</code>)
*/
public boolean isSignCommits() {
- return config.getBoolean(ConfigConstants.CONFIG_COMMIT_SECTION,
- ConfigConstants.CONFIG_KEY_GPGSIGN, false);
+ return signCommits;
}
/**
@@ -94,8 +151,7 @@ public class GpgConfig {
* @since 5.11
*/
public boolean isSignAllTags() {
- return config.getBoolean(ConfigConstants.CONFIG_TAG_SECTION,
- ConfigConstants.CONFIG_KEY_GPGSIGN, false);
+ return signAllTags;
}
/**
@@ -107,7 +163,6 @@ public class GpgConfig {
* @since 5.11
*/
public boolean isSignAnnotated() {
- return config.getBoolean(ConfigConstants.CONFIG_TAG_SECTION,
- ConfigConstants.CONFIG_KEY_FORCE_SIGN_ANNOTATED, false);
+ return forceAnnotated;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgObjectSigner.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgObjectSigner.java
index 6fb767774c..074f46567b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgObjectSigner.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgObjectSigner.java
@@ -12,6 +12,7 @@ package org.eclipse.jgit.lib;
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.errors.CanceledException;
+import org.eclipse.jgit.api.errors.UnsupportedSigningFormatException;
import org.eclipse.jgit.transport.CredentialsProvider;
/**
@@ -48,12 +49,47 @@ public interface GpgObjectSigner {
* @param credentialsProvider
* provider to use when querying for signing key credentials (eg.
* passphrase)
+ * @param config
+ * GPG settings from the git config
* @throws CanceledException
* when signing was canceled (eg., user aborted when entering
* passphrase)
+ * @throws UnsupportedSigningFormatException
+ * if a config is given and the wanted key format is not
+ * supported
*/
void signObject(@NonNull ObjectBuilder object,
@Nullable String gpgSigningKey, @NonNull PersonIdent committer,
- CredentialsProvider credentialsProvider) throws CanceledException;
+ CredentialsProvider credentialsProvider, GpgConfig config)
+ throws CanceledException, UnsupportedSigningFormatException;
+
+ /**
+ * Indicates if a signing key is available for the specified committer
+ * and/or signing key.
+ *
+ * @param gpgSigningKey
+ * the signing key to locate (passed as is to the GPG signing
+ * tool as is; eg., value of <code>user.signingkey</code>)
+ * @param committer
+ * the signing identity (to help with key lookup in case signing
+ * key is not specified)
+ * @param credentialsProvider
+ * provider to use when querying for signing key credentials (eg.
+ * passphrase)
+ * @param config
+ * GPG settings from the git config
+ * @return <code>true</code> if a signing key is available,
+ * <code>false</code> otherwise
+ * @throws CanceledException
+ * when signing was canceled (eg., user aborted when entering
+ * passphrase)
+ * @throws UnsupportedSigningFormatException
+ * if a config is given and the wanted key format is not
+ * supported
+ */
+ public abstract boolean canLocateSigningKey(@Nullable String gpgSigningKey,
+ @NonNull PersonIdent committer,
+ CredentialsProvider credentialsProvider, GpgConfig config)
+ throws CanceledException, UnsupportedSigningFormatException;
}