Merge "Inline image diff version switcher button styles"
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 8793458..ed4cf5a 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -4837,6 +4837,16 @@
replicate = replication start
----
+[[ssh]]
+=== Section ssh
+
+[[ssh.clientImplementation]]ssh.clientImplementation::
++
+JCraft JSch client is supported in addition to Apache MINA SSH client.
+To use JSch client set the value to `JSCH`.
++
+By default, `APACHE`.
+
[[sshd]]
=== Section sshd
diff --git a/Documentation/dev-bazel.txt b/Documentation/dev-bazel.txt
index adc9be5..747f761 100644
--- a/Documentation/dev-bazel.txt
+++ b/Documentation/dev-bazel.txt
@@ -318,6 +318,18 @@
bazel test //javatests/com/google/gerrit/acceptance/rest/account:rest_account
----
+To run SSH tests using JSch ssh client:
+
+----
+ bazel test --test_env=SSH_CLIENT_IMPLEMENTATION=JSCH //...
+----
+
+To run SSH tests using Apache MINA ssh client:
+
+----
+ bazel test --test_env=SSH_CLIENT_IMPLEMENTATION=APACHE //...
+----
+
To run only tests that do not use SSH:
----
diff --git a/Documentation/licenses.txt b/Documentation/licenses.txt
index 737f2a4..63601d2 100644
--- a/Documentation/licenses.txt
+++ b/Documentation/licenses.txt
@@ -84,6 +84,7 @@
* mime4j:dom
* mina:core
* mina:sshd
+* mina:sshd-sftp
* openid:consumer
* openid:nekohtml
* openid:xerces
@@ -2348,6 +2349,7 @@
* jgit
* jgit-archive
* jgit-servlet
+* jgit-ssh-apache
[[jgit_license]]
----
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 722d9f3..b05050d 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -48,6 +48,7 @@
import com.google.gerrit.acceptance.testsuite.account.TestSshKeys;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.acceptance.testsuite.request.SshSessionFactory;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.AccessSection;
import com.google.gerrit.entities.Account;
@@ -144,7 +145,6 @@
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Provider;
-import com.jcraft.jsch.KeyPair;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
@@ -156,6 +156,7 @@
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.security.KeyPair;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.ArrayList;
@@ -563,7 +564,7 @@
&& (adminSshSession == null || userSshSession == null)) {
// Create Ssh sessions
KeyPair adminKeyPair = sshKeys.getKeyPair(admin);
- GitUtil.initSsh(adminKeyPair);
+ SshSessionFactory.initSsh(adminKeyPair);
Context ctx = newRequestContext(user);
atrScope.set(ctx);
userSshSession = ctx.getSession();
diff --git a/java/com/google/gerrit/acceptance/BUILD b/java/com/google/gerrit/acceptance/BUILD
index 28f67b8..5ee1a08 100644
--- a/java/com/google/gerrit/acceptance/BUILD
+++ b/java/com/google/gerrit/acceptance/BUILD
@@ -40,6 +40,7 @@
"//lib:guava-retrying",
"//lib:jgit",
"//lib:jgit-ssh-jsch",
+ "//lib:jgit-ssh-apache",
"//lib:jsch",
"//lib/commons:compress",
"//lib/commons:lang",
@@ -52,6 +53,7 @@
"//lib/mina:sshd",
"//lib:guava",
"//lib/bouncycastle:bcpg",
+ "//lib/bouncycastle:bcpkix",
"//lib/bouncycastle:bcprov",
"//prolog:gerrit-prolog-common",
]
diff --git a/java/com/google/gerrit/acceptance/GitUtil.java b/java/com/google/gerrit/acceptance/GitUtil.java
index ae72793..94d329d 100644
--- a/java/com/google/gerrit/acceptance/GitUtil.java
+++ b/java/com/google/gerrit/acceptance/GitUtil.java
@@ -20,17 +20,11 @@
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.primitives.Ints;
-import com.google.gerrit.acceptance.testsuite.account.TestSshKeys;
import com.google.gerrit.common.FooterConstants;
import com.google.gerrit.entities.Project;
-import com.jcraft.jsch.JSch;
-import com.jcraft.jsch.JSchException;
-import com.jcraft.jsch.KeyPair;
-import com.jcraft.jsch.Session;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
-import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jgit.api.FetchCommand;
@@ -47,41 +41,15 @@
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.FetchResult;
-import org.eclipse.jgit.transport.JschConfigSessionFactory;
-import org.eclipse.jgit.transport.OpenSshConfig.Host;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteRefUpdate;
-import org.eclipse.jgit.transport.SshSessionFactory;
import org.eclipse.jgit.util.FS;
public class GitUtil {
private static final AtomicInteger testRepoCount = new AtomicInteger();
private static final int TEST_REPO_WINDOW_DAYS = 2;
- public static void initSsh(KeyPair keyPair) {
- final Properties config = new Properties();
- config.put("StrictHostKeyChecking", "no");
- JSch.setConfig(config);
-
- // register a JschConfigSessionFactory that adds the private key as identity
- // to the JSch instance of JGit so that SSH communication via JGit can
- // succeed
- SshSessionFactory.setInstance(
- new JschConfigSessionFactory() {
- @Override
- protected void configure(Host hc, Session session) {
- try {
- final JSch jsch = getJSch(hc, FS.DETECTED);
- jsch.addIdentity(
- "KeyPair", TestSshKeys.privateKey(keyPair), keyPair.getPublicKeyBlob(), null);
- } catch (JSchException e) {
- throw new RuntimeException(e);
- }
- }
- });
- }
-
/**
* Create a new {@link TestRepository} with a distinct commit clock.
*
diff --git a/java/com/google/gerrit/acceptance/SshSession.java b/java/com/google/gerrit/acceptance/SshSession.java
index 6698657..054e523 100644
--- a/java/com/google/gerrit/acceptance/SshSession.java
+++ b/java/com/google/gerrit/acceptance/SshSession.java
@@ -14,27 +14,18 @@
package com.google.gerrit.acceptance;
-import static com.google.common.base.Preconditions.checkState;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
-import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.gerrit.acceptance.testsuite.account.TestAccount;
import com.google.gerrit.acceptance.testsuite.account.TestSshKeys;
-import com.jcraft.jsch.ChannelExec;
-import com.jcraft.jsch.JSch;
-import com.jcraft.jsch.KeyPair;
-import com.jcraft.jsch.Session;
-import java.io.InputStream;
import java.net.InetSocketAddress;
-import java.util.Scanner;
-public class SshSession {
- private final TestSshKeys sshKeys;
- private final InetSocketAddress addr;
- private final TestAccount account;
- private Session session;
- private String error;
+public abstract class SshSession {
+ protected final TestSshKeys sshKeys;
+ protected final InetSocketAddress addr;
+ protected final TestAccount account;
+ protected String error;
public SshSession(TestSshKeys sshKeys, InetSocketAddress addr, TestAccount account) {
this.sshKeys = sshKeys;
@@ -42,44 +33,13 @@
this.account = account;
}
- public void open() throws Exception {
- getSession();
- }
+ public abstract void open() throws Exception;
- @SuppressWarnings("resource")
- public String exec(String command) throws Exception {
- ChannelExec channel = (ChannelExec) getSession().openChannel("exec");
- try {
- channel.setCommand(command);
- InputStream in = channel.getInputStream();
- InputStream err = channel.getErrStream();
- channel.connect();
+ public abstract void close();
- Scanner s = new Scanner(err, UTF_8.name()).useDelimiter("\\A");
- error = s.hasNext() ? s.next() : null;
+ public abstract String exec(String command) throws Exception;
- s = new Scanner(in, UTF_8.name()).useDelimiter("\\A");
- return s.hasNext() ? s.next() : "";
- } finally {
- channel.disconnect();
- }
- }
-
- @SuppressWarnings("resource")
- public int execAndReturnStatus(String command) throws Exception {
- ChannelExec channel = (ChannelExec) getSession().openChannel("exec");
- try {
- channel.setCommand(command);
- InputStream err = channel.getErrStream();
- channel.connect();
-
- Scanner s = new Scanner(err, UTF_8.name()).useDelimiter("\\A");
- error = s.hasNext() ? s.next() : null;
- return channel.getExitStatus();
- } finally {
- channel.disconnect();
- }
- }
+ public abstract int execAndReturnStatus(String command) throws Exception;
private boolean hasError() {
return error != null;
@@ -102,46 +62,23 @@
assertThat(getError()).contains(error);
}
- public void close() {
- if (session != null) {
- session.disconnect();
- session = null;
- }
- }
-
- private Session getSession() throws Exception {
- if (session == null) {
- KeyPair keyPair = sshKeys.getKeyPair(account);
- JSch jsch = new JSch();
- jsch.addIdentity(
- "KeyPair", TestSshKeys.privateKey(keyPair), keyPair.getPublicKeyBlob(), null);
- String username =
- account
- .username()
- .orElseThrow(
- () ->
- new IllegalStateException(
- "account " + account.accountId() + " must have a username to use SSH"));
- session = jsch.getSession(username, addr.getAddress().getHostAddress(), addr.getPort());
- session.setConfig("StrictHostKeyChecking", "no");
- session.connect();
- }
- return session;
- }
-
public String getUrl() {
- checkState(session != null, "session must be opened");
StringBuilder b = new StringBuilder();
b.append("ssh://");
- b.append(session.getUserName());
+ b.append(account.username().get());
b.append("@");
- b.append(session.getHost());
+ b.append(addr.getAddress().getHostAddress());
b.append(":");
- b.append(session.getPort());
+ b.append(addr.getPort());
return b.toString();
}
- public TestAccount getAccount() {
- return account;
+ protected String getUsername() {
+ return account
+ .username()
+ .orElseThrow(
+ () ->
+ new IllegalStateException(
+ "account " + account.accountId() + " must have a username to use SSH"));
}
}
diff --git a/java/com/google/gerrit/acceptance/SshSessionJsch.java b/java/com/google/gerrit/acceptance/SshSessionJsch.java
new file mode 100644
index 0000000..86cc438
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/SshSessionJsch.java
@@ -0,0 +1,156 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed 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.
+
+package com.google.gerrit.acceptance;
+
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.gerrit.acceptance.testsuite.account.TestAccount;
+import com.google.gerrit.acceptance.testsuite.account.TestSshKeys;
+import com.jcraft.jsch.ChannelExec;
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.net.InetSocketAddress;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Properties;
+import java.util.Scanner;
+import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
+import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator;
+import org.bouncycastle.util.io.pem.PemObject;
+import org.eclipse.jgit.transport.JschConfigSessionFactory;
+import org.eclipse.jgit.transport.OpenSshConfig.Host;
+import org.eclipse.jgit.transport.SshSessionFactory;
+import org.eclipse.jgit.util.FS;
+
+public class SshSessionJsch extends SshSession {
+
+ private Session session;
+
+ public static void initClient(KeyPair keyPair) {
+ Properties config = new Properties();
+ config.put("StrictHostKeyChecking", "no");
+ JSch.setConfig(config);
+
+ // register a JschConfigSessionFactory that adds the private key as identity
+ // to the JSch instance of JGit so that SSH communication via JGit can
+ // succeed
+ SshSessionFactory.setInstance(
+ new JschConfigSessionFactory() {
+ @Override
+ protected void configure(Host hc, Session session) {
+ try {
+ JSch jsch = getJSch(hc, FS.DETECTED);
+ jsch.addIdentity(
+ "KeyPair", privateKey(keyPair), TestSshKeys.publicKeyBlob(keyPair), null);
+ } catch (JSchException | GeneralSecurityException | IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ }
+
+ public static KeyPairGenerator initKeyPairGenerator() throws NoSuchAlgorithmException {
+ KeyPairGenerator gen;
+ gen = KeyPairGenerator.getInstance("RSA");
+ gen.initialize(512, new SecureRandom());
+ return gen;
+ }
+
+ public SshSessionJsch(TestSshKeys sshKeys, InetSocketAddress addr, TestAccount account) {
+ super(sshKeys, addr, account);
+ }
+
+ @Override
+ public void open() throws Exception {
+ getJschSession();
+ }
+
+ @Override
+ public void close() {
+ if (session != null) {
+ session.disconnect();
+ session = null;
+ }
+ }
+
+ @SuppressWarnings("resource")
+ @Override
+ public String exec(String command) throws Exception {
+ ChannelExec channel = (ChannelExec) getJschSession().openChannel("exec");
+ try {
+ channel.setCommand(command);
+ InputStream in = channel.getInputStream();
+ InputStream err = channel.getErrStream();
+ channel.connect();
+
+ Scanner s = new Scanner(err, UTF_8.name()).useDelimiter("\\A");
+ error = s.hasNext() ? s.next() : null;
+
+ s = new Scanner(in, UTF_8.name()).useDelimiter("\\A");
+ return s.hasNext() ? s.next() : "";
+ } finally {
+ channel.disconnect();
+ }
+ }
+
+ @SuppressWarnings("resource")
+ @Override
+ public int execAndReturnStatus(String command) throws Exception {
+ ChannelExec channel = (ChannelExec) getJschSession().openChannel("exec");
+ try {
+ channel.setCommand(command);
+ InputStream err = channel.getErrStream();
+ channel.connect();
+
+ Scanner s = new Scanner(err, UTF_8.name()).useDelimiter("\\A");
+ error = s.hasNext() ? s.next() : null;
+ return channel.getExitStatus();
+ } finally {
+ channel.disconnect();
+ }
+ }
+
+ private Session getJschSession() throws Exception {
+ if (session == null) {
+ KeyPair keyPair = sshKeys.getKeyPair(account);
+ JSch jsch = new JSch();
+ jsch.addIdentity("KeyPair", privateKey(keyPair), TestSshKeys.publicKeyBlob(keyPair), null);
+ String username = getUsername();
+ session = jsch.getSession(username, addr.getAddress().getHostAddress(), addr.getPort());
+ session.setConfig("StrictHostKeyChecking", "no");
+ session.connect();
+ }
+ return session;
+ }
+
+ private static byte[] privateKey(KeyPair keyPair) throws IOException {
+ // unencrypted form of PKCS#8 file
+ JcaPKCS8Generator gen1 = new JcaPKCS8Generator(keyPair.getPrivate(), null);
+ PemObject obj1 = gen1.generate();
+ StringWriter sw1 = new StringWriter();
+ try (JcaPEMWriter pw = new JcaPEMWriter(sw1)) {
+ pw.writeObject(obj1);
+ }
+ return sw1.toString().getBytes(US_ASCII.name());
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/SshSessionMina.java b/java/com/google/gerrit/acceptance/SshSessionMina.java
new file mode 100644
index 0000000..4514f44
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/SshSessionMina.java
@@ -0,0 +1,170 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed 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.
+
+package com.google.gerrit.acceptance;
+
+import static com.google.common.io.RecursiveDeleteOption.ALLOW_INSECURE;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.io.CharSink;
+import com.google.common.io.Files;
+import com.google.common.io.MoreFiles;
+import com.google.gerrit.acceptance.testsuite.account.TestAccount;
+import com.google.gerrit.acceptance.testsuite.account.TestSshKeys;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPairGenerator;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+import java.util.Scanner;
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.writer.openssh.OpenSSHKeyPairResourceWriter;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.eclipse.jgit.transport.SshSessionFactory;
+import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.transport.sshd.DefaultProxyDataFactory;
+import org.eclipse.jgit.transport.sshd.JGitKeyCache;
+import org.eclipse.jgit.transport.sshd.SshdSession;
+import org.eclipse.jgit.transport.sshd.SshdSessionFactory;
+import org.eclipse.jgit.util.FS;
+
+public class SshSessionMina extends SshSession {
+ private static final int TIMEOUT = 100000;
+
+ private SshdSession session;
+
+ public static void initClient() {
+ JGitKeyCache keyCache = new JGitKeyCache();
+ SshdSessionFactory factory = new SshdSessionFactory(keyCache, new DefaultProxyDataFactory());
+ SshSessionFactory.setInstance(factory);
+ }
+
+ public static KeyPairGenerator initKeyPairGenerator()
+ throws GeneralSecurityException, InvalidKeySpecException, InvalidAlgorithmParameterException {
+ int size = 256;
+ KeyPairGenerator gen = SecurityUtils.getKeyPairGenerator(KeyUtils.EC_ALGORITHM);
+ ECCurves curve = ECCurves.fromCurveSize(size);
+ if (curve == null) {
+ throw new InvalidKeySpecException("Unknown curve for key size=" + size);
+ }
+ gen.initialize(curve.getParameters());
+ return gen;
+ }
+
+ public SshSessionMina(TestSshKeys sshKeys, InetSocketAddress addr, TestAccount account) {
+ super(sshKeys, addr, account);
+ }
+
+ @Override
+ public void open() throws Exception {
+ getMinaSession();
+ }
+
+ @Override
+ public void close() {
+ if (session != null) {
+ session.disconnect();
+ session = null;
+ }
+ }
+
+ @SuppressWarnings("resource")
+ @Override
+ public String exec(String command) throws Exception {
+ Process process = getMinaSession().exec(command, TIMEOUT);
+ InputStream in = process.getInputStream();
+ InputStream err = process.getErrorStream();
+
+ Scanner s = new Scanner(err, UTF_8.name()).useDelimiter("\\A");
+ error = s.hasNext() ? s.next() : null;
+
+ s = new Scanner(in, UTF_8.name()).useDelimiter("\\A");
+ return s.hasNext() ? s.next() : "";
+ }
+
+ @SuppressWarnings("resource")
+ @Override
+ public int execAndReturnStatus(String command) throws Exception {
+ Process process = getMinaSession().exec(command, 0);
+ InputStream in = process.getInputStream();
+ InputStream err = process.getErrorStream();
+
+ Scanner s = new Scanner(err, UTF_8.name()).useDelimiter("\\A");
+ error = s.hasNext() ? s.next() : null;
+
+ s = new Scanner(in, UTF_8.name()).useDelimiter("\\A");
+ try {
+ return process.exitValue();
+ } catch (IllegalThreadStateException e) {
+ // SSH command was interrupted
+ return -1;
+ }
+ }
+
+ private SshdSession getMinaSession() throws Exception {
+ if (session == null) {
+ String username = getUsername();
+
+ URIish uri =
+ new URIish(
+ "ssh://"
+ + username
+ + "@"
+ + addr.getAddress().getHostAddress()
+ + ":"
+ + addr.getPort());
+
+ // TODO(davido): Switch to memory only key resolving mode.
+ File userhome = Files.createTempDir();
+
+ FS fs = FS.DETECTED.setUserHome(userhome);
+ File sshDir = new File(userhome, ".ssh");
+ sshDir.mkdir();
+ OpenSSHKeyPairResourceWriter keyPairWriter = new OpenSSHKeyPairResourceWriter();
+ try (OutputStream out = new FileOutputStream(new File(sshDir, "id_ecdsa"))) {
+ keyPairWriter.writePrivateKey(sshKeys.getKeyPair(account), null, null, out);
+ }
+
+ // TODO(davido): Disable programmatically host key checking: "StrictHostKeyChecking: no" mode.
+ CharSink configFile = Files.asCharSink(new File(sshDir, "config"), UTF_8);
+ configFile.writeLines(Arrays.asList("Host *", "StrictHostKeyChecking no"));
+
+ JGitKeyCache keyCache = new JGitKeyCache();
+ try (SshdSessionFactory factory =
+ new SshdSessionFactory(keyCache, new DefaultProxyDataFactory())) {
+ factory.setHomeDirectory(userhome);
+ factory.setSshDirectory(sshDir);
+
+ session = factory.getSession(uri, null, fs, TIMEOUT);
+
+ session.addCloseListener(
+ future -> {
+ try {
+ MoreFiles.deleteRecursively(userhome.toPath(), ALLOW_INSECURE);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ });
+ }
+ }
+ return session;
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java b/java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java
index 6c95360..277d219 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java
@@ -18,19 +18,20 @@
import static java.nio.charset.StandardCharsets.US_ASCII;
import com.google.gerrit.acceptance.SshEnabled;
+import com.google.gerrit.acceptance.testsuite.request.SshSessionFactory;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.Account;
import com.google.gerrit.server.account.VersionedAuthorizedKeys;
import com.google.gerrit.server.ssh.SshKeyCache;
import com.google.inject.Inject;
import com.google.inject.Singleton;
-import com.jcraft.jsch.JSch;
-import com.jcraft.jsch.JSchException;
-import com.jcraft.jsch.KeyPair;
import java.io.ByteArrayOutputStream;
-import java.io.UnsupportedEncodingException;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
import java.util.HashMap;
import java.util.Map;
+import org.apache.sshd.common.config.keys.writer.openssh.OpenSSHKeyPairResourceWriter;
@Singleton
public class TestSshKeys {
@@ -86,27 +87,26 @@
private KeyPair createKeyPair(Account.Id accountId, String username, @Nullable String email)
throws Exception {
- KeyPair keyPair = genSshKey();
+ KeyPair keyPair = SshSessionFactory.genSshKey();
authorizedKeys.addKey(accountId, publicKey(keyPair, email));
sshKeyCache.evict(username);
return keyPair;
}
- public static KeyPair genSshKey() throws JSchException {
- JSch jsch = new JSch();
- return KeyPair.genKeyPair(jsch, KeyPair.ECDSA, 256);
- }
-
public static String publicKey(KeyPair sshKey, @Nullable String comment)
- throws UnsupportedEncodingException {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- sshKey.writePublicKey(out, comment);
- return out.toString(US_ASCII.name()).trim();
+ throws IOException, GeneralSecurityException {
+ return preparePublicKey(sshKey, comment).toString(US_ASCII.name()).trim();
}
- public static byte[] privateKey(KeyPair keyPair) {
+ public static byte[] publicKeyBlob(KeyPair sshKey) throws IOException, GeneralSecurityException {
+ return preparePublicKey(sshKey, null).toByteArray();
+ }
+
+ private static ByteArrayOutputStream preparePublicKey(KeyPair sshKey, String comment)
+ throws IOException, GeneralSecurityException {
+ OpenSSHKeyPairResourceWriter keyPairWriter = new OpenSSHKeyPairResourceWriter();
ByteArrayOutputStream out = new ByteArrayOutputStream();
- keyPair.writePrivateKey(out);
- return out.toByteArray();
+ keyPairWriter.writePublicKey(sshKey, comment, out);
+ return out;
}
}
diff --git a/java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImpl.java
index db730a6..895c7a0 100644
--- a/java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImpl.java
@@ -19,7 +19,6 @@
import com.google.gerrit.acceptance.AcceptanceTestRequestScope;
import com.google.gerrit.acceptance.GerritServer.TestSshServerAddress;
-import com.google.gerrit.acceptance.SshSession;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
import com.google.gerrit.acceptance.testsuite.account.TestAccount;
import com.google.gerrit.acceptance.testsuite.account.TestSshKeys;
@@ -82,7 +81,7 @@
public AcceptanceTestRequestScope.Context setApiUser(TestAccount testAccount) {
return atrScope.set(
atrScope.newContext(
- new SshSession(testSshKeys, sshAddress, testAccount),
+ SshSessionFactory.createSession(testSshKeys, sshAddress, testAccount),
createIdentifiedUser(testAccount.accountId())));
}
diff --git a/java/com/google/gerrit/acceptance/testsuite/request/SshSessionFactory.java b/java/com/google/gerrit/acceptance/testsuite/request/SshSessionFactory.java
new file mode 100644
index 0000000..d5dd28a
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/request/SshSessionFactory.java
@@ -0,0 +1,52 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed 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.
+
+package com.google.gerrit.acceptance.testsuite.request;
+
+import static com.google.gerrit.server.config.SshClientImplementation.getFromEnvironment;
+
+import com.google.gerrit.acceptance.SshSession;
+import com.google.gerrit.acceptance.SshSessionJsch;
+import com.google.gerrit.acceptance.SshSessionMina;
+import com.google.gerrit.acceptance.testsuite.account.TestAccount;
+import com.google.gerrit.acceptance.testsuite.account.TestSshKeys;
+import java.net.InetSocketAddress;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+
+public class SshSessionFactory {
+ public static SshSession createSession(
+ TestSshKeys testSshKeys, InetSocketAddress sshAddress, TestAccount testAccount) {
+ return getFromEnvironment().isMina()
+ ? new SshSessionMina(testSshKeys, sshAddress, testAccount)
+ : new SshSessionJsch(testSshKeys, sshAddress, testAccount);
+ }
+
+ public static void initSsh(KeyPair keyPair) {
+ if (getFromEnvironment().isMina()) {
+ SshSessionMina.initClient();
+ } else {
+ SshSessionJsch.initClient(keyPair);
+ }
+ }
+
+ private SshSessionFactory() {}
+
+ public static KeyPair genSshKey() throws GeneralSecurityException {
+ return (getFromEnvironment().isMina()
+ ? SshSessionMina.initKeyPairGenerator()
+ : SshSessionJsch.initKeyPairGenerator())
+ .generateKeyPair();
+ }
+}
diff --git a/java/com/google/gerrit/httpd/BUILD b/java/com/google/gerrit/httpd/BUILD
index ee99702..cd3ebb9 100644
--- a/java/com/google/gerrit/httpd/BUILD
+++ b/java/com/google/gerrit/httpd/BUILD
@@ -32,7 +32,6 @@
"//lib:guava",
"//lib:jgit",
"//lib:jgit-servlet",
- "//lib:jsch",
"//lib:servlet-api",
"//lib:soy",
"//lib/auto:auto-value",
diff --git a/java/com/google/gerrit/httpd/init/BUILD b/java/com/google/gerrit/httpd/init/BUILD
index 37c63a2..adfbdcc 100644
--- a/java/com/google/gerrit/httpd/init/BUILD
+++ b/java/com/google/gerrit/httpd/init/BUILD
@@ -30,6 +30,7 @@
"//java/com/google/gerrit/sshd",
"//lib:guava",
"//lib:jgit",
+ "//lib:jgit-ssh-apache",
"//lib:servlet-api",
"//lib/flogger:api",
"//lib/guice",
diff --git a/java/com/google/gerrit/httpd/init/WebAppInitializer.java b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
index 2df4739..d03340b 100644
--- a/java/com/google/gerrit/httpd/init/WebAppInitializer.java
+++ b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
@@ -103,6 +103,7 @@
import com.google.gerrit.sshd.SshHostKeyModule;
import com.google.gerrit.sshd.SshKeyCacheImpl;
import com.google.gerrit.sshd.SshModule;
+import com.google.gerrit.sshd.SshSessionFactoryInitializer;
import com.google.gerrit.sshd.commands.DefaultCommandModule;
import com.google.gerrit.sshd.commands.IndexCommandsModule;
import com.google.gerrit.sshd.commands.SequenceCommandsModule;
@@ -339,6 +340,7 @@
});
modules.add(new DefaultUrlFormatter.Module());
+ SshSessionFactoryInitializer.init(config);
modules.add(SshKeyCacheImpl.module());
modules.add(
new AbstractModule() {
diff --git a/java/com/google/gerrit/httpd/raw/SshInfoServlet.java b/java/com/google/gerrit/httpd/raw/SshInfoServlet.java
index 1605360..ec67b8b 100644
--- a/java/com/google/gerrit/httpd/raw/SshInfoServlet.java
+++ b/java/com/google/gerrit/httpd/raw/SshInfoServlet.java
@@ -16,11 +16,11 @@
import static java.nio.charset.StandardCharsets.UTF_8;
+import com.google.gerrit.server.ssh.HostKey;
import com.google.gerrit.server.ssh.SshInfo;
import com.google.gerrit.util.http.CacheHeaders;
import com.google.inject.Inject;
import com.google.inject.Singleton;
-import com.jcraft.jsch.HostKey;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
@@ -59,14 +59,14 @@
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
- final List<HostKey> hostKeys = sshd.getHostKeys();
- final String out;
+ List<HostKey> hostKeys = sshd.getHostKeys();
+ String out;
if (!hostKeys.isEmpty()) {
String host = hostKeys.get(0).getHost();
String port = "22";
if (host.contains(":")) {
- final int p = host.lastIndexOf(':');
+ int p = host.lastIndexOf(':');
port = host.substring(p + 1);
host = host.substring(0, p);
}
diff --git a/java/com/google/gerrit/pgm/BUILD b/java/com/google/gerrit/pgm/BUILD
index faedcb7..16eebf2 100644
--- a/java/com/google/gerrit/pgm/BUILD
+++ b/java/com/google/gerrit/pgm/BUILD
@@ -44,6 +44,7 @@
"//lib:args4j",
"//lib:guava",
"//lib:jgit",
+ "//lib:jgit-ssh-apache",
"//lib:protobuf",
"//lib:servlet-api-without-neverlink",
"//lib/auto:auto-value",
diff --git a/java/com/google/gerrit/pgm/Daemon.java b/java/com/google/gerrit/pgm/Daemon.java
index 16c9d27..07bab24 100644
--- a/java/com/google/gerrit/pgm/Daemon.java
+++ b/java/com/google/gerrit/pgm/Daemon.java
@@ -114,6 +114,7 @@
import com.google.gerrit.sshd.SshHostKeyModule;
import com.google.gerrit.sshd.SshKeyCacheImpl;
import com.google.gerrit.sshd.SshModule;
+import com.google.gerrit.sshd.SshSessionFactoryInitializer;
import com.google.gerrit.sshd.commands.DefaultCommandModule;
import com.google.gerrit.sshd.commands.IndexCommandsModule;
import com.google.gerrit.sshd.commands.SequenceCommandsModule;
@@ -482,6 +483,7 @@
});
}
modules.add(new DefaultUrlFormatter.Module());
+ SshSessionFactoryInitializer.init(config);
if (sshd) {
modules.add(SshKeyCacheImpl.module());
} else {
diff --git a/java/com/google/gerrit/server/BUILD b/java/com/google/gerrit/server/BUILD
index 9fa7456..404906d 100644
--- a/java/com/google/gerrit/server/BUILD
+++ b/java/com/google/gerrit/server/BUILD
@@ -97,7 +97,6 @@
"//lib:guava-retrying",
"//lib:jgit",
"//lib:jgit-archive",
- "//lib:jsch",
"//lib:juniversalchardet",
"//lib:mime-util",
"//lib:protobuf",
diff --git a/java/com/google/gerrit/server/audit/BUILD b/java/com/google/gerrit/server/audit/BUILD
index 0870786..3faa259 100644
--- a/java/com/google/gerrit/server/audit/BUILD
+++ b/java/com/google/gerrit/server/audit/BUILD
@@ -46,7 +46,6 @@
"//lib:guava-retrying",
"//lib:jgit",
"//lib:jgit-archive",
- "//lib:jsch",
"//lib:juniversalchardet",
"//lib:mime-util",
"//lib:protobuf",
diff --git a/java/com/google/gerrit/server/config/SshClientImplementation.java b/java/com/google/gerrit/server/config/SshClientImplementation.java
new file mode 100644
index 0000000..5811e4d
--- /dev/null
+++ b/java/com/google/gerrit/server/config/SshClientImplementation.java
@@ -0,0 +1,61 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed 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.
+
+package com.google.gerrit.server.config;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Enums;
+import com.google.common.base.Strings;
+
+/* SSH implementation to use by JGit SSH client transport protocol. */
+public enum SshClientImplementation {
+ /** JCraft JSch implementation. */
+ JSCH,
+
+ /** Apache MINA implementation. */
+ APACHE;
+
+ private static final String ENV_VAR = "SSH_CLIENT_IMPLEMENTATION";
+ private static final String SYS_PROP = "gerrit.sshClientImplementation";
+
+ @VisibleForTesting
+ public static SshClientImplementation getFromEnvironment() {
+ String value = System.getenv(ENV_VAR);
+ if (Strings.isNullOrEmpty(value)) {
+ value = System.getProperty(SYS_PROP);
+ }
+ if (Strings.isNullOrEmpty(value)) {
+ return APACHE;
+ }
+ SshClientImplementation client =
+ Enums.getIfPresent(SshClientImplementation.class, value).orNull();
+ if (!Strings.isNullOrEmpty(System.getenv(ENV_VAR))) {
+ checkArgument(
+ client != null, "Invalid value for env variable %s: %s", ENV_VAR, System.getenv(ENV_VAR));
+ } else {
+ checkArgument(
+ client != null,
+ "Invalid value for system property %s: %s",
+ SYS_PROP,
+ System.getProperty(SYS_PROP));
+ }
+ return client;
+ }
+
+ public boolean isMina() {
+ return this == APACHE;
+ }
+}
diff --git a/java/com/google/gerrit/server/git/validators/CommitValidators.java b/java/com/google/gerrit/server/git/validators/CommitValidators.java
index 1fde48c..1cb0bea 100644
--- a/java/com/google/gerrit/server/git/validators/CommitValidators.java
+++ b/java/com/google/gerrit/server/git/validators/CommitValidators.java
@@ -56,11 +56,11 @@
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectConfig;
import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.ssh.HostKey;
import com.google.gerrit.server.ssh.SshInfo;
import com.google.gerrit.server.util.MagicBranch;
import com.google.inject.Inject;
import com.google.inject.Singleton;
-import com.jcraft.jsch.HostKey;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
diff --git a/java/com/google/gerrit/server/ssh/HostKey.java b/java/com/google/gerrit/server/ssh/HostKey.java
new file mode 100644
index 0000000..9397612
--- /dev/null
+++ b/java/com/google/gerrit/server/ssh/HostKey.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed 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.
+
+package com.google.gerrit.server.ssh;
+
+public class HostKey {
+ private final String host;
+ private final byte[] key;
+
+ public HostKey(String host, byte[] key) {
+ this.host = host;
+ this.key = key;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public byte[] getKey() {
+ return key;
+ }
+}
diff --git a/java/com/google/gerrit/server/ssh/NoSshInfo.java b/java/com/google/gerrit/server/ssh/NoSshInfo.java
index 91a949b..a716398 100644
--- a/java/com/google/gerrit/server/ssh/NoSshInfo.java
+++ b/java/com/google/gerrit/server/ssh/NoSshInfo.java
@@ -14,7 +14,6 @@
package com.google.gerrit.server.ssh;
-import com.jcraft.jsch.HostKey;
import java.util.Collections;
import java.util.List;
diff --git a/java/com/google/gerrit/server/ssh/SshInfo.java b/java/com/google/gerrit/server/ssh/SshInfo.java
index 430846d..ec5a579 100644
--- a/java/com/google/gerrit/server/ssh/SshInfo.java
+++ b/java/com/google/gerrit/server/ssh/SshInfo.java
@@ -14,7 +14,6 @@
package com.google.gerrit.server.ssh;
-import com.jcraft.jsch.HostKey;
import java.util.List;
public interface SshInfo {
diff --git a/java/com/google/gerrit/sshd/BUILD b/java/com/google/gerrit/sshd/BUILD
index a5b88b4..0668c1e 100644
--- a/java/com/google/gerrit/sshd/BUILD
+++ b/java/com/google/gerrit/sshd/BUILD
@@ -4,6 +4,7 @@
name = "sshd",
srcs = glob(["**/*.java"]),
visibility = ["//visibility:public"],
+ runtime_deps = ["//lib:jsch"],
deps = [
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/common:server",
@@ -28,7 +29,7 @@
"//lib:guava",
"//lib:jgit",
"//lib:jgit-archive",
- "//lib:jsch",
+ "//lib:jgit-ssh-apache",
"//lib:servlet-api",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
diff --git a/java/com/google/gerrit/sshd/SshDaemon.java b/java/com/google/gerrit/sshd/SshDaemon.java
index cd5a511..9ae8660 100644
--- a/java/com/google/gerrit/sshd/SshDaemon.java
+++ b/java/com/google/gerrit/sshd/SshDaemon.java
@@ -37,6 +37,7 @@
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.ssh.HostKey;
import com.google.gerrit.server.ssh.SshAdvertisedAddresses;
import com.google.gerrit.server.ssh.SshInfo;
import com.google.gerrit.server.ssh.SshListenAddresses;
@@ -44,8 +45,6 @@
import com.google.gerrit.server.util.SocketUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
-import com.jcraft.jsch.HostKey;
-import com.jcraft.jsch.JSchException;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
@@ -435,12 +434,7 @@
byte[] keyBin = buf.getCompactData();
for (String addr : advertised) {
- try {
- r.add(new HostKey(addr, keyBin));
- } catch (JSchException e) {
- logger.atWarning().log(
- "Cannot format SSHD host key [%s]: %s", pub.getAlgorithm(), e.getMessage());
- }
+ r.add(new HostKey(addr, keyBin));
}
}
diff --git a/java/com/google/gerrit/sshd/SshSessionFactoryInitializer.java b/java/com/google/gerrit/sshd/SshSessionFactoryInitializer.java
new file mode 100644
index 0000000..1cdf923
--- /dev/null
+++ b/java/com/google/gerrit/sshd/SshSessionFactoryInitializer.java
@@ -0,0 +1,37 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed 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.
+
+package com.google.gerrit.sshd;
+
+import static com.google.gerrit.server.config.SshClientImplementation.APACHE;
+
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.transport.SshSessionFactory;
+import org.eclipse.jgit.transport.sshd.DefaultProxyDataFactory;
+import org.eclipse.jgit.transport.sshd.JGitKeyCache;
+import org.eclipse.jgit.transport.sshd.SshdSessionFactory;
+import org.eclipse.jgit.util.FS;
+
+public class SshSessionFactoryInitializer {
+ public static void init(Config config) {
+ if (APACHE == config.getEnum("ssh", null, "clientImplementation", APACHE)) {
+ SshdSessionFactory factory =
+ new SshdSessionFactory(new JGitKeyCache(), new DefaultProxyDataFactory());
+ factory.setHomeDirectory(FS.DETECTED.userHome());
+ SshSessionFactory.setInstance(factory);
+ }
+ }
+
+ private SshSessionFactoryInitializer() {}
+}
diff --git a/java/com/google/gerrit/testing/TestLoggingActivator.java b/java/com/google/gerrit/testing/TestLoggingActivator.java
index a766429..6b5d8fd 100644
--- a/java/com/google/gerrit/testing/TestLoggingActivator.java
+++ b/java/com/google/gerrit/testing/TestLoggingActivator.java
@@ -31,6 +31,7 @@
// Silence non-critical messages from MINA SSHD.
.put("org.apache.mina", Level.WARN)
+ .put("org.apache.sshd.client", Level.WARN)
.put("org.apache.sshd.common", Level.WARN)
.put("org.apache.sshd.server", Level.WARN)
.put("org.apache.sshd.common.keyprovider.FileKeyPairProvider", Level.INFO)
@@ -61,6 +62,8 @@
// Silence non-critical messages from JGit.
.put("org.eclipse.jgit.transport.PacketLineIn", Level.WARN)
.put("org.eclipse.jgit.transport.PacketLineOut", Level.WARN)
+ .put("org.eclipse.jgit.internal.transport.sshd", Level.WARN)
+ .put("org.eclipse.jgit.util.FileUtils", Level.WARN)
.put("org.eclipse.jgit.internal.storage.file.FileSnapshot", Level.WARN)
.put("org.eclipse.jgit.util.FS", Level.WARN)
.put("org.eclipse.jgit.util.SystemReader", Level.WARN)
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 1b55652..7495e63 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -75,6 +75,7 @@
import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.acceptance.testsuite.request.SshSessionFactory;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.entities.AccessSection;
@@ -150,9 +151,9 @@
import com.google.gerrit.truth.NullAwareCorrespondence;
import com.google.inject.Inject;
import com.google.inject.Provider;
-import com.jcraft.jsch.KeyPair;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.security.KeyPair;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -2001,7 +2002,7 @@
// Add a new key
sender.clear();
- String newKey = TestSshKeys.publicKey(TestSshKeys.genSshKey(), admin.email());
+ String newKey = TestSshKeys.publicKey(SshSessionFactory.genSshKey(), admin.email());
gApi.accounts().self().addSshKey(newKey);
info = gApi.accounts().self().listSshKeys();
assertThat(info).hasSize(2);
@@ -2023,7 +2024,7 @@
// Add another new key
sender.clear();
- String newKey2 = TestSshKeys.publicKey(TestSshKeys.genSshKey(), admin.email());
+ String newKey2 = TestSshKeys.publicKey(SshSessionFactory.genSshKey(), admin.email());
gApi.accounts().self().addSshKey(newKey2);
info = gApi.accounts().self().listSshKeys();
assertThat(info).hasSize(3);
@@ -2074,7 +2075,7 @@
// Add a new key
sender.clear();
- String newKey = TestSshKeys.publicKey(TestSshKeys.genSshKey(), user.email());
+ String newKey = TestSshKeys.publicKey(SshSessionFactory.genSshKey(), user.email());
gApi.accounts().id(user.username()).addSshKey(newKey);
info = gApi.accounts().id(user.username()).listSshKeys();
assertThat(info).hasSize(2);
@@ -2103,7 +2104,7 @@
@Test
@UseSsh
public void userCannotAddSshKeyToOtherAccount() throws Exception {
- String newKey = TestSshKeys.publicKey(TestSshKeys.genSshKey(), admin.email());
+ String newKey = TestSshKeys.publicKey(SshSessionFactory.genSshKey(), admin.email());
requestScopeOperations.setApiUser(user.id());
assertThrows(AuthException.class, () -> gApi.accounts().id(admin.username()).addSshKey(newKey));
}
diff --git a/lib/BUILD b/lib/BUILD
index 0110047..f924e4ca 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -54,6 +54,16 @@
)
java_library(
+ name = "jgit-ssh-apache",
+ data = ["//lib:LICENSE-jgit"],
+ visibility = ["//visibility:public"],
+ exports = ["@jgit//org.eclipse.jgit.ssh.apache:ssh-apache"],
+ runtime_deps = [
+ "//lib/mina:sshd-sftp",
+ ],
+)
+
+java_library(
name = "jgit-archive",
data = ["//lib:LICENSE-jgit"],
visibility = ["//visibility:public"],
diff --git a/lib/mina/BUILD b/lib/mina/BUILD
index 70e7c1d..3f23263 100644
--- a/lib/mina/BUILD
+++ b/lib/mina/BUILD
@@ -13,6 +13,13 @@
)
java_library(
+ name = "sshd-sftp",
+ data = ["//lib:LICENSE-Apache2.0"],
+ visibility = ["//visibility:public"],
+ exports = ["@sshd-sftp//jar"],
+)
+
+java_library(
name = "eddsa",
data = ["//lib:LICENSE-CC0-1.0"],
visibility = ["//visibility:public"],
diff --git a/lib/nongoogle_test.sh b/lib/nongoogle_test.sh
index f596164..272cfa9 100755
--- a/lib/nongoogle_test.sh
+++ b/lib/nongoogle_test.sh
@@ -40,6 +40,7 @@
soy
sshd-mina
sshd-osgi
+sshd-sftp
testcontainers
truth
truth-java8-extension
diff --git a/resources/log4j.properties b/resources/log4j.properties
index 28c0ee4..39246b3 100644
--- a/resources/log4j.properties
+++ b/resources/log4j.properties
@@ -21,6 +21,7 @@
# Silence non-critical messages from MINA SSHD.
#
log4j.logger.org.apache.mina=WARN
+log4j.logger.org.apache.sshd.client=WARN
log4j.logger.org.apache.sshd.common=WARN
log4j.logger.org.apache.sshd.server=WARN
log4j.logger.org.apache.sshd.common.keyprovider.FileKeyPairProvider=INFO
diff --git a/tools/eclipse/project.py b/tools/eclipse/project.py
index acb5346..c7398a8 100755
--- a/tools/eclipse/project.py
+++ b/tools/eclipse/project.py
@@ -188,6 +188,8 @@
classpathentry('src', 'modules/jgit/org.eclipse.jgit.junit/src')
classpathentry('src', 'modules/jgit/org.eclipse.jgit.ssh.jsch/src')
classpathentry('src', 'modules/jgit/org.eclipse.jgit.ssh.jsch/resources')
+ classpathentry('src', 'modules/jgit/org.eclipse.jgit.ssh.apache/src')
+ classpathentry('src', 'modules/jgit/org.eclipse.jgit.ssh.apache/resources')
def classpathentry(kind, path, src=None, out=None, exported=None, excluding=None):
e = doc.createElement('classpathentry')
diff --git a/tools/nongoogle.bzl b/tools/nongoogle.bzl
index 11710f0..3d04592 100644
--- a/tools/nongoogle.bzl
+++ b/tools/nongoogle.bzl
@@ -44,6 +44,12 @@
)
maven_jar(
+ name = "sshd-sftp",
+ artifact = "org.apache.sshd:sshd-sftp:" + SSHD_VERS,
+ sha1 = "6eddfe8fdf59a3d9a49151e4177f8c1bebeb30c9",
+ )
+
+ maven_jar(
name = "eddsa",
artifact = "net.i2p.crypto:eddsa:0.3.0",
sha1 = "1901c8d4d8bffb7d79027686cfb91e704217c3e1",