Ssh tests with an Apache MINA sshd test git server
Add a simple ssh git server based on Apache MINA sshd, and use it in new tests that verify ssh operations and in particular a number of bugs that had cropped up over time in JSch. The git server supports fetching only, and sftp access. The tests are all in an abstract base class; the concrete JschSshTest class only provides ssh-specific test setup. So the same tests could be run easily also with some other ssh client. Bug: 520927 Change-Id: Ide6687b717fb497a29fc83f22b07390a26dfce1d Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
This commit is contained in:
parent
3ec3ca0f09
commit
08b0a8632d
12
WORKSPACE
12
WORKSPACE
|
@ -52,6 +52,18 @@ maven_jar(
|
||||||
sha1 = "a86ce739e5a7175b4b234c290a00a5fdb80957a0",
|
sha1 = "a86ce739e5a7175b4b234c290a00a5fdb80957a0",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
maven_jar(
|
||||||
|
name = "sshd-core",
|
||||||
|
artifact = "org.apache.sshd:sshd-core:2.0.0",
|
||||||
|
sha1 = "f4275079a2463cfd2bf1548a80e1683288a8e86b",
|
||||||
|
)
|
||||||
|
|
||||||
|
maven_jar(
|
||||||
|
name = "sshd-sftp",
|
||||||
|
artifact = "org.apache.sshd:sshd-sftp:2.0.0",
|
||||||
|
sha1 = "a12d64dc2d5d23271a4dc58075e55f9c64a68494",
|
||||||
|
)
|
||||||
|
|
||||||
maven_jar(
|
maven_jar(
|
||||||
name = "commons-codec",
|
name = "commons-codec",
|
||||||
artifact = "commons-codec:commons-codec:1.10",
|
artifact = "commons-codec:commons-codec:1.10",
|
||||||
|
|
18
lib/BUILD
18
lib/BUILD
|
@ -58,6 +58,24 @@ java_library(
|
||||||
exports = ["@httpcore//jar"],
|
exports = ["@httpcore//jar"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
java_library(
|
||||||
|
name = "sshd-core",
|
||||||
|
visibility = [
|
||||||
|
"//org.eclipse.jgit.junit:__pkg__",
|
||||||
|
"//org.eclipse.jgit.test:__pkg__",
|
||||||
|
],
|
||||||
|
exports = ["@sshd-core//jar"],
|
||||||
|
)
|
||||||
|
|
||||||
|
java_library(
|
||||||
|
name = "sshd-sftp",
|
||||||
|
visibility = [
|
||||||
|
"//org.eclipse.jgit.junit:__pkg__",
|
||||||
|
"//org.eclipse.jgit.test:__pkg__",
|
||||||
|
],
|
||||||
|
exports = ["@sshd-sftp//jar"],
|
||||||
|
)
|
||||||
|
|
||||||
java_library(
|
java_library(
|
||||||
name = "javaewah",
|
name = "javaewah",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
|
|
|
@ -8,6 +8,8 @@ java_library(
|
||||||
resources = glob(["resources/**"]),
|
resources = glob(["resources/**"]),
|
||||||
deps = [
|
deps = [
|
||||||
"//lib:junit",
|
"//lib:junit",
|
||||||
|
"//lib:sshd-core",
|
||||||
|
"//lib:sshd-sftp",
|
||||||
# We want these deps to be provided_deps
|
# We want these deps to be provided_deps
|
||||||
"//org.eclipse.jgit:jgit",
|
"//org.eclipse.jgit:jgit",
|
||||||
],
|
],
|
||||||
|
|
|
@ -8,7 +8,21 @@ Bundle-Localization: plugin
|
||||||
Bundle-Vendor: %provider_name
|
Bundle-Vendor: %provider_name
|
||||||
Bundle-ActivationPolicy: lazy
|
Bundle-ActivationPolicy: lazy
|
||||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||||
Import-Package: org.eclipse.jgit.api;version="[5.2.0,5.3.0)",
|
Import-Package: org.apache.sshd.common;version="[2.0.0,2.1.0)",
|
||||||
|
org.apache.sshd.common.config.keys;version="[2.0.0,2.1.0)",
|
||||||
|
org.apache.sshd.common.file.virtualfs;version="[2.0.0,2.1.0)",
|
||||||
|
org.apache.sshd.common.helpers;version="[2.0.0,2.1.0)",
|
||||||
|
org.apache.sshd.common.kex;version="[2.0.0,2.1.0)",
|
||||||
|
org.apache.sshd.common.keyprovider;version="[2.0.0,2.1.0)",
|
||||||
|
org.apache.sshd.common.session;version="[2.0.0,2.1.0)",
|
||||||
|
org.apache.sshd.common.util.logging;version="[2.0.0,2.1.0)",
|
||||||
|
org.apache.sshd.common.util.security;version="[2.0.0,2.1.0)",
|
||||||
|
org.apache.sshd.server;version="[2.0.0,2.1.0)",
|
||||||
|
org.apache.sshd.server.command;version="[2.0.0,2.1.0)",
|
||||||
|
org.apache.sshd.server.shell;version="[2.0.0,2.1.0)",
|
||||||
|
org.apache.sshd.server.subsystem.sftp;version="[2.0.0,2.1.0)",
|
||||||
|
org.eclipse.jgit.annotations;version="[5.2.0,5.3.0)",
|
||||||
|
org.eclipse.jgit.api;version="[5.2.0,5.3.0)",
|
||||||
org.eclipse.jgit.api.errors;version="[5.2.0,5.3.0)",
|
org.eclipse.jgit.api.errors;version="[5.2.0,5.3.0)",
|
||||||
org.eclipse.jgit.dircache;version="[5.2.0,5.3.0)",
|
org.eclipse.jgit.dircache;version="[5.2.0,5.3.0)",
|
||||||
org.eclipse.jgit.errors;version="[5.2.0,5.3.0)",
|
org.eclipse.jgit.errors;version="[5.2.0,5.3.0)",
|
||||||
|
@ -18,6 +32,7 @@ Import-Package: org.eclipse.jgit.api;version="[5.2.0,5.3.0)",
|
||||||
org.eclipse.jgit.merge;version="[5.2.0,5.3.0)",
|
org.eclipse.jgit.merge;version="[5.2.0,5.3.0)",
|
||||||
org.eclipse.jgit.revwalk;version="[5.2.0,5.3.0)",
|
org.eclipse.jgit.revwalk;version="[5.2.0,5.3.0)",
|
||||||
org.eclipse.jgit.storage.file;version="[5.2.0,5.3.0)",
|
org.eclipse.jgit.storage.file;version="[5.2.0,5.3.0)",
|
||||||
|
org.eclipse.jgit.transport;version="5.2.0",
|
||||||
org.eclipse.jgit.treewalk;version="[5.2.0,5.3.0)",
|
org.eclipse.jgit.treewalk;version="[5.2.0,5.3.0)",
|
||||||
org.eclipse.jgit.treewalk.filter;version="[5.2.0,5.3.0)",
|
org.eclipse.jgit.treewalk.filter;version="[5.2.0,5.3.0)",
|
||||||
org.eclipse.jgit.util;version="[5.2.0,5.3.0)",
|
org.eclipse.jgit.util;version="[5.2.0,5.3.0)",
|
||||||
|
@ -26,7 +41,8 @@ Import-Package: org.eclipse.jgit.api;version="[5.2.0,5.3.0)",
|
||||||
org.junit;version="[4.12,5.0.0)",
|
org.junit;version="[4.12,5.0.0)",
|
||||||
org.junit.rules;version="[4.12,5.0.0)",
|
org.junit.rules;version="[4.12,5.0.0)",
|
||||||
org.junit.runner;version="[4.12,5.0.0)",
|
org.junit.runner;version="[4.12,5.0.0)",
|
||||||
org.junit.runners.model;version="[4.12,5.0.0)"
|
org.junit.runners.model;version="[4.12,5.0.0)",
|
||||||
|
org.slf4j;version="[1.7.0,2.0.0)"
|
||||||
Export-Package: org.eclipse.jgit.junit;version="5.2.0";
|
Export-Package: org.eclipse.jgit.junit;version="5.2.0";
|
||||||
uses:="org.eclipse.jgit.dircache,
|
uses:="org.eclipse.jgit.dircache,
|
||||||
org.eclipse.jgit.lib,
|
org.eclipse.jgit.lib,
|
||||||
|
@ -35,5 +51,10 @@ Export-Package: org.eclipse.jgit.junit;version="5.2.0";
|
||||||
org.eclipse.jgit.treewalk,
|
org.eclipse.jgit.treewalk,
|
||||||
org.eclipse.jgit.util,
|
org.eclipse.jgit.util,
|
||||||
org.eclipse.jgit.storage.file,
|
org.eclipse.jgit.storage.file,
|
||||||
org.eclipse.jgit.api",
|
org.eclipse.jgit.api,
|
||||||
org.eclipse.jgit.junit.time;version="5.2.0"
|
org.junit.rules,
|
||||||
|
org.junit.runners.model,
|
||||||
|
org.junit.runner,
|
||||||
|
org.eclipse.jgit.util.time",
|
||||||
|
org.eclipse.jgit.junit.ssh;version="5.2.0",
|
||||||
|
org.eclipse.jgit.junit.time;version="5.2.0";uses:="org.eclipse.jgit.util.time"
|
||||||
|
|
|
@ -73,6 +73,18 @@
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.sshd</groupId>
|
||||||
|
<artifactId>sshd-core</artifactId>
|
||||||
|
<version>${apache-sshd-version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.sshd</groupId>
|
||||||
|
<artifactId>sshd-sftp</artifactId>
|
||||||
|
<version>${apache-sshd-version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
|
|
|
@ -0,0 +1,217 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
|
||||||
|
* and other copyright owners as documented in the project's IP log.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available
|
||||||
|
* under the terms of the Eclipse Distribution License v1.0 which
|
||||||
|
* accompanies this distribution, is reproduced below, and is
|
||||||
|
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or
|
||||||
|
* without modification, are permitted provided that the following
|
||||||
|
* conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of the Eclipse Foundation, Inc. nor the
|
||||||
|
* names of its contributors may be used to endorse or promote
|
||||||
|
* products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package org.eclipse.jgit.junit.ssh;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
import org.apache.sshd.common.config.keys.IdentityUtils;
|
||||||
|
import org.apache.sshd.common.config.keys.KeyUtils;
|
||||||
|
import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
|
||||||
|
import org.apache.sshd.common.keyprovider.KeyPairProvider;
|
||||||
|
import org.apache.sshd.common.session.Session;
|
||||||
|
import org.apache.sshd.common.util.security.SecurityUtils;
|
||||||
|
import org.apache.sshd.server.SshServer;
|
||||||
|
import org.apache.sshd.server.command.AbstractCommandSupport;
|
||||||
|
import org.apache.sshd.server.shell.UnknownCommand;
|
||||||
|
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
|
||||||
|
import org.eclipse.jgit.annotations.NonNull;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.eclipse.jgit.transport.UploadPack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple ssh/sftp git <em>test</em> server based on Apache MINA sshd.
|
||||||
|
* <p>
|
||||||
|
* Supports only a single repository. Authenticates only the given test user
|
||||||
|
* against his given test public key. ssh is limited to fetching (upload-pack).
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
public class SshTestGitServer {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private String testUser;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private PublicKey testKey;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private Repository repository;
|
||||||
|
|
||||||
|
private final ExecutorService executorService = Executors
|
||||||
|
.newFixedThreadPool(2);
|
||||||
|
|
||||||
|
private final SshServer server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a ssh git <em>test</em> server. It serves one single repository,
|
||||||
|
* and accepts public-key authentication for exactly one test user.
|
||||||
|
*
|
||||||
|
* @param testUser
|
||||||
|
* user name of the test user
|
||||||
|
* @param testKey
|
||||||
|
* <em>private</em> key file of the test user; the server will
|
||||||
|
* only user the public key from it
|
||||||
|
* @param repository
|
||||||
|
* to serve
|
||||||
|
* @param hostKey
|
||||||
|
* the unencrypted private key to use as host key
|
||||||
|
* @throws IOException
|
||||||
|
* @throws GeneralSecurityException
|
||||||
|
*/
|
||||||
|
public SshTestGitServer(@NonNull String testUser, @NonNull Path testKey,
|
||||||
|
@NonNull Repository repository, @NonNull byte[] hostKey)
|
||||||
|
throws IOException, GeneralSecurityException {
|
||||||
|
this.testUser = testUser;
|
||||||
|
this.testKey = IdentityUtils
|
||||||
|
.loadIdentities(Collections.singletonMap("A", testKey), null)
|
||||||
|
.get("A").getPublic();
|
||||||
|
this.repository = repository;
|
||||||
|
server = SshServer.setUpDefaultServer();
|
||||||
|
// Set host key
|
||||||
|
server.setKeyPairProvider(new KeyPairProvider() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<KeyPair> loadKeys() {
|
||||||
|
try (ByteArrayInputStream in = new ByteArrayInputStream(
|
||||||
|
hostKey)) {
|
||||||
|
return Collections.singletonList(
|
||||||
|
SecurityUtils.loadKeyPairIdentity("", in, null));
|
||||||
|
} catch (IOException | GeneralSecurityException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
// SFTP.
|
||||||
|
server.setFileSystemFactory(new VirtualFileSystemFactory() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Path computeRootDir(Session session) throws IOException {
|
||||||
|
return SshTestGitServer.this.repository.getDirectory()
|
||||||
|
.getParentFile().getAbsoluteFile().toPath();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
server.setSubsystemFactories(Collections
|
||||||
|
.singletonList((new SftpSubsystemFactory.Builder()).build()));
|
||||||
|
// No shell
|
||||||
|
server.setShellFactory(null);
|
||||||
|
// Disable some authentications
|
||||||
|
server.setPasswordAuthenticator(null);
|
||||||
|
server.setKeyboardInteractiveAuthenticator(null);
|
||||||
|
server.setGSSAuthenticator(null);
|
||||||
|
server.setHostBasedAuthenticator(null);
|
||||||
|
// Accept only the test user/public key
|
||||||
|
server.setPublickeyAuthenticator((userName, publicKey, session) -> {
|
||||||
|
return SshTestGitServer.this.testUser.equals(userName) && KeyUtils
|
||||||
|
.compareKeys(SshTestGitServer.this.testKey, publicKey);
|
||||||
|
});
|
||||||
|
server.setCommandFactory(command -> {
|
||||||
|
if (command.startsWith("git-upload-pack")
|
||||||
|
|| command.startsWith("git upload-pack")) {
|
||||||
|
return new GitUploadPackCommand(command, executorService);
|
||||||
|
}
|
||||||
|
return new UnknownCommand(command);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the test server, listening on a random port.
|
||||||
|
*
|
||||||
|
* @return the port the server listens on; test clients should connect to
|
||||||
|
* that port
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public int start() throws IOException {
|
||||||
|
server.start();
|
||||||
|
return server.getPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the test server.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void stop() throws IOException {
|
||||||
|
executorService.shutdownNow();
|
||||||
|
server.stop(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class GitUploadPackCommand extends AbstractCommandSupport {
|
||||||
|
|
||||||
|
protected GitUploadPackCommand(String command,
|
||||||
|
ExecutorService executorService) {
|
||||||
|
super(command, executorService, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
UploadPack uploadPack = new UploadPack(repository);
|
||||||
|
String gitProtocol = getEnvironment().getEnv().get("GIT_PROTOCOL");
|
||||||
|
if (gitProtocol != null) {
|
||||||
|
uploadPack
|
||||||
|
.setExtraParameters(Collections.singleton(gitProtocol));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
uploadPack.upload(getInputStream(), getOutputStream(),
|
||||||
|
getErrorStream());
|
||||||
|
onExit(0);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.warn(
|
||||||
|
MessageFormat.format("Could not run {0}", getCommand()),
|
||||||
|
e);
|
||||||
|
onExit(-1, e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ HELPERS = glob(["src/**/*.java"]) + [PKG + c for c in [
|
||||||
"revwalk/RevWalkTestCase.java",
|
"revwalk/RevWalkTestCase.java",
|
||||||
"transport/ObjectIdMatcher.java",
|
"transport/ObjectIdMatcher.java",
|
||||||
"transport/SpiTransport.java",
|
"transport/SpiTransport.java",
|
||||||
|
"transport/SshTestBase.java",
|
||||||
"treewalk/FileTreeIteratorWithTimeControl.java",
|
"treewalk/FileTreeIteratorWithTimeControl.java",
|
||||||
"treewalk/filter/AlwaysCloneTreeFilter.java",
|
"treewalk/filter/AlwaysCloneTreeFilter.java",
|
||||||
"test/resources/SampleDataRepositoryTestCase.java",
|
"test/resources/SampleDataRepositoryTestCase.java",
|
||||||
|
@ -44,6 +45,9 @@ java_library(
|
||||||
resources = DATA,
|
resources = DATA,
|
||||||
deps = [
|
deps = [
|
||||||
"//lib:junit",
|
"//lib:junit",
|
||||||
|
"//lib:jsch",
|
||||||
|
"//lib:sshd-core",
|
||||||
|
"//lib:sshd-sftp",
|
||||||
"//org.eclipse.jgit:jgit",
|
"//org.eclipse.jgit:jgit",
|
||||||
"//org.eclipse.jgit.junit:junit",
|
"//org.eclipse.jgit.junit:junit",
|
||||||
],
|
],
|
||||||
|
|
|
@ -10,6 +10,7 @@ Bundle-ActivationPolicy: lazy
|
||||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||||
Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
|
Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
|
||||||
com.jcraft.jsch;version="[0.1.54,0.2.0)",
|
com.jcraft.jsch;version="[0.1.54,0.2.0)",
|
||||||
|
org.eclipse.jgit.annotations;version="[5.2.0,5.3.0)",
|
||||||
org.eclipse.jgit.api;version="[5.2.0,5.3.0)",
|
org.eclipse.jgit.api;version="[5.2.0,5.3.0)",
|
||||||
org.eclipse.jgit.api.errors;version="[5.2.0,5.3.0)",
|
org.eclipse.jgit.api.errors;version="[5.2.0,5.3.0)",
|
||||||
org.eclipse.jgit.attributes;version="[5.2.0,5.3.0)",
|
org.eclipse.jgit.attributes;version="[5.2.0,5.3.0)",
|
||||||
|
@ -34,6 +35,7 @@ Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
|
||||||
org.eclipse.jgit.internal.storage.reftree;version="[5.2.0,5.3.0)",
|
org.eclipse.jgit.internal.storage.reftree;version="[5.2.0,5.3.0)",
|
||||||
org.eclipse.jgit.internal.transport.parser;version="[5.2.0,5.3.0)",
|
org.eclipse.jgit.internal.transport.parser;version="[5.2.0,5.3.0)",
|
||||||
org.eclipse.jgit.junit;version="[5.2.0,5.3.0)",
|
org.eclipse.jgit.junit;version="[5.2.0,5.3.0)",
|
||||||
|
org.eclipse.jgit.junit.ssh;version="[5.2.0,5.3.0)",
|
||||||
org.eclipse.jgit.lfs;version="[5.2.0,5.3.0)",
|
org.eclipse.jgit.lfs;version="[5.2.0,5.3.0)",
|
||||||
org.eclipse.jgit.lib;version="[5.2.0,5.3.0)",
|
org.eclipse.jgit.lib;version="[5.2.0,5.3.0)",
|
||||||
org.eclipse.jgit.merge;version="[5.2.0,5.3.0)",
|
org.eclipse.jgit.merge;version="[5.2.0,5.3.0)",
|
||||||
|
|
|
@ -112,6 +112,18 @@
|
||||||
<artifactId>org.eclipse.jgit.pgm</artifactId>
|
<artifactId>org.eclipse.jgit.pgm</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.sshd</groupId>
|
||||||
|
<artifactId>sshd-core</artifactId>
|
||||||
|
<version>${apache-sshd-version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.sshd</groupId>
|
||||||
|
<artifactId>sshd-sftp</artifactId>
|
||||||
|
<version>${apache-sshd-version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
|
|
|
@ -41,7 +41,13 @@ def tests(tests):
|
||||||
additional_deps = [
|
additional_deps = [
|
||||||
"//lib:jsch",
|
"//lib:jsch",
|
||||||
]
|
]
|
||||||
|
if src.endswith("JSchSshTest.java"):
|
||||||
|
additional_deps = [
|
||||||
|
"//lib:jsch",
|
||||||
|
"//lib:jzlib",
|
||||||
|
"//lib:sshd-core",
|
||||||
|
"//lib:sshd-sftp",
|
||||||
|
]
|
||||||
heap_size = "-Xmx256m"
|
heap_size = "-Xmx256m"
|
||||||
if src.endswith("HugeCommitMessageTest.java"):
|
if src.endswith("HugeCommitMessageTest.java"):
|
||||||
heap_size = "-Xmx512m"
|
heap_size = "-Xmx512m"
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
-----BEGIN DSA PRIVATE KEY-----
|
||||||
|
Proc-Type: 4,ENCRYPTED
|
||||||
|
DEK-Info: AES-128-CBC,D7B8FC3F4E304A2A22754B068767081F
|
||||||
|
|
||||||
|
IewkLt6JyqtPccAsnfeLv7IMlLvgm7tqQSYK1/CLhmDE0aZXViD8sqxLA6dVjmkp
|
||||||
|
BVyk7EBpp43PnVQYsDcMPnyM8H83vNRDtIQ6fxM1PJafiP7Rbn1k1fDh7DwA48PU
|
||||||
|
FnT6zZ9aYEKYMto0WIdQ86j/uY+LtYygQDDoZ2ohn2NlpykeyrSp0bDRIoW6sdc5
|
||||||
|
+LlfDtq2usv3fcxMAJpO/SSN78LvBlyOK4n/JAVSkPawsW1WsIrXA52mk0iUhjYc
|
||||||
|
aYOCuL+wA7OmHAOpfS5HUXZ4i/7qONnLBkEqeIOcgTmShh1c4oWw9TjWK1AzdSDU
|
||||||
|
G2nkRJ/8zK/jdm5wcmrjrzuREM1VbCiXHlVoHYI0W1Z9etOgz1cj4KLz/bB8Nf+8
|
||||||
|
shCez1Aw5ec33BzwysfwymfAKeXjYaxdKcur3j+UdXAlYRD28BRnWmTL5Jx82eUu
|
||||||
|
NIh0U9pHkn+PjdzmjSPEUP7wzDjQQacaQTkBRf5gPyOYfv/+Mnq6YyflKaPYmkEr
|
||||||
|
eztO22VZlpyp/hj2LzBav9wi0++teInNQGU+GxHedsWm4+YpffMhz1bz5ZUQ670A
|
||||||
|
0WJJH3k/KnxbCY3usj4eJr+CsX+LNZhm+rKyjRDmRwA=
|
||||||
|
-----END DSA PRIVATE KEY-----
|
|
@ -0,0 +1 @@
|
||||||
|
ssh-dss AAAAB3NzaC1kc3MAAACBAIsXi0EUiI6GmhHqrwwjvO2wdujW46+uXM/SG2GVI3KxCSf95B2XgXBsgiKH0sy3guyqjDcP4Ph5Mctg1IxqmqugN6xf9YB6lf09bRdIbumVGU6nXW7bZDHdk9nmvWy56vurofwvhoRnQBUJ3L4n7dxxvXhIyRPOxptayOS2ZcnRAAAAFQDsgGxVxcBBM9y0Rm3kNz/R64CYEQAAAIEAgCbyCJNZb66KQBMO7B+NPxx0caSKjZ+3TpWL6pLJGTAu1pztd1wpElECNCEBhTX9p1HEypTIjOUFU2gjgaBLUcWE0JK+/4vJjjvaENvrQardH0EeRfrazhpRY+X6ytUTk0YPDuQn+ZqBhXxAoD8BA+TJMvk7oMpMUTyr6LGBuj4AAACAeXCfOrKY6wHuMkHHpa9Ix95T+7h5ZrSosrV1WO5g9X04LNiPFRXvGyMWYF17VaGqVWID5NbbGP4PqwSw0rjmw7c/xxV2DYNfJ5NFWsDHxhI6RP9AaGTKcdIEykWEkGgJDiVF/DJgjvapGCW4Lo5UB1JJRXEM4YmTiEbyUyahKqw= thomas@Arcturus
|
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
|
||||||
|
* and other copyright owners as documented in the project's IP log.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available
|
||||||
|
* under the terms of the Eclipse Distribution License v1.0 which
|
||||||
|
* accompanies this distribution, is reproduced below, and is
|
||||||
|
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or
|
||||||
|
* without modification, are permitted provided that the following
|
||||||
|
* conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of the Eclipse Foundation, Inc. nor the
|
||||||
|
* names of its contributors may be used to endorse or promote
|
||||||
|
* products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package org.eclipse.jgit.transport;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.errors.TransportException;
|
||||||
|
import org.eclipse.jgit.lib.Constants;
|
||||||
|
import org.eclipse.jgit.transport.OpenSshConfig.Host;
|
||||||
|
import org.eclipse.jgit.util.FS;
|
||||||
|
|
||||||
|
import com.jcraft.jsch.JSch;
|
||||||
|
import com.jcraft.jsch.JSchException;
|
||||||
|
import com.jcraft.jsch.Session;
|
||||||
|
|
||||||
|
public class JSchSshTest extends SshTestBase {
|
||||||
|
|
||||||
|
private class TestSshSessionFactory extends JschConfigSessionFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(Host hc, Session session) {
|
||||||
|
// Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized RemoteSession getSession(URIish uri,
|
||||||
|
CredentialsProvider credentialsProvider, FS fs, int tms)
|
||||||
|
throws TransportException {
|
||||||
|
return super.getSession(uri, credentialsProvider, fs, tms);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JSch createDefaultJSch(FS fs) throws JSchException {
|
||||||
|
JSch defaultJSch = super.createDefaultJSch(fs);
|
||||||
|
if (knownHosts.exists()) {
|
||||||
|
defaultJSch.setKnownHosts(knownHosts.getAbsolutePath());
|
||||||
|
}
|
||||||
|
return defaultJSch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SshSessionFactory createSessionFactory() {
|
||||||
|
return new TestSshSessionFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installConfig(String... config) {
|
||||||
|
SshSessionFactory factory = getSessionFactory();
|
||||||
|
assertTrue(factory instanceof JschConfigSessionFactory);
|
||||||
|
JschConfigSessionFactory j = (JschConfigSessionFactory) factory;
|
||||||
|
try {
|
||||||
|
j.setConfig(createConfig(config));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OpenSshConfig createConfig(String... content) throws IOException {
|
||||||
|
File configFile = new File(sshDir, Constants.CONFIG);
|
||||||
|
if (content != null) {
|
||||||
|
Files.write(configFile.toPath(), Arrays.asList(content));
|
||||||
|
}
|
||||||
|
return new OpenSshConfig(getTemporaryDirectory(), configFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -67,6 +67,7 @@
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.jcraft.jsch.ConfigRepository;
|
import com.jcraft.jsch.ConfigRepository;
|
||||||
|
import com.jcraft.jsch.ConfigRepository.Config;
|
||||||
|
|
||||||
public class OpenSshConfigTest extends RepositoryTestCase {
|
public class OpenSshConfigTest extends RepositoryTestCase {
|
||||||
private File home;
|
private File home;
|
||||||
|
@ -163,6 +164,20 @@ public void testQuoteParsing() throws Exception {
|
||||||
assertEquals("bad.tld\"", osc.lookup("bad").getHostName());
|
assertEquals("bad.tld\"", osc.lookup("bad").getHostName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCaseInsensitiveKeyLookup() throws Exception {
|
||||||
|
config("Host orcz\n" + "Port 29418\n"
|
||||||
|
+ "\tHostName repo.or.cz\nStrictHostKeyChecking yes\n");
|
||||||
|
final Host h = osc.lookup("orcz");
|
||||||
|
Config c = h.getConfig();
|
||||||
|
String exactCase = c.getValue("StrictHostKeyChecking");
|
||||||
|
assertEquals("yes", exactCase);
|
||||||
|
assertEquals(exactCase, c.getValue("stricthostkeychecking"));
|
||||||
|
assertEquals(exactCase, c.getValue("STRICTHOSTKEYCHECKING"));
|
||||||
|
assertEquals(exactCase, c.getValue("sTrIcThostKEYcheckING"));
|
||||||
|
assertNull(c.getValue("sTrIcThostKEYcheckIN"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAlias_DoesNotMatch() throws Exception {
|
public void testAlias_DoesNotMatch() throws Exception {
|
||||||
config("Host orcz\n" + "Port 29418\n" + "\tHostName repo.or.cz\n");
|
config("Host orcz\n" + "Port 29418\n" + "\tHostName repo.or.cz\n");
|
||||||
|
|
|
@ -0,0 +1,466 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
|
||||||
|
* and other copyright owners as documented in the project's IP log.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available
|
||||||
|
* under the terms of the Eclipse Distribution License v1.0 which
|
||||||
|
* accompanies this distribution, is reproduced below, and is
|
||||||
|
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or
|
||||||
|
* without modification, are permitted provided that the following
|
||||||
|
* conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of the Eclipse Foundation, Inc. nor the
|
||||||
|
* names of its contributors may be used to endorse or promote
|
||||||
|
* products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package org.eclipse.jgit.transport;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.api.CloneCommand;
|
||||||
|
import org.eclipse.jgit.api.Git;
|
||||||
|
import org.eclipse.jgit.api.errors.TransportException;
|
||||||
|
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
|
||||||
|
import org.eclipse.jgit.junit.RepositoryTestCase;
|
||||||
|
import org.eclipse.jgit.junit.ssh.SshTestGitServer;
|
||||||
|
import org.eclipse.jgit.util.FS;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.jcraft.jsch.JSch;
|
||||||
|
import com.jcraft.jsch.KeyPair;
|
||||||
|
|
||||||
|
public abstract class SshTestBase extends RepositoryTestCase {
|
||||||
|
|
||||||
|
protected static final String TEST_USER = "testuser";
|
||||||
|
|
||||||
|
protected File sshDir;
|
||||||
|
|
||||||
|
protected File privateKey1;
|
||||||
|
|
||||||
|
protected File privateKey2;
|
||||||
|
|
||||||
|
private SshTestGitServer server;
|
||||||
|
|
||||||
|
private SshSessionFactory factory;
|
||||||
|
|
||||||
|
protected int testPort;
|
||||||
|
|
||||||
|
protected File knownHosts;
|
||||||
|
|
||||||
|
private File homeDir;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
writeTrashFile("file.txt", "something");
|
||||||
|
try (Git git = new Git(db)) {
|
||||||
|
git.add().addFilepattern("file.txt").call();
|
||||||
|
git.commit().setMessage("Initial commit").call();
|
||||||
|
}
|
||||||
|
mockSystemReader.setProperty("user.home",
|
||||||
|
getTemporaryDirectory().getAbsolutePath());
|
||||||
|
mockSystemReader.setProperty("HOME",
|
||||||
|
getTemporaryDirectory().getAbsolutePath());
|
||||||
|
homeDir = FS.DETECTED.userHome();
|
||||||
|
FS.DETECTED.setUserHome(getTemporaryDirectory().getAbsoluteFile());
|
||||||
|
sshDir = new File(getTemporaryDirectory(), ".ssh");
|
||||||
|
assertTrue(sshDir.mkdir());
|
||||||
|
File serverDir = new File(getTemporaryDirectory(), "srv");
|
||||||
|
assertTrue(serverDir.mkdir());
|
||||||
|
// Create two key pairs. Let's not call them "id_rsa".
|
||||||
|
privateKey1 = new File(sshDir, "first_key");
|
||||||
|
privateKey2 = new File(sshDir, "second_key");
|
||||||
|
createKeyPair(privateKey1);
|
||||||
|
createKeyPair(privateKey2);
|
||||||
|
ByteArrayOutputStream publicHostKey = new ByteArrayOutputStream();
|
||||||
|
// Start a server with our test user and the first key.
|
||||||
|
server = new SshTestGitServer(TEST_USER, privateKey1.toPath(), db,
|
||||||
|
createHostKey(publicHostKey));
|
||||||
|
testPort = server.start();
|
||||||
|
assertTrue(testPort > 0);
|
||||||
|
knownHosts = new File(sshDir, "known_hosts");
|
||||||
|
Files.write(knownHosts.toPath(), Collections.singleton("[localhost]:"
|
||||||
|
+ testPort + ' '
|
||||||
|
+ publicHostKey.toString(StandardCharsets.US_ASCII.name())));
|
||||||
|
factory = createSessionFactory();
|
||||||
|
SshSessionFactory.setInstance(factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void createKeyPair(File privateKeyFile) throws Exception {
|
||||||
|
// Found no way to do this with MINA sshd except rolling it all
|
||||||
|
// ourselves...
|
||||||
|
JSch jsch = new JSch();
|
||||||
|
KeyPair pair = KeyPair.genKeyPair(jsch, KeyPair.RSA, 2048);
|
||||||
|
try (OutputStream out = new FileOutputStream(privateKeyFile)) {
|
||||||
|
pair.writePrivateKey(out);
|
||||||
|
}
|
||||||
|
File publicKeyFile = new File(privateKeyFile.getParentFile(),
|
||||||
|
privateKeyFile.getName() + ".pub");
|
||||||
|
try (OutputStream out = new FileOutputStream(publicKeyFile)) {
|
||||||
|
pair.writePublicKey(out, TEST_USER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] createHostKey(OutputStream publicKey)
|
||||||
|
throws Exception {
|
||||||
|
JSch jsch = new JSch();
|
||||||
|
KeyPair pair = KeyPair.genKeyPair(jsch, KeyPair.RSA, 2048);
|
||||||
|
pair.writePublicKey(publicKey, "");
|
||||||
|
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||||
|
pair.writePrivateKey(out);
|
||||||
|
out.flush();
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void shutdownServer() throws Exception {
|
||||||
|
if (server != null) {
|
||||||
|
server.stop();
|
||||||
|
server = null;
|
||||||
|
}
|
||||||
|
FS.DETECTED.setUserHome(homeDir);
|
||||||
|
SshSessionFactory.setInstance(null);
|
||||||
|
factory = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract SshSessionFactory createSessionFactory();
|
||||||
|
|
||||||
|
protected SshSessionFactory getSessionFactory() {
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void installConfig(String... config);
|
||||||
|
|
||||||
|
@Test(expected = TransportException.class)
|
||||||
|
public void testSshCloneWithoutConfig() throws Exception {
|
||||||
|
cloneWith("ssh://" + TEST_USER + "@localhost:" + testPort
|
||||||
|
+ "/doesntmatter", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSshCloneWithGlobalIdentity() throws Exception {
|
||||||
|
cloneWith(
|
||||||
|
"ssh://" + TEST_USER + "@localhost:" + testPort
|
||||||
|
+ "/doesntmatter",
|
||||||
|
null,
|
||||||
|
"IdentityFile " + privateKey1.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSshCloneWithDefaultIdentity() throws Exception {
|
||||||
|
File idRsa = new File(privateKey1.getParentFile(), "id_rsa");
|
||||||
|
Files.copy(privateKey1.toPath(), idRsa.toPath());
|
||||||
|
// We expect the session factory to pick up these keys...
|
||||||
|
cloneWith("ssh://" + TEST_USER + "@localhost:" + testPort
|
||||||
|
+ "/doesntmatter", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSshCloneWithConfig() throws Exception {
|
||||||
|
cloneWith("ssh://localhost/doesntmatter", null, //
|
||||||
|
"Host localhost", //
|
||||||
|
"HostName localhost", //
|
||||||
|
"Port " + testPort, //
|
||||||
|
"User " + TEST_USER, //
|
||||||
|
"IdentityFile " + privateKey1.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSshCloneWithConfigEncryptedUnusedKey() throws Exception {
|
||||||
|
// Copy the encrypted test key from the bundle.
|
||||||
|
File encryptedKey = new File(sshDir, "id_dsa");
|
||||||
|
try (InputStream in = SshTestBase.class
|
||||||
|
.getResourceAsStream("id_dsa_test")) {
|
||||||
|
Files.copy(in, encryptedKey.toPath());
|
||||||
|
}
|
||||||
|
TestCredentialsProvider provider = new TestCredentialsProvider(
|
||||||
|
"testpass");
|
||||||
|
cloneWith("ssh://localhost/doesntmatter", provider, //
|
||||||
|
"Host localhost", //
|
||||||
|
"HostName localhost", //
|
||||||
|
"Port " + testPort, //
|
||||||
|
"User " + TEST_USER, //
|
||||||
|
"IdentityFile " + privateKey1.getAbsolutePath());
|
||||||
|
assertEquals("CredentialsProvider should not have been called", 0,
|
||||||
|
provider.getLog().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = TransportException.class)
|
||||||
|
public void testSshCloneWithoutKnownHosts() throws Exception {
|
||||||
|
assertTrue("Could not delete known_hosts", knownHosts.delete());
|
||||||
|
cloneWith("ssh://localhost/doesntmatter", null, //
|
||||||
|
"Host localhost", //
|
||||||
|
"HostName localhost", //
|
||||||
|
"Port " + testPort, //
|
||||||
|
"User " + TEST_USER, //
|
||||||
|
"IdentityFile " + privateKey1.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSshCloneWithoutKnownHostsWithProvider() throws Exception {
|
||||||
|
File copiedHosts = new File(knownHosts.getParentFile(),
|
||||||
|
"copiedKnownHosts");
|
||||||
|
assertTrue("Failed to rename known_hosts",
|
||||||
|
knownHosts.renameTo(copiedHosts));
|
||||||
|
TestCredentialsProvider provider = new TestCredentialsProvider();
|
||||||
|
cloneWith("ssh://localhost/doesntmatter", provider, //
|
||||||
|
"Host localhost", //
|
||||||
|
"HostName localhost", //
|
||||||
|
"Port " + testPort, //
|
||||||
|
"User " + TEST_USER, //
|
||||||
|
"IdentityFile " + privateKey1.getAbsolutePath());
|
||||||
|
Map<URIish, List<CredentialItem>> messages = provider.getLog();
|
||||||
|
assertFalse("Expected user iteraction", messages.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSftpCloneWithConfig() throws Exception {
|
||||||
|
cloneWith("sftp://localhost/.git", null, //
|
||||||
|
"Host localhost", //
|
||||||
|
"HostName localhost", //
|
||||||
|
"Port " + testPort, //
|
||||||
|
"User " + TEST_USER, //
|
||||||
|
"IdentityFile " + privateKey1.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = TransportException.class)
|
||||||
|
public void testSshCloneWithConfigWrongKey() throws Exception {
|
||||||
|
cloneWith("ssh://localhost/doesntmatter", null, //
|
||||||
|
"Host localhost", //
|
||||||
|
"HostName localhost", //
|
||||||
|
"Port " + testPort, //
|
||||||
|
"User " + TEST_USER, //
|
||||||
|
"IdentityFile " + privateKey2.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSshCloneWithWrongUserNameInConfig() throws Exception {
|
||||||
|
// Bug 526778
|
||||||
|
cloneWith(
|
||||||
|
"ssh://" + TEST_USER + "@localhost:" + testPort
|
||||||
|
+ "/doesntmatter",
|
||||||
|
null, //
|
||||||
|
"Host localhost", //
|
||||||
|
"HostName localhost", //
|
||||||
|
"User sombody_else", //
|
||||||
|
"IdentityFile " + privateKey1.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSshCloneWithWrongPortInConfig() throws Exception {
|
||||||
|
// Bug 526778
|
||||||
|
cloneWith(
|
||||||
|
"ssh://" + TEST_USER + "@localhost:" + testPort
|
||||||
|
+ "/doesntmatter",
|
||||||
|
null, //
|
||||||
|
"Host localhost", //
|
||||||
|
"HostName localhost", //
|
||||||
|
"Port 22", //
|
||||||
|
"User " + TEST_USER, //
|
||||||
|
"IdentityFile " + privateKey1.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSshCloneWithAliasInConfig() throws Exception {
|
||||||
|
// Bug 531118
|
||||||
|
cloneWith("ssh://git/doesntmatter", null, //
|
||||||
|
"Host git", //
|
||||||
|
"HostName localhost", //
|
||||||
|
"Port " + testPort, //
|
||||||
|
"User " + TEST_USER, //
|
||||||
|
"IdentityFile " + privateKey1.getAbsolutePath(), "", //
|
||||||
|
"Host localhost", //
|
||||||
|
"HostName localhost", //
|
||||||
|
"Port 22", //
|
||||||
|
"User someone_else", //
|
||||||
|
"IdentityFile " + privateKey2.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSshCloneWithUnknownCiphersInConfig() throws Exception {
|
||||||
|
// Bug 535672
|
||||||
|
cloneWith("ssh://git/doesntmatter", null, //
|
||||||
|
"Host git", //
|
||||||
|
"HostName localhost", //
|
||||||
|
"Port " + testPort, //
|
||||||
|
"User " + TEST_USER, //
|
||||||
|
"IdentityFile " + privateKey1.getAbsolutePath(), //
|
||||||
|
"Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSshCloneWithUnknownHostKeyAlgorithmsInConfig()
|
||||||
|
throws Exception {
|
||||||
|
// Bug 535672
|
||||||
|
cloneWith("ssh://git/doesntmatter", null, //
|
||||||
|
"Host git", //
|
||||||
|
"HostName localhost", //
|
||||||
|
"Port " + testPort, //
|
||||||
|
"User " + TEST_USER, //
|
||||||
|
"IdentityFile " + privateKey1.getAbsolutePath(), //
|
||||||
|
"HostKeyAlgorithms foobar,ssh-rsa,ssh-dss");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSshCloneWithUnknownKexAlgorithmsInConfig()
|
||||||
|
throws Exception {
|
||||||
|
// Bug 535672
|
||||||
|
cloneWith("ssh://git/doesntmatter", null, //
|
||||||
|
"Host git", //
|
||||||
|
"HostName localhost", //
|
||||||
|
"Port " + testPort, //
|
||||||
|
"User " + TEST_USER, //
|
||||||
|
"IdentityFile " + privateKey1.getAbsolutePath(), //
|
||||||
|
"KexAlgorithms foobar,diffie-hellman-group14-sha1,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSshCloneWithMinimalHostKeyAlgorithmsInConfig()
|
||||||
|
throws Exception {
|
||||||
|
// Bug 537790
|
||||||
|
cloneWith("ssh://git/doesntmatter", null, //
|
||||||
|
"Host git", //
|
||||||
|
"HostName localhost", //
|
||||||
|
"Port " + testPort, //
|
||||||
|
"User " + TEST_USER, //
|
||||||
|
"IdentityFile " + privateKey1.getAbsolutePath(), //
|
||||||
|
"HostKeyAlgorithms ssh-rsa,ssh-dss");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cloneWith(String uri, CredentialsProvider provider,
|
||||||
|
String... config) throws Exception {
|
||||||
|
installConfig(config);
|
||||||
|
File cloned = new File(getTemporaryDirectory(), "cloned");
|
||||||
|
CloneCommand clone = Git.cloneRepository().setCloneAllBranches(true)
|
||||||
|
.setDirectory(cloned).setURI(uri);
|
||||||
|
if (provider != null) {
|
||||||
|
clone.setCredentialsProvider(provider);
|
||||||
|
}
|
||||||
|
try (Git git = clone.call()) {
|
||||||
|
assertNotNull(git.getRepository().resolve("master"));
|
||||||
|
assertNotEquals(db.getWorkTree(),
|
||||||
|
git.getRepository().getWorkTree());
|
||||||
|
checkFile(new File(git.getRepository().getWorkTree(), "file.txt"),
|
||||||
|
"something");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestCredentialsProvider extends CredentialsProvider {
|
||||||
|
|
||||||
|
private final List<String> stringStore;
|
||||||
|
|
||||||
|
private final Iterator<String> strings;
|
||||||
|
|
||||||
|
public TestCredentialsProvider(String... strings) {
|
||||||
|
if (strings == null || strings.length == 0) {
|
||||||
|
stringStore = Collections.emptyList();
|
||||||
|
} else {
|
||||||
|
stringStore = Arrays.asList(strings);
|
||||||
|
}
|
||||||
|
this.strings = stringStore.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInteractive() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(CredentialItem... items) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean get(URIish uri, CredentialItem... items)
|
||||||
|
throws UnsupportedCredentialItem {
|
||||||
|
System.out.println("URI: " + uri);
|
||||||
|
for (CredentialItem item : items) {
|
||||||
|
System.out.println(item.getClass().getSimpleName() + ' '
|
||||||
|
+ item.getPromptText());
|
||||||
|
}
|
||||||
|
logItems(uri, items);
|
||||||
|
for (CredentialItem item : items) {
|
||||||
|
if (item instanceof CredentialItem.InformationalMessage) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (item instanceof CredentialItem.YesNoType) {
|
||||||
|
((CredentialItem.YesNoType) item).setValue(true);
|
||||||
|
} else if (item instanceof CredentialItem.CharArrayType) {
|
||||||
|
if (strings.hasNext()) {
|
||||||
|
((CredentialItem.CharArrayType) item)
|
||||||
|
.setValue(strings.next().toCharArray());
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (item instanceof CredentialItem.StringType) {
|
||||||
|
if (strings.hasNext()) {
|
||||||
|
((CredentialItem.StringType) item)
|
||||||
|
.setValue(strings.next());
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<URIish, List<CredentialItem>> log = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
private void logItems(URIish uri, CredentialItem... items) {
|
||||||
|
log.put(uri, Arrays.asList(items));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<URIish, List<CredentialItem>> getLog() {
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
pom.xml
1
pom.xml
|
@ -198,6 +198,7 @@
|
||||||
<bundle-manifest>${project.build.directory}/META-INF/MANIFEST.MF</bundle-manifest>
|
<bundle-manifest>${project.build.directory}/META-INF/MANIFEST.MF</bundle-manifest>
|
||||||
|
|
||||||
<jgit-last-release-version>4.11.0.201803080745-r</jgit-last-release-version>
|
<jgit-last-release-version>4.11.0.201803080745-r</jgit-last-release-version>
|
||||||
|
<apache-sshd-version>2.0.0</apache-sshd-version>
|
||||||
<jsch-version>0.1.54</jsch-version>
|
<jsch-version>0.1.54</jsch-version>
|
||||||
<jzlib-version>1.1.1</jzlib-version>
|
<jzlib-version>1.1.1</jzlib-version>
|
||||||
<javaewah-version>1.1.6</javaewah-version>
|
<javaewah-version>1.1.6</javaewah-version>
|
||||||
|
|
Loading…
Reference in New Issue