diff options
author | Thomas Wolf | 2015-08-25 19:29:12 +0000 |
---|---|---|
committer | Matthias Sohn | 2015-08-28 14:21:30 +0000 |
commit | 3a6a38c067ef807fe3898367c891029cf3611e17 (patch) | |
tree | 0f72a014855348db8a8e16ed0f6da714e4f242c5 | |
parent | 92de59d06e5eb67856257a3eafac2e83febbb916 (diff) | |
download | egit-3a6a38c067ef807fe3898367c891029cf3611e17.tar.gz egit-3a6a38c067ef807fe3898367c891029cf3611e17.tar.xz egit-3a6a38c067ef807fe3898367c891029cf3611e17.zip |
Auto-configure Gerrit after ssh clone
ConfigureGerritAfterCloneTask.isGerrit() only worked for HTTP(S)
cloning in general; for ssh cloning it worked only for git.eclipse.org.
Use the same approach as for HTTP also for SSH: invoke Gerrit's
"gerrit version" SSH endpoint and see what we get.
Bug: 474019
Change-Id: Ic97040add0e805f34ef00f194e2a34ad3011fdcf
Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
-rw-r--r-- | org.eclipse.egit.core/src/org/eclipse/egit/core/op/ConfigureGerritAfterCloneTask.java | 100 | ||||
-rw-r--r-- | org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/clone/AbstractGitCloneWizard.java | 24 |
2 files changed, 114 insertions, 10 deletions
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/ConfigureGerritAfterCloneTask.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/ConfigureGerritAfterCloneTask.java index 0a76a58a0c..43723255d3 100644 --- a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/ConfigureGerritAfterCloneTask.java +++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/ConfigureGerritAfterCloneTask.java @@ -1,5 +1,6 @@ /******************************************************************************* * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com> + * Copyright (C) 2015, 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 v1.0 @@ -8,13 +9,16 @@ *******************************************************************************/ package org.eclipse.egit.core.op; +import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; +import java.util.regex.Pattern; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; @@ -25,8 +29,14 @@ import org.eclipse.egit.core.op.CloneOperation.PostCloneTask; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.RemoteConfig; +import org.eclipse.jgit.transport.RemoteSession; +import org.eclipse.jgit.transport.SshSessionFactory; import org.eclipse.jgit.transport.URIish; +import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.io.MessageWriter; +import org.eclipse.jgit.util.io.StreamCopyThread; /** * Configure Gerrit if repository was cloned from a Gerrit server @@ -45,6 +55,29 @@ public class ConfigureGerritAfterCloneTask implements PostCloneTask { private static final String GERRIT_CONFIG_SERVER_VERSION_API = "/config/server/version"; //$NON-NLS-1$ + private static final int GERRIT_SSHD_DEFAULT_PORT = 29418; + + private static final String GERRIT_SSHD_VERSION_API = "gerrit version"; //$NON-NLS-1$ + + /** + * Pattern to match the sshd reply[1] against to determine whether it's a + * Gerrit. + * <p> + * [1]<a href= + * "https://gerrit-documentation.storage.googleapis.com/Documentation/2.11/cmd-version.html"> + * Gerrit 2.11 gerrit version ssh command</a> + * </p> + * <p> + * We match the whole reply from Gerrit's sshd (as opposed to a prefix match + * for "gerrit version") just in case a non-Gerrit has the great idea to + * return an error message like "gerrit version: unknown command" or some + * such on its stdout... + * </p> + */ + private static final Pattern GERRIT_SSHD_REPLY = Pattern + .compile(GERRIT_SSHD_VERSION_API + + "\\s+(?:\\d+(?:\\.\\d+)+|.+-\\d+-g[0-9a-fA-F]{7})"); //$NON-NLS-1$ + /** * To prevent against Cross Site Script Inclusion (XSSI) attacks, the Gerrit * JSON response body starts with a magic prefix line we can use as a second @@ -61,6 +94,8 @@ public class ConfigureGerritAfterCloneTask implements PostCloneTask { private final String remoteName; + private final CredentialsProvider credentialsProvider; + private int timeout; /** @@ -68,14 +103,19 @@ public class ConfigureGerritAfterCloneTask implements PostCloneTask { * not null * @param remoteName * not null + * @param credentialsProvider + * {@link CredentialsProvider} to use for remote communication; + * if {@code null} auto-configuration for repositories cloned + * over ssh will work only for git.eclipse.org * @param timeout * timeout for remote communication in seconds */ public ConfigureGerritAfterCloneTask(String uri, String remoteName, - int timeout) { + CredentialsProvider credentialsProvider, int timeout) { this.uri = uri; this.remoteName = remoteName; this.timeout = timeout; + this.credentialsProvider = credentialsProvider; } public void execute(Repository repository, IProgressMonitor monitor) @@ -121,7 +161,8 @@ public class ConfigureGerritAfterCloneTask implements PostCloneTask { if (HTTPS.equals(s) && (u.getPort() == 443 || u.getPort() == -1) && path != null && path.startsWith(GERRIT_CONTEXT_ROOT)) { return true; - } else if (SSH.equals(s) && (u.getPort() == 29418)) { + } else + if (SSH.equals(s) && u.getPort() == GERRIT_SSHD_DEFAULT_PORT) { return true; } } @@ -178,6 +219,25 @@ public class ConfigureGerritAfterCloneTask implements PostCloneTask { httpConnection.disconnect(); } } + } else if (SSH.equals(s)) { + if (u.getPort() < 0 || u.getUser() == null + || credentialsProvider == null) { + return false; + } + URIish sshUri = u.setPath(""); //$NON-NLS-1$ + try { + String result = runSshCommand(sshUri, credentialsProvider, + repo.getFS(), GERRIT_SSHD_VERSION_API); + return result != null + && GERRIT_SSHD_REPLY.matcher(result).matches(); + } catch (IOException e) { + // Something went wrong with the connection or with the command + // execution. Maybe the server didn't recognize the command. Do + // the safe thing and claim it wasn't a Gerrit. In the worst + // case, the user may have to do the Gerrit config setup via + // the ConfigureGerritWizard. + return false; + } } return false; } @@ -213,4 +273,40 @@ public class ConfigureGerritAfterCloneTask implements PostCloneTask { config.save(); } + private String runSshCommand(URIish sshUri, CredentialsProvider provider, + FS fs, String command) throws IOException { + RemoteSession session = null; + Process process = null; + StreamCopyThread errorThread = null; + try (MessageWriter stderr = new MessageWriter()) { + session = SshSessionFactory.getInstance().getSession(sshUri, + provider, fs, 1000 * timeout); + process = session.exec(command, 0); + errorThread = new StreamCopyThread(process.getErrorStream(), + stderr.getRawStream()); + errorThread.start(); + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(process.getInputStream(), + Constants.CHARSET))) { + return reader.readLine(); + } + } finally { + if (errorThread != null) { + try { + errorThread.halt(); + } catch (InterruptedException e) { + // Stop waiting and return anyway. + } finally { + errorThread = null; + } + } + if (process != null) { + process.destroy(); + } + if (session != null) { + SshSessionFactory.getInstance().releaseSession(session); + } + } + } + } diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/clone/AbstractGitCloneWizard.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/clone/AbstractGitCloneWizard.java index b1c53431e5..49ff1d1777 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/clone/AbstractGitCloneWizard.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/clone/AbstractGitCloneWizard.java @@ -61,6 +61,7 @@ import org.eclipse.jface.wizard.Wizard; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.URIish; import org.eclipse.osgi.util.NLS; import org.eclipse.ui.IWorkingSet; @@ -245,17 +246,21 @@ public abstract class AbstractGitCloneWizard extends Wizard { final CloneOperation op = new CloneOperation(uri, allSelected, selectedBranches, workdir, ref != null ? ref.getName() : null, remoteName, timeout); - if (credentials != null) - op.setCredentialsProvider(new EGitCredentialsProvider( - credentials.getUser(), credentials.getPassword())); - else - op.setCredentialsProvider(new EGitCredentialsProvider()); + CredentialsProvider credentialsProvider = null; + if (credentials != null) { + credentialsProvider = new EGitCredentialsProvider( + credentials.getUser(), credentials.getPassword()); + } else { + credentialsProvider = new EGitCredentialsProvider(); + } + op.setCredentialsProvider(credentialsProvider); op.setCloneSubmodules(cloneDestination.isCloneSubmodules()); configureFetchSpec(op, gitRepositoryInfo, remoteName); configurePush(op, gitRepositoryInfo, remoteName); configureRepositoryConfig(op, gitRepositoryInfo); - configureGerrit(op, gitRepositoryInfo, remoteName, timeout); + configureGerrit(op, gitRepositoryInfo, credentialsProvider, remoteName, + timeout); if (cloneDestination.isImportProjects()) { final IWorkingSet[] sets = cloneDestination.getWorkingSets(); @@ -349,9 +354,12 @@ public abstract class AbstractGitCloneWizard extends Wizard { } private void configureGerrit(CloneOperation op, - GitRepositoryInfo gitRepositoryInfo, String remoteName, int timeout) { + GitRepositoryInfo gitRepositoryInfo, + CredentialsProvider credentialsProvider, String remoteName, + int timeout) { ConfigureGerritAfterCloneTask task = new ConfigureGerritAfterCloneTask( - gitRepositoryInfo.getCloneUri(), remoteName, timeout); + gitRepositoryInfo.getCloneUri(), remoteName, + credentialsProvider, timeout); op.addPostCloneTask(task); } |