You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by mi...@apache.org on 2022/07/21 21:37:24 UTC
[maven-scm] branch master updated: [SCM-993] Add tests for SSH private key-based authentication during checkout (clone)
This is an automated email from the ASF dual-hosted git repository.
michaelo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-scm.git
The following commit(s) were added to refs/heads/master by this push:
new b7f2a58ce [SCM-993] Add tests for SSH private key-based authentication during checkout (clone)
b7f2a58ce is described below
commit b7f2a58ceb4acd486b126e1a7caed1f910cf7f12
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Fri Jul 8 08:44:28 2022 +0200
[SCM-993] Add tests for SSH private key-based authentication during checkout (clone)
This closes #155
---
.../main/java/org/apache/maven/scm/ScmFileSet.java | 2 +-
.../maven/scm/manager/AbstractScmManager.java | 3 +-
.../scm/provider/git/gitexe/GitExeScmProvider.java | 20 ++-
.../git/gitexe/command/GitCommandLineUtils.java | 21 ++-
.../gitexe/command/checkin/GitCheckInCommand.java | 28 ++-
.../command/checkout/GitCheckOutCommand.java | 16 +-
.../command/remoteinfo/GitRemoteInfoCommand.java | 13 +-
.../checkout/GitExeSshCheckOutCommandTckTest.java | 71 ++++++++
.../maven-scm-provider-gittest/pom.xml | 28 +++
.../checkout/GitSshCheckOutCommandTckTest.java | 187 +++++++++++++++++++++
.../maven-scm-provider-jgit/pom.xml | 5 +
.../scm/provider/git/jgit/JGitTestScmProvider.java | 104 ++++++++++++
.../checkout/JGitSshCheckOutCommandTckTest.java | 148 ++++++++++++++++
.../src/test/resources/ssh-keypairs/README | 4 +
.../src/test/resources/ssh-keypairs/sample_rsa | 50 ++++++
.../src/test/resources/ssh-keypairs/sample_rsa.pub | 1 +
pom.xml | 7 +
17 files changed, 691 insertions(+), 17 deletions(-)
diff --git a/maven-scm-api/src/main/java/org/apache/maven/scm/ScmFileSet.java b/maven-scm-api/src/main/java/org/apache/maven/scm/ScmFileSet.java
index 28d0022b3..8401c844b 100644
--- a/maven-scm-api/src/main/java/org/apache/maven/scm/ScmFileSet.java
+++ b/maven-scm-api/src/main/java/org/apache/maven/scm/ScmFileSet.java
@@ -43,7 +43,7 @@ public class ScmFileSet
private static final long serialVersionUID = -5978597349974797556L;
private static final String DELIMITER = ",";
-
+
/** @see DirectoryScanner#DEFAULTEXCLUDES */
private static final String DEFAULT_EXCLUDES = StringUtils.join( DirectoryScanner.DEFAULTEXCLUDES, DELIMITER );
diff --git a/maven-scm-api/src/main/java/org/apache/maven/scm/manager/AbstractScmManager.java b/maven-scm-api/src/main/java/org/apache/maven/scm/manager/AbstractScmManager.java
index 343dce5ee..8514a1bb9 100644
--- a/maven-scm-api/src/main/java/org/apache/maven/scm/manager/AbstractScmManager.java
+++ b/maven-scm-api/src/main/java/org/apache/maven/scm/manager/AbstractScmManager.java
@@ -79,7 +79,8 @@ public abstract class AbstractScmManager
{
requireNonNull( providers );
this.scmProviders.clear();
- providers.forEach( this::setScmProvider );
+ // first provider must not be overwritten by subsequent ones if they are registered for the same key
+ providers.forEach( this.scmProviders::putIfAbsent );
}
/**
diff --git a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/GitExeScmProvider.java b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/GitExeScmProvider.java
index 50943c12f..1b3948441 100644
--- a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/GitExeScmProvider.java
+++ b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/GitExeScmProvider.java
@@ -23,6 +23,8 @@ import javax.inject.Named;
import javax.inject.Singleton;
import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
import org.apache.maven.scm.ScmException;
import org.apache.maven.scm.ScmFileSet;
@@ -54,6 +56,13 @@ import org.apache.maven.scm.repository.ScmRepositoryException;
public class GitExeScmProvider
extends AbstractGitScmProvider
{
+ private final Map<String, String> environmentVariables;
+
+ public GitExeScmProvider()
+ {
+ environmentVariables = new HashMap<>();
+ }
+
/** {@inheritDoc} */
protected GitCommand getAddCommand()
{
@@ -75,13 +84,13 @@ public class GitExeScmProvider
/** {@inheritDoc} */
protected GitCommand getCheckInCommand()
{
- return new GitCheckInCommand();
+ return new GitCheckInCommand( environmentVariables );
}
/** {@inheritDoc} */
protected GitCommand getCheckOutCommand()
{
- return new GitCheckOutCommand();
+ return new GitCheckOutCommand( environmentVariables );
}
/** {@inheritDoc} */
@@ -141,7 +150,7 @@ public class GitExeScmProvider
/** {@inheritDoc} */
protected GitCommand getRemoteInfoCommand()
{
- return new GitRemoteInfoCommand();
+ return new GitRemoteInfoCommand( environmentVariables );
}
/** {@inheritDoc} */
@@ -162,4 +171,9 @@ public class GitExeScmProvider
return result.getInfoItems().get( 0 ).getURL();
}
+
+ public void setEnvironmentVariable( String key, String value )
+ {
+ environmentVariables.put( key, value );
+ }
}
diff --git a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/GitCommandLineUtils.java b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/GitCommandLineUtils.java
index 0d9a0e5b4..2dc908b9e 100644
--- a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/GitCommandLineUtils.java
+++ b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/GitCommandLineUtils.java
@@ -33,7 +33,9 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
/**
* Command line construction utility.
@@ -87,15 +89,30 @@ public final class GitCommandLineUtils
}
}
+ /**
+ *
+ * @param workingDirectory
+ * @param command
+ * @return TODO
+ */
+ public static Commandline getBaseGitCommandLine( File workingDirectory, String command )
+ {
+ return getBaseGitCommandLine( workingDirectory, command, Collections.emptyMap() );
+ }
+
/**
*
* @param workingDirectory
* @param command
+ * @param environment
* @return TODO
*/
- public static Commandline getBaseGitCommandLine( File workingDirectory, String command )
+ public static Commandline getBaseGitCommandLine( File workingDirectory, String command,
+ Map<String, String> environment )
{
- return getAnonymousBaseGitCommandLine( workingDirectory, command );
+ Commandline cl = getAnonymousBaseGitCommandLine( workingDirectory, command );
+ environment.forEach( cl::addEnvironment );
+ return cl;
}
/**
diff --git a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/checkin/GitCheckInCommand.java b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/checkin/GitCheckInCommand.java
index 0e42d6ab9..09cbdb8a2 100644
--- a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/checkin/GitCheckInCommand.java
+++ b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/checkin/GitCheckInCommand.java
@@ -47,6 +47,7 @@ import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
/**
* @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
@@ -57,6 +58,14 @@ public class GitCheckInCommand
extends AbstractCheckInCommand
implements GitCommand
{
+ private final Map<String, String> environmentVariables;
+
+ public GitCheckInCommand( Map<String, String> environmentVariables )
+ {
+ super();
+ this.environmentVariables = environmentVariables;
+ }
+
/** {@inheritDoc} */
protected CheckInScmResult executeCheckInCommand( ScmProviderRepository repo, ScmFileSet fileSet, String message,
ScmVersion version )
@@ -145,7 +154,7 @@ public class GitCheckInCommand
return new CheckInScmResult( null, statusConsumer.getChangedFiles() );
}
- Commandline clCommit = createCommitCommandLine( repository, fileSet, messageFile );
+ Commandline clCommit = createCommitCommandLine( repository, fileSet, messageFile, environmentVariables );
exitCode = GitCommandLineUtils.execute( clCommit, stdout, stderr );
if ( exitCode != 0 )
@@ -211,11 +220,12 @@ public class GitCheckInCommand
//
// ----------------------------------------------------------------------
- public static Commandline createPushCommandLine( GitScmProviderRepository repository,
- ScmFileSet fileSet, ScmVersion version )
+ public Commandline createPushCommandLine( GitScmProviderRepository repository,
+ ScmFileSet fileSet, ScmVersion version )
throws ScmException
{
- Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( fileSet.getBasedir(), "push" );
+ Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( fileSet.getBasedir(), "push",
+ environmentVariables );
String branch = GitBranchCommand.getCurrentBranch( repository, fileSet );
@@ -235,7 +245,15 @@ public class GitCheckInCommand
File messageFile )
throws ScmException
{
- Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( fileSet.getBasedir(), "commit" );
+ return createCommitCommandLine( repository, fileSet, messageFile, Collections.emptyMap() );
+ }
+
+ public static Commandline createCommitCommandLine( GitScmProviderRepository repository, ScmFileSet fileSet,
+ File messageFile, Map<String, String> environmentVariables )
+ throws ScmException
+ {
+ Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( fileSet.getBasedir(), "commit",
+ environmentVariables );
cl.createArg().setValue( "--verbose" );
diff --git a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/checkout/GitCheckOutCommand.java b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/checkout/GitCheckOutCommand.java
index 2f9e7c0dc..915810651 100644
--- a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/checkout/GitCheckOutCommand.java
+++ b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/checkout/GitCheckOutCommand.java
@@ -20,6 +20,7 @@ package org.apache.maven.scm.provider.git.gitexe.command.checkout;
*/
import java.io.File;
+import java.util.Map;
import org.apache.maven.scm.CommandParameter;
import org.apache.maven.scm.CommandParameters;
@@ -52,6 +53,14 @@ public class GitCheckOutCommand
extends AbstractCheckOutCommand
implements GitCommand
{
+ private final Map<String, String> environmentVariables;
+
+ public GitCheckOutCommand( Map<String, String> environmentVariables )
+ {
+ super();
+ this.environmentVariables = environmentVariables;
+ }
+
/**
* For git, the given repository is a remote one.
* We have to clone it first if the working directory does not contain a git repo yet,
@@ -104,7 +113,7 @@ public class GitCheckOutCommand
lastCommandLine = clClone.toString();
}
- GitRemoteInfoCommand gitRemoteInfoCommand = new GitRemoteInfoCommand();
+ GitRemoteInfoCommand gitRemoteInfoCommand = new GitRemoteInfoCommand( environmentVariables );
RemoteInfoScmResult result = gitRemoteInfoCommand.executeRemoteInfoCommand( repository, null, null );
@@ -173,7 +182,8 @@ public class GitCheckOutCommand
private Commandline createCloneCommand( GitScmProviderRepository repository, File workingDirectory,
ScmVersion version, boolean binary, boolean shallow )
{
- Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( workingDirectory.getParentFile(), "clone" );
+ Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( workingDirectory.getParentFile(), "clone",
+ environmentVariables );
forceBinary( cl, binary );
@@ -240,7 +250,7 @@ public class GitCheckOutCommand
}
else
{
- cl = GitCommandLineUtils.getBaseGitCommandLine( workingDirectory, "pull" );
+ cl = GitCommandLineUtils.getBaseGitCommandLine( workingDirectory, "pull", environmentVariables );
cl.createArg().setValue( repository.getFetchUrl() );
cl.createArg().setValue( "master" );
diff --git a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/remoteinfo/GitRemoteInfoCommand.java b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/remoteinfo/GitRemoteInfoCommand.java
index c97d5a3f3..87b207920 100644
--- a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/remoteinfo/GitRemoteInfoCommand.java
+++ b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/remoteinfo/GitRemoteInfoCommand.java
@@ -19,6 +19,8 @@ package org.apache.maven.scm.provider.git.gitexe.command.remoteinfo;
* under the License.
*/
+import java.util.Map;
+
import org.apache.maven.scm.CommandParameters;
import org.apache.maven.scm.ScmException;
import org.apache.maven.scm.ScmFileSet;
@@ -38,6 +40,13 @@ public class GitRemoteInfoCommand
extends AbstractRemoteInfoCommand
implements GitCommand
{
+ private final Map<String, String> environmentVariables;
+
+ public GitRemoteInfoCommand( Map<String, String> environmentVariables )
+ {
+ super();
+ this.environmentVariables = environmentVariables;
+ }
@Override
public RemoteInfoScmResult executeRemoteInfoCommand( ScmProviderRepository repository, ScmFileSet fileSet,
@@ -65,9 +74,9 @@ public class GitRemoteInfoCommand
//
// ----------------------------------------------------------------------
- public static Commandline createCommandLine( GitScmProviderRepository repository )
+ public Commandline createCommandLine( GitScmProviderRepository repository )
{
- Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( null, "ls-remote" );
+ Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( null, "ls-remote", environmentVariables );
cl.setWorkingDirectory( System.getProperty( "java.io.tmpdir" ) );
diff --git a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/test/java/org/apache/maven/scm/provider/git/gitexe/command/checkout/GitExeSshCheckOutCommandTckTest.java b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/test/java/org/apache/maven/scm/provider/git/gitexe/command/checkout/GitExeSshCheckOutCommandTckTest.java
new file mode 100644
index 000000000..f83d0488d
--- /dev/null
+++ b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/test/java/org/apache/maven/scm/provider/git/gitexe/command/checkout/GitExeSshCheckOutCommandTckTest.java
@@ -0,0 +1,71 @@
+package org.apache.maven.scm.provider.git.gitexe.command.checkout;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.maven.scm.provider.git.command.checkout.GitSshCheckOutCommandTckTest;
+import org.apache.maven.scm.provider.git.gitexe.GitExeScmProvider;
+import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
+import org.apache.maven.scm.repository.ScmRepository;
+
+/**
+ *
+ */
+public class GitExeSshCheckOutCommandTckTest
+ extends GitSshCheckOutCommandTckTest
+{
+ private Path knownHostsFile;
+
+ public static final String VARIABLE_GIT_SSH_COMMAND = "GIT_SSH_COMMAND"; // https://git-scm.com/docs/git#Documentation/git.txt-codeGITSSHCOMMANDcode, requires git 2.3.0 or newer
+
+ public GitExeSshCheckOutCommandTckTest() throws GeneralSecurityException
+ {
+ super();
+ }
+
+ @Override
+ protected String getScmProvider()
+ {
+ return "git";
+ }
+
+ @Override
+ public void configureCredentials( ScmRepository repository, String passphrase ) throws Exception
+ {
+ super.configureCredentials( repository, passphrase );
+ GitScmProviderRepository providerRepository = (GitScmProviderRepository) repository.getProviderRepository();
+ GitExeScmProvider provider = (GitExeScmProvider) getScmManager().getProviderByRepository( getScmRepository() );
+ knownHostsFile = Files.createTempFile( "known-hosts", null );
+ provider.setEnvironmentVariable( VARIABLE_GIT_SSH_COMMAND, "ssh -o UserKnownHostsFile=" + knownHostsFile +
+ " -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -i " + FilenameUtils.separatorsToUnix( providerRepository.getPrivateKey() ) );
+ }
+
+ @Override
+ public void removeRepo() throws Exception
+ {
+ super.removeRepo();
+ Files.deleteIfExists( knownHostsFile );
+ }
+
+}
diff --git a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gittest/pom.xml b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gittest/pom.xml
index 728c1582f..2d5257682 100644
--- a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gittest/pom.xml
+++ b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gittest/pom.xml
@@ -33,6 +33,10 @@
<name>Maven SCM Git Provider TCK Tests</name>
<description>Tests library for SCM Git Provider.</description>
+ <properties>
+ <minaSshdVersion>2.8.0</minaSshdVersion>
+ </properties>
+
<dependencies>
<dependency>
<groupId>org.apache.maven.scm</groupId>
@@ -46,6 +50,30 @@
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
</dependency>
+ <!-- for testing SSH authentication -->
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-git</artifactId>
+ <version>${minaSshdVersion}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-common</artifactId>
+ <version>${minaSshdVersion}</version>
+ <type>test-jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-core</artifactId>
+ <version>${minaSshdVersion}</version>
+ <type>test-jar</type>
+ </dependency>
+ <!-- for creating SSH keypairs dynamically -->
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ <version>1.70</version>
+ </dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
diff --git a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gittest/src/main/java/org/apache/maven/scm/provider/git/command/checkout/GitSshCheckOutCommandTckTest.java b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gittest/src/main/java/org/apache/maven/scm/provider/git/command/checkout/GitSshCheckOutCommandTckTest.java
new file mode 100644
index 000000000..376d8acf2
--- /dev/null
+++ b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gittest/src/main/java/org/apache/maven/scm/provider/git/command/checkout/GitSshCheckOutCommandTckTest.java
@@ -0,0 +1,187 @@
+package org.apache.maven.scm.provider.git.command.checkout;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.file.FileSystem;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.maven.scm.provider.ScmProviderRepositoryWithHost;
+import org.apache.maven.scm.provider.git.GitScmTestUtils;
+import org.apache.maven.scm.repository.ScmRepository;
+import org.apache.maven.scm.tck.command.checkout.CheckOutCommandTckTest;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.writer.openssh.OpenSSHKeyEncryptionContext;
+import org.apache.sshd.common.config.keys.writer.openssh.OpenSSHKeyPairResourceWriter;
+import org.apache.sshd.git.GitLocationResolver;
+import org.apache.sshd.git.pack.GitPackCommandFactory;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.auth.pubkey.KeySetPublickeyAuthenticator;
+import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator;
+import org.apache.sshd.server.session.ServerSession;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
+import org.bouncycastle.openssl.PKCS8Generator;
+import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
+import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator;
+import org.bouncycastle.util.io.pem.PemObject;
+import org.junit.Assume;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ *
+ */
+public abstract class GitSshCheckOutCommandTckTest
+ extends CheckOutCommandTckTest
+{
+ protected final SshServer sshServer;
+ protected final KeyPair keyPair;
+ protected final List<PublicKey> acceptedPublicKeys;
+
+ @Rule
+ public TemporaryFolder tmpDirectory = new TemporaryFolder();
+
+ protected GitSshCheckOutCommandTckTest() throws GeneralSecurityException
+ {
+ sshServer = CoreTestSupportUtils.setupTestServer( getClass() );
+ keyPair = CommonTestSupportUtils.generateKeyPair( KeyUtils.RSA_ALGORITHM, 2048 );
+ acceptedPublicKeys = new ArrayList<>();
+ acceptedPublicKeys.add( keyPair.getPublic() );
+ PublickeyAuthenticator authenticator = new KeySetPublickeyAuthenticator( "onlykey",
+ acceptedPublicKeys );
+ sshServer.setPublickeyAuthenticator( authenticator );
+ }
+
+ void writePrivateKeyAsPkcs8( Path file, String passphrase )
+ throws IOException, GeneralSecurityException
+ {
+ // encryption only optional
+ if ( passphrase != null )
+ {
+ // encryption with format outlined in https://dnaeon.github.io/openssh-private-key-binary-format/
+ OpenSSHKeyPairResourceWriter writer = new OpenSSHKeyPairResourceWriter();
+ OpenSSHKeyEncryptionContext context = new OpenSSHKeyEncryptionContext();
+ context.setCipherType( "192" );
+ context.setPassword( passphrase );
+ try ( OutputStream output = Files.newOutputStream( file ) )
+ {
+ writer.writePrivateKey( keyPair, "comment", context, output );
+ }
+ }
+ else
+ {
+ // wrap unencrypted private key as regular PKCS8 private key
+ PKCS8Generator pkcs8Generator = new JcaPKCS8Generator( keyPair.getPrivate(), null );
+ PemObject pemObject = pkcs8Generator.generate();
+
+ try ( Writer writer = Files.newBufferedWriter( file );
+ JcaPEMWriter pw = new JcaPEMWriter( writer ) )
+ {
+ pw.writeObject( pemObject );
+ }
+ }
+
+ if ( file.getFileSystem().supportedFileAttributeViews().contains( "posix" ) )
+ {
+ // must only be readable/writeable by me
+ Files.setPosixFilePermissions( file, PosixFilePermissions.fromString( "rwx------" ) );
+ }
+ }
+
+ protected abstract String getScmProvider();
+
+ /** {@inheritDoc} */
+ public String getScmUrl()
+ throws Exception
+ {
+ return "scm:" + getScmProvider() + ":ssh://localhost:" + sshServer.getPort() + "/repository";
+ }
+
+ public void configureCredentials( ScmRepository repository, String passphrase )
+ throws Exception
+ {
+ ScmProviderRepositoryWithHost providerRepository =
+ ScmProviderRepositoryWithHost.class.cast( repository.getProviderRepository() );
+ // store as file
+ Path privateKeyFile = tmpDirectory.newFile().toPath();
+ writePrivateKeyAsPkcs8( privateKeyFile, passphrase );
+ providerRepository.setPrivateKey( privateKeyFile.toString() );
+ providerRepository.setPassphrase( passphrase ); // may be null
+ }
+
+ /** {@inheritDoc} */
+ public void initRepo()
+ throws Exception
+ {
+ GitScmTestUtils.initRepo( "src/test/resources/repository/", getRepositoryRoot(), getWorkingDirectory() );
+
+ GitLocationResolver gitLocationResolver = new GitLocationResolver()
+ {
+ @Override
+ public Path resolveRootDirectory( String command, String[] args, ServerSession session, FileSystem fs )
+ throws IOException
+ {
+ return getRepositoryRoot().getParentFile().toPath();
+ }
+ };
+ sshServer.setCommandFactory( new GitPackCommandFactory( gitLocationResolver ) );
+ sshServer.start();
+
+ // as checkout also already happens in setup() make sure to configure credentials here as well
+ configureCredentials( getScmRepository(), null );
+ }
+
+ @Override
+ public void removeRepo() throws Exception
+ {
+ sshServer.stop();
+ super.removeRepo();
+ }
+
+ @Override
+ @Test
+ public void testCheckOutCommandTest()
+ throws Exception
+ {
+ configureCredentials( getScmRepository(), null );
+ super.testCheckOutCommandTest();
+ }
+
+ @Test
+ public void testCheckOutCommandWithPassphraseTest() throws Exception
+ {
+ // TODO: currently no easy way to pass passphrase in gitexe
+ Assume.assumeTrue( "Ignore test with passphrase for provider " + getScmProvider(),
+ "jgit".equals( getScmProvider() ) );
+ configureCredentials( getScmRepository(), "mySecret" );
+ super.testCheckOutCommandTest();
+ }
+}
diff --git a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-jgit/pom.xml b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-jgit/pom.xml
index d343e513a..9f15c4c56 100644
--- a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-jgit/pom.xml
+++ b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-jgit/pom.xml
@@ -98,6 +98,11 @@
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-jgit/src/test/java/org/apache/maven/scm/provider/git/jgit/JGitTestScmProvider.java b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-jgit/src/test/java/org/apache/maven/scm/provider/git/jgit/JGitTestScmProvider.java
new file mode 100644
index 000000000..befeffb1c
--- /dev/null
+++ b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-jgit/src/test/java/org/apache/maven/scm/provider/git/jgit/JGitTestScmProvider.java
@@ -0,0 +1,104 @@
+package org.apache.maven.scm.provider.git.jgit;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import javax.annotation.Priority;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import java.util.function.Consumer;
+
+import org.apache.maven.scm.provider.ScmProvider;
+import org.apache.maven.scm.provider.git.command.GitCommand;
+import org.apache.maven.scm.provider.git.jgit.command.checkin.JGitCheckInCommand;
+import org.apache.maven.scm.provider.git.jgit.command.checkout.JGitCheckOutCommand;
+import org.apache.maven.scm.provider.git.jgit.command.remoteinfo.JGitRemoteInfoCommand;
+import org.codehaus.plexus.components.interactivity.Prompter;
+import org.eclipse.jgit.api.TransportCommand;
+
+/**
+ * Allows to register callbacks for all commands leveraging {@link TransportCommand}.
+ */
+@Singleton
+@Named( "jgit" )
+@Priority( 1 ) // must have higher priority than default JGitScmProvider
+public class JGitTestScmProvider
+ extends JGitScmProvider implements ScmProvider
+{
+ private Consumer<JGitCheckInCommand> checkInCommandCallback;
+ private Consumer<JGitCheckOutCommand> checkOutCommandCallback;
+ private Consumer<JGitRemoteInfoCommand> remoteInfoCommandCallback;
+
+ @Inject
+ public JGitTestScmProvider( Prompter prompter )
+ {
+ super( prompter );
+ }
+
+ public void registerCheckInCommandCallback( Consumer<JGitCheckInCommand> gitCommandConsumer )
+ {
+ checkInCommandCallback = gitCommandConsumer;
+ }
+
+ public void registerCheckOutCommandCallback( Consumer<JGitCheckOutCommand> gitCommandConsumer )
+ {
+ checkOutCommandCallback = gitCommandConsumer;
+ }
+
+ public void registerRemoteInfoCommandCallback( Consumer<JGitRemoteInfoCommand> gitCommandConsumer )
+ {
+ remoteInfoCommandCallback = gitCommandConsumer;
+ }
+
+ @Override
+ protected GitCommand getCheckInCommand()
+ {
+ JGitCheckInCommand command = (JGitCheckInCommand) super.getCheckInCommand();
+ if ( checkInCommandCallback != null )
+ {
+ checkInCommandCallback.accept( command );
+ }
+ return command;
+ }
+
+ @Override
+ protected GitCommand getCheckOutCommand()
+ {
+ JGitCheckOutCommand command = (JGitCheckOutCommand) super.getCheckOutCommand();
+ if ( checkOutCommandCallback != null )
+ {
+ checkOutCommandCallback.accept( command );
+ }
+ return command;
+ }
+
+ @Override
+ protected GitCommand getRemoteInfoCommand()
+ {
+ JGitRemoteInfoCommand command = (JGitRemoteInfoCommand) super.getRemoteInfoCommand();
+ if ( remoteInfoCommandCallback != null )
+ {
+ remoteInfoCommandCallback.accept( command );
+ }
+ return command;
+ }
+
+}
diff --git a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-jgit/src/test/java/org/apache/maven/scm/provider/git/jgit/command/checkout/JGitSshCheckOutCommandTckTest.java b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-jgit/src/test/java/org/apache/maven/scm/provider/git/jgit/command/checkout/JGitSshCheckOutCommandTckTest.java
new file mode 100644
index 000000000..faaea591f
--- /dev/null
+++ b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-jgit/src/test/java/org/apache/maven/scm/provider/git/jgit/command/checkout/JGitSshCheckOutCommandTckTest.java
@@ -0,0 +1,148 @@
+package org.apache.maven.scm.provider.git.jgit.command.checkout;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+import java.util.function.Consumer;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.maven.scm.provider.ScmProviderRepositoryWithHost;
+import org.apache.maven.scm.provider.git.command.checkout.GitSshCheckOutCommandTckTest;
+import org.apache.maven.scm.provider.git.jgit.JGitTestScmProvider;
+import org.apache.maven.scm.provider.git.jgit.command.ScmProviderAwareSshdSessionFactory;
+import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
+import org.apache.maven.scm.repository.ScmRepository;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.internal.transport.sshd.OpenSshServerKeyDatabase;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.sshd.ServerKeyDatabase;
+import org.eclipse.jgit.util.FileUtils;
+import org.junit.Test;
+import org.slf4j.Logger;
+
+/** @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a> */
+public class JGitSshCheckOutCommandTckTest
+ extends GitSshCheckOutCommandTckTest
+{
+
+ public JGitSshCheckOutCommandTckTest() throws GeneralSecurityException, IOException
+ {
+ super();
+ }
+
+ @Override
+ protected String getScmProvider()
+ {
+ return "jgit";
+ }
+
+ @Override
+ public void initRepo() throws Exception
+ {
+ super.initRepo();
+ JGitTestScmProvider provider = (JGitTestScmProvider) getScmManager().getProviderByRepository( getScmRepository() );
+ // accept all hosts
+ provider.registerCheckOutCommandCallback( new Consumer<JGitCheckOutCommand>()
+ {
+ @Override
+ public void accept( JGitCheckOutCommand command )
+ {
+ command.setSshSessionFactorySupplier( AcceptAllHostsSshdSessionFactory::new );
+ }
+
+ } );
+ }
+
+ private static final class AcceptAllHostsSshdSessionFactory extends ScmProviderAwareSshdSessionFactory
+ {
+ public AcceptAllHostsSshdSessionFactory( GitScmProviderRepository repo, Logger logger )
+ {
+ super( repo, logger );
+ }
+
+ @Override
+ protected ServerKeyDatabase createServerKeyDatabase( File homeDir, File sshDir )
+ {
+ return new OpenSshServerKeyDatabase( false, null )
+ {
+ @Override
+ public boolean accept( @NonNull String connectAddress,
+ @NonNull InetSocketAddress remoteAddress,
+ @NonNull PublicKey serverKey,
+ @NonNull Configuration config, CredentialsProvider provider )
+ {
+ return true;
+ }
+ };
+ }
+ }
+
+ @Override
+ protected void deleteDirectory( File directory )
+ throws IOException
+ {
+ if ( directory.exists() )
+ {
+ FileUtils.delete( directory, FileUtils.RECURSIVE | FileUtils.RETRY );
+ }
+ }
+
+ @Test
+ public void testCheckOutCommandWithPregeneratedKeysTest()
+ throws Exception
+ {
+ // test key pairs being generated with ssh-keygen (they have a slighly different format than the ones tested
+ // in testCheckOutCommandWithPassphraseTest and testCheckOutCommandTest)
+ configureKeypairFromClasspathResource( getScmRepository(), "sample_rsa", "mySecret");
+ super.testCheckOutCommandTest();
+ }
+
+ private void configureKeypairFromClasspathResource( ScmRepository repository, String resourceName, String passphrase )
+ throws IOException, GeneralSecurityException
+ {
+ // accept public key
+ try ( InputStream publicKeyInputStream = this.getClass().getResourceAsStream( "/ssh-keypairs/" + resourceName + ".pub" ) )
+ {
+ PublicKey publicKey = PublicKeyEntry.parsePublicKeyEntry( IOUtils.toString( publicKeyInputStream, StandardCharsets.US_ASCII ) ).resolvePublicKey( null, null, null );
+ acceptedPublicKeys.add( publicKey );
+ }
+ Path privateKeyFile = Files.createTempFile( "privateKey", null );
+ // private key into tmp file
+ try ( InputStream privateKeyInputStream = this.getClass().getResourceAsStream( "/ssh-keypairs/" + resourceName ) )
+ {
+ Files.copy( privateKeyInputStream, privateKeyFile, StandardCopyOption.REPLACE_EXISTING );
+ }
+ // configure provider repository with private key details
+ ScmProviderRepositoryWithHost providerRepository =
+ ScmProviderRepositoryWithHost.class.cast( repository.getProviderRepository() );
+ providerRepository.setPassphrase( passphrase ); // may be null
+ providerRepository.setPrivateKey( privateKeyFile.toString() );
+ }
+}
diff --git a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-jgit/src/test/resources/ssh-keypairs/README b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-jgit/src/test/resources/ssh-keypairs/README
new file mode 100644
index 000000000..ea832c158
--- /dev/null
+++ b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-jgit/src/test/resources/ssh-keypairs/README
@@ -0,0 +1,4 @@
+The keys in here have only been created via "ssh-keygen" for testing purposes,
+they are not usable in other context, especially they don't grant access to any server.
+
+For all private keys which are encrypted, they have the passphrase "mySecret".
diff --git a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-jgit/src/test/resources/ssh-keypairs/sample_rsa b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-jgit/src/test/resources/ssh-keypairs/sample_rsa
new file mode 100644
index 000000000..5307fdbd5
--- /dev/null
+++ b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-jgit/src/test/resources/ssh-keypairs/sample_rsa
@@ -0,0 +1,50 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+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
+CFHN0oAR6VHE53faIMez2A0XY=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-jgit/src/test/resources/ssh-keypairs/sample_rsa.pub b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-jgit/src/test/resources/ssh-keypairs/sample_rsa.pub
new file mode 100644
index 000000000..e2cf1d628
--- /dev/null
+++ b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-jgit/src/test/resources/ssh-keypairs/sample_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC8e3gqfjzieCzdozP2XLd5H0TyGmT7lRckFcwbOuudArJ/cQrN2JpK0Kze+Y+1uT7eB2bITmA+yntnPcqjSD7OyS+xGhw4XevvHZiKSsNqbS98Woxlop6z6HyuXeIeKCMsx1YXrGrej12uMEHHx1Qyq5kN2nECip+LlXIY25PCPQcj1F0x25Rl3x819QpRi9eIwnyclyeq381eefn7sJS0rpHC2uN6PtmuDP4oDJ59HYuQfRQNkjubGTKCd0Zi+aJy5tRYBUnLEQ3ZIbmoXnA8uiIkSZoX5q5w9lxarMp4jkiaLdKzNLZInSQpn1Pw9NiZqgrH83xyVgusr9id22lNf2PqFQbEi2tcYHbH2DBaysh0azNkWdBlTmjY1KuqzL8a3Cw8vCxccBvYKMRQNrzAmPO7Tg7fq4qAlhF64nk4PEFbsD+gLTDmbsn4dE9cXLIrxF9G [...]
diff --git a/pom.xml b/pom.xml
index 122a94d8f..3ffa4873c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -300,6 +300,13 @@
</excludes>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <trimStackTrace>false</trimStackTrace>
+ </configuration>
+ </plugin>
</plugins>
</pluginManagement>
<plugins>