Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Wolf2021-01-24 00:09:02 +0000
committerThomas Wolf2021-02-19 10:37:21 +0000
commit2b66e9b8baa2104c320c301daea9147227ef39fe (patch)
treef5da6256be2d50b5ab9fcd6ec4aa98d5e539d5f2 /org.eclipse.egit.ui
parent53e90c5e561c4fb5d4d3e7ad7e2871c1bc227b5a (diff)
downloadegit-2b66e9b8baa2104c320c301daea9147227ef39fe.tar.gz
egit-2b66e9b8baa2104c320c301daea9147227ef39fe.tar.xz
egit-2b66e9b8baa2104c320c301daea9147227ef39fe.zip
GPG signing: use an external GPG executable
Don't rely on JGit's GPG signer based on BouncyCastle. BouncyCastle in Eclipse is crippled; it lacks the patent-encumbered AES/OCB cipher that is needed to decrypt encrypted private keys stored by GPG in the Extended Key Format. Also, the JGit BouncyCastle GpgSigner has to rely on a number of assumptions about GPG internals. Compare bug 570501. Instead use a GpgSigner implementation that uses an external GPG executable for signing. That way it is guaranteed that signing works if it works in command-line git. Delegate all passphrase handling to the external GPG. This gives automatic integration with the native keychain and with gpg-agent. Add a new EGit preference at Git->Committing where the user can specify the path to a GPG executable. EGit finds the GPG to use * via the EGit preference, if set, otherwise * via the git config gpg.program, if set, otherwise * by looking on $PATH for an executable named "gpg" (or "gpg.exe"). If the EGit preference is an invalid path or not an executable file, errors are logged and the EGit preference is ignored. Normally, it should not be necessary to set the Eclipse preference, but it helps avoid the lookup on $PATH, and it may help if for some reason the value specified in the git config should not work with EGit. Verifying signatures still uses the JGit verifier, which is also based on BouncyCastle. Verifying doesn't need AES/OCB, and needs only a public key, which JGit can find without access to GPG internals. Automated tests for this are not possible on the current build infrastructure as it would need a GPG installation and keys. The change has been tested manually on * OS X 10.14.6 with gpg 2.2.25 * CentOS 7 with gpg 2.0.22 * Win 10 with gpg 2.2.27 (gpg4win 3.1.15) Bug: 547789 JGit-Dependency: If7e34aeed6ca6636a92bf774d893d98f6d459181 Change-Id: Id95b89cfbf822422668f668a316c5a2d8ee2d847 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch> Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
Diffstat (limited to 'org.eclipse.egit.ui')
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java15
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CommitMessageComponent.java16
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CreateTagDialog.java13
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/preferences/CommittingPreferencePage.java46
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/preferences/DoublePreferencesPreferencePage.java9
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/preferences/FullWidthFileFieldEditor.java95
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties6
7 files changed, 196 insertions, 4 deletions
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java
index cf4a929901..d516f4db88 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java
@@ -3453,6 +3453,18 @@ public class UIText extends NLS {
public static String CommittingPreferencePage_formatting;
/** */
+ public static String CommittingPreferencePage_gpgExecutableInvalid;
+
+ /** */
+ public static String CommittingPreferencePage_gpgExecutableLabel;
+
+ /** */
+ public static String CommittingPreferencePage_gpgExecutableNotExecutable;
+
+ /** */
+ public static String CommittingPreferencePage_gpgExecutableTooltip;
+
+ /** */
public static String CommittingPreferencePage_includeUntrackedFiles;
/** */
@@ -3782,6 +3794,9 @@ public class UIText extends NLS {
public static String DeleteTagCommand_titleConfirm;
/** */
+ public static String FullWidthFileFieldEditor_buttonTooltipMac;
+
+ /** */
public static String IgnoreActionHandler_addToGitignore;
/** */
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CommitMessageComponent.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CommitMessageComponent.java
index 6c0731e854..3fbc30002f 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CommitMessageComponent.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CommitMessageComponent.java
@@ -20,6 +20,7 @@
*******************************************************************************/
package org.eclipse.egit.ui.internal.dialogs;
+import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
@@ -28,6 +29,7 @@ import java.util.regex.Pattern;
import org.eclipse.egit.core.RevUtils;
import org.eclipse.egit.core.internal.gerrit.GerritUtil;
+import org.eclipse.egit.core.settings.GitSettings;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.CommitMessageWithCaretPosition;
import org.eclipse.egit.ui.UIPreferences;
@@ -578,11 +580,21 @@ public class CommitMessageComponent {
}
if (signCommit) {
+ // Ensure the Eclipse preference, if set, overrides the git config
+ File gpg = GitSettings.getGpgExecutable();
GpgConfig gpgConfig;
if (repository != null) {
- gpgConfig = new GpgConfig(repository.getConfig());
+ gpgConfig = new GpgConfig(repository.getConfig()) {
+
+ @Override
+ public String getProgram() {
+ return gpg != null ? gpg.getAbsolutePath()
+ : super.getProgram();
+ }
+ };
} else {
- gpgConfig = new GpgConfig(null, GpgFormat.OPENPGP, null);
+ gpgConfig = new GpgConfig(null, GpgFormat.OPENPGP,
+ gpg != null ? gpg.getAbsolutePath() : null);
}
boolean signingKeyAvailable = SignatureUtils
.checkSigningKey(gpgConfig, committerPersonIdent);
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CreateTagDialog.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CreateTagDialog.java
index 8ad9bba305..47b4d85b55 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CreateTagDialog.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CreateTagDialog.java
@@ -12,6 +12,7 @@
*******************************************************************************/
package org.eclipse.egit.ui.internal.dialogs;
+import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
@@ -25,6 +26,7 @@ import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.egit.core.settings.GitSettings;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.JobFamilies;
import org.eclipse.egit.ui.UIUtils;
@@ -583,8 +585,17 @@ public class CreateTagDialog extends TitleAreaDialog {
GpgSigner signer = GpgSigner.getDefault();
if (signer instanceof GpgObjectSigner) {
- GpgConfig gpgConfig = new GpgConfig(repo.getConfig());
PersonIdent tagger = new PersonIdent(repo);
+ // Ensure the Eclipse preference, if set, overrides the git config
+ File gpg = GitSettings.getGpgExecutable();
+ GpgConfig gpgConfig = new GpgConfig(repo.getConfig()) {
+
+ @Override
+ public String getProgram() {
+ return gpg != null ? gpg.getAbsolutePath()
+ : super.getProgram();
+ }
+ };
if (SignatureUtils.checkSigningKey(signer, gpgConfig, tagger)) {
// We can sign at all.
signAll = gpgConfig.isSignAllTags();
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/preferences/CommittingPreferencePage.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/preferences/CommittingPreferencePage.java
index 632242779f..c16ade0309 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/preferences/CommittingPreferencePage.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/preferences/CommittingPreferencePage.java
@@ -1,7 +1,7 @@
/*******************************************************************************
* Copyright (C) 2010, 2013 Robin Stocker <robin@nibor.org> and others.
* Copyright (C) 2015 SAP SE (Christian Georgi <christian.georgi@sap.com>)
- * Copyright (C) 2016, 2017 Thomas Wolf <thomas.wolf@paranor.ch>
+ * Copyright (C) 2016, 2021 Thomas Wolf <thomas.wolf@paranor.ch>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -12,6 +12,10 @@
*******************************************************************************/
package org.eclipse.egit.ui.internal.preferences;
+import java.nio.file.Files;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Paths;
+
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.egit.core.GitCorePreferences;
import org.eclipse.egit.ui.Activator;
@@ -23,6 +27,7 @@ import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.preference.BooleanFieldEditor;
import org.eclipse.jface.preference.ComboFieldEditor;
import org.eclipse.jface.preference.FieldEditor;
+import org.eclipse.jface.preference.FileFieldEditor;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.IntegerFieldEditor;
import org.eclipse.jface.util.IPropertyChangeListener;
@@ -35,6 +40,7 @@ import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.eclipse.ui.preferences.ScopedPreferenceStore;
@@ -145,6 +151,44 @@ public class CommittingPreferencePage extends DoublePreferencesPreferencePage
generalGroup);
addField(historySize);
+ FileFieldEditor gpgExecutable = new FullWidthFileFieldEditor(
+ GitCorePreferences.core_gpgExecutable,
+ UIText.CommittingPreferencePage_gpgExecutableLabel, true,
+ generalGroup) {
+
+ @Override
+ public void setPreferenceStore(IPreferenceStore store) {
+ super.setPreferenceStore(
+ store == null ? null : getSecondaryPreferenceStore());
+ }
+
+ @Override
+ protected boolean doCheckState() {
+ Text text = getTextControl();
+ if (text != null) {
+ String value = text.getText().trim();
+ if (!value.isEmpty()) {
+ try {
+ // Super class resolves symlinks.
+ if (!Files.isExecutable(Paths.get(value))) {
+ setErrorMessage(
+ UIText.CommittingPreferencePage_gpgExecutableNotExecutable);
+ return false;
+ }
+ } catch (InvalidPathException e) {
+ setErrorMessage(
+ UIText.CommittingPreferencePage_gpgExecutableInvalid);
+ return false;
+ }
+ }
+ }
+ return super.doCheckState();
+ }
+ };
+ addField(gpgExecutable);
+ gpgExecutable.getLabelControl(generalGroup).setToolTipText(
+ UIText.CommittingPreferencePage_gpgExecutableTooltip);
+
updateMargins(generalGroup);
Group formattingGroup = new Group(main, SWT.SHADOW_ETCHED_IN);
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/preferences/DoublePreferencesPreferencePage.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/preferences/DoublePreferencesPreferencePage.java
index 799f9fafee..fcdf310d9f 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/preferences/DoublePreferencesPreferencePage.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/preferences/DoublePreferencesPreferencePage.java
@@ -107,6 +107,15 @@ public abstract class DoublePreferencesPreferencePage
}
@Override
+ public void setValid(boolean b) {
+ super.setValid(b);
+ // Super class forgets to clear the error message.
+ if (b) {
+ setErrorMessage(null);
+ }
+ }
+
+ @Override
public boolean performOk() {
boolean isOk = super.performOk();
if (isOk) {
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/preferences/FullWidthFileFieldEditor.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/preferences/FullWidthFileFieldEditor.java
new file mode 100644
index 0000000000..bfade53fcd
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/preferences/FullWidthFileFieldEditor.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (C) 2021 Thomas Wolf <thomas.wolf@paranor.ch>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.egit.ui.internal.preferences;
+
+import org.eclipse.egit.ui.internal.UIText;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.preference.FileFieldEditor;
+import org.eclipse.jgit.util.SystemReader;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * A {@link FileFieldEditor} that is wrapped inside another composite so
+ * that it can always fill a whole row and the columns for the three
+ * controls are independent of the global grid layout of the page.
+ */
+class FullWidthFileFieldEditor extends FileFieldEditor {
+
+ private Composite wrapper;
+
+ private GridData layoutData;
+
+ public FullWidthFileFieldEditor(String configName, String label,
+ boolean enforceAbsolute, Composite parent) {
+ super(configName, label, enforceAbsolute, parent);
+ }
+
+ @Override
+ protected void createControl(Composite parent) {
+ wrapper = new Composite(parent, SWT.NONE);
+ layoutData = GridDataFactory.fillDefaults().grab(true, false)
+ .create();
+ wrapper.setLayoutData(layoutData);
+ GridLayoutFactory.fillDefaults()
+ .numColumns(super.getNumberOfControls()).applyTo(wrapper);
+ doFillIntoGrid(wrapper, super.getNumberOfControls());
+ if (SystemReader.getInstance().isMacOS()) {
+ // The default "Open File" dialog on Mac does not show
+ // hidden files, even if the user has enabled showing them
+ // in the Finder. GPG is normally installed under /usr,
+ // which is a hidden directory on Mac. There is a keyboard
+ // shortcut to make it show hidden files and directories
+ // (Cmd-Shift-.), but that's not obvious. Tell the user
+ // about that shortcut in a tooltip.
+ getChangeControl(wrapper).setToolTipText(
+ UIText.FullWidthFileFieldEditor_buttonTooltipMac);
+ }
+ }
+
+ @Override
+ protected void doFillIntoGrid(Composite parent, int numColumns) {
+ if (parent != wrapper) {
+ layoutData.horizontalSpan = numColumns;
+ }
+ super.doFillIntoGrid(wrapper, super.getNumberOfControls());
+ }
+
+ @Override
+ protected void adjustForNumColumns(int numColumns) {
+ layoutData.horizontalSpan = numColumns;
+ }
+
+ @Override
+ public Label getLabelControl(Composite parent) {
+ return super.getLabelControl(wrapper);
+ }
+
+ @Override
+ public Text getTextControl(Composite parent) {
+ return super.getTextControl(wrapper);
+ }
+
+ @Override
+ protected Button getChangeControl(Composite parent) {
+ return super.getChangeControl(wrapper);
+ }
+
+ @Override
+ public int getNumberOfControls() {
+ return 1;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties
index 244c457e66..7d9d6442a0 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties
@@ -1107,6 +1107,10 @@ RefUpdateElement_UrisDecoration=({0})
CommittingPreferencePage_commitMessageHistory=Maximum number of commit messages in history:
CommittingPreferencePage_title=Committing
CommittingPreferencePage_formatting=Formatting
+CommittingPreferencePage_gpgExecutableInvalid=The path for the GPG executable is invalid.
+CommittingPreferencePage_gpgExecutableLabel=GPG Executable:
+CommittingPreferencePage_gpgExecutableNotExecutable=The GPG executable file is not executable.
+CommittingPreferencePage_gpgExecutableTooltip=Defines the path to the GPG program to use for signing commits or tags. If empty, the program given by the git config (gpg.program) is used, or if that is also empty, a GPG available on the PATH.
CommittingPreferencePage_hardWrapMessage=Hard-wrap commit message
CommittingPreferencePage_hardWrapMessageTooltip=Wrap text in commit message editor while typing
CommittingPreferencePage_warnAboutCommitMessageSecondLine=Warn if second line of commit message is not empty
@@ -1156,6 +1160,8 @@ DateFormatPreferencePage_helpGitLocale_label=Time of the commit in the committer
DateFormatPreferencePage_helpGitLocaleLocal_label=Time of the commit converted to your local time zone in your system format, without time zone.
DateFormatPreferencePage_helpCustom_label=Time of the commit converted to your local time zone in the format specified.
+FullWidthFileFieldEditor_buttonTooltipMac=Hit Cmd-Shift-. in the dialog to show hidden directories like /usr.
+
BasicConfigurationDialog_ConfigLocationInfo=These settings will be stored in the Git configuration file in your home directory.
BasicConfigurationDialog_DialogMessage=When creating a commit, Git records name and e-mail of author and committer. Please fill in the information so that your commits are correctly attributed.
BasicConfigurationDialog_DialogTitle=Please identify yourself

Back to the top