org.jenkins-ci
diff --git a/src/main/java/org/jenkinsci/main/modules/sshd/AsynchronousCommand.java b/src/main/java/org/jenkinsci/main/modules/sshd/AsynchronousCommand.java
index a6debb3..efe3b01 100644
--- a/src/main/java/org/jenkinsci/main/modules/sshd/AsynchronousCommand.java
+++ b/src/main/java/org/jenkinsci/main/modules/sshd/AsynchronousCommand.java
@@ -81,9 +81,9 @@ public void setSession(ServerSession session) {
@CheckForNull
protected User getCurrentUser() {
- final Jenkins jenkins = Jenkins.getInstance();
- if (jenkins != null && jenkins.isUseSecurity()) {
- return User.get(getSession().getUsername()); // then UserAuthNamedFactory must have done public key auth
+ final Jenkins jenkins = Jenkins.get();
+ if (jenkins.isUseSecurity()) {
+ return User.getById(getSession().getUsername(), true); // then UserAuthNamedFactory must have done public key auth
} else {
return null; // not authenticated. anonymous.
}
diff --git a/src/main/java/org/jenkinsci/main/modules/sshd/CLICommandAdapter.java b/src/main/java/org/jenkinsci/main/modules/sshd/CLICommandAdapter.java
index cd88324..3c72c77 100644
--- a/src/main/java/org/jenkinsci/main/modules/sshd/CLICommandAdapter.java
+++ b/src/main/java/org/jenkinsci/main/modules/sshd/CLICommandAdapter.java
@@ -1,13 +1,15 @@
package org.jenkinsci.main.modules.sshd;
+import hudson.CloseProofOutputStream;
import hudson.Extension;
import hudson.cli.CLICommand;
import hudson.model.User;
+import org.apache.sshd.server.command.Command;
+
import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.util.Locale;
-import org.apache.sshd.server.command.Command;
/**
* {@link SshCommandFactory} that invokes {@link CLICommand}s.
@@ -26,14 +28,17 @@ public Command create(CommandLine commandLine) {
@Override
public int runCommand() throws IOException {
User u = getCurrentUser();
- if (u!=null) c.setTransportAuth(u.impersonate());
+ if (u != null) {
+ c.setTransportAuth2(u.impersonate2());
+ }
CommandLine cmds = getCmdLine();
//TODO: Consider switching to UTF-8
+ //TODO: Consider removing the CloseProofOutputStream wrapper when SSHD-1257 is available
return c.main(cmds.subList(1,cmds.size()), Locale.getDefault(), getInputStream(),
- new PrintStream(getOutputStream(), false, Charset.defaultCharset().toString()),
- new PrintStream(getErrorStream(), false, Charset.defaultCharset().toString()));
+ new PrintStream(new CloseProofOutputStream(getOutputStream()), false, Charset.defaultCharset().toString()),
+ new PrintStream(new CloseProofOutputStream(getErrorStream()), false, Charset.defaultCharset().toString()));
}
};
}
diff --git a/src/main/java/org/jenkinsci/main/modules/sshd/PortAdvertiser.java b/src/main/java/org/jenkinsci/main/modules/sshd/PortAdvertiser.java
index 7df8afd..851d905 100644
--- a/src/main/java/org/jenkinsci/main/modules/sshd/PortAdvertiser.java
+++ b/src/main/java/org/jenkinsci/main/modules/sshd/PortAdvertiser.java
@@ -25,10 +25,7 @@ public String getEndpoint() {
try {
int p = sshd.getActualPort();
if (p>0) {
- final Jenkins jenkins = Jenkins.getInstance();
- if (jenkins == null) {
- throw new IllegalStateException("Jenkins has not been started, or was already shut down");
- }
+ final Jenkins jenkins = Jenkins.get();
return (host != null ? host : new URL(jenkins.getRootUrl()).getHost()) + ":" + p;
}
} catch (Exception e) {
diff --git a/src/main/java/org/jenkinsci/main/modules/sshd/UserAuthNamedFactory.java b/src/main/java/org/jenkinsci/main/modules/sshd/UserAuthNamedFactory.java
index 29ee8da..990f5bd 100644
--- a/src/main/java/org/jenkinsci/main/modules/sshd/UserAuthNamedFactory.java
+++ b/src/main/java/org/jenkinsci/main/modules/sshd/UserAuthNamedFactory.java
@@ -18,8 +18,8 @@ class UserAuthNamedFactory implements UserAuthFactory {
UserAuthFactory none = UserAuthNoneFactory.INSTANCE;
private UserAuthFactory select() {
- final Jenkins jenkins = Jenkins.getInstance();
- return (jenkins != null && jenkins.isUseSecurity()) ? publicKey : none;
+ final Jenkins jenkins = Jenkins.get();
+ return jenkins.isUseSecurity() ? publicKey : none;
}
public String getName() {
diff --git a/src/main/resources/index.jelly b/src/main/resources/index.jelly
new file mode 100644
index 0000000..496cc22
--- /dev/null
+++ b/src/main/resources/index.jelly
@@ -0,0 +1,2 @@
+
+Adds SSH server functionality to Jenkins, exposing CLI commands through it.
\ No newline at end of file
diff --git a/src/test/java/org/jenkinsci/main/modules/cli/auth/ssh/CLITest.java b/src/test/java/org/jenkinsci/main/modules/cli/auth/ssh/CLITest.java
index f16b43d..39e3c5a 100644
--- a/src/test/java/org/jenkinsci/main/modules/cli/auth/ssh/CLITest.java
+++ b/src/test/java/org/jenkinsci/main/modules/cli/auth/ssh/CLITest.java
@@ -25,17 +25,19 @@
package org.jenkinsci.main.modules.cli.auth.ssh;
import com.gargoylesoftware.htmlunit.WebResponse;
+import hudson.Extension;
import hudson.Launcher;
import hudson.Proc;
+import hudson.cli.CLICommand;
import hudson.model.FreeStyleProject;
import hudson.model.UnprotectedRootAction;
import hudson.model.User;
import hudson.security.csrf.CrumbExclusion;
import hudson.util.StreamTaskListener;
-import jenkins.model.GlobalConfiguration;
-import jenkins.model.Jenkins;
import io.jenkins.cli.shaded.org.apache.commons.io.FileUtils;
import io.jenkins.cli.shaded.org.apache.commons.io.output.TeeOutputStream;
+import jenkins.model.GlobalConfiguration;
+import jenkins.model.Jenkins;
import org.apache.commons.io.IOUtils;
import org.apache.sshd.common.util.io.ModifiableFileWatcher;
import org.jenkinsci.main.modules.sshd.SSHD;
@@ -64,17 +66,19 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
-import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNull;
import static org.junit.Assume.assumeNoException;
import static org.junit.Assume.assumeThat;
import static org.junit.Assume.assumeTrue;
@@ -130,7 +134,7 @@ public void strictHostKey() throws Exception {
SSHD.get().setPort(0);
File privkey = tmp.newFile("id_rsa");
FileUtils.copyURLToFile(CLITest.class.getResource("id_rsa"), privkey);
- User.get("admin").addProperty(new UserPropertyImpl(IOUtils.toString(CLITest.class.getResource("id_rsa.pub"))));
+ User.getById("admin", true).addProperty(new UserPropertyImpl(IOUtils.toString(CLITest.class.getResource("id_rsa.pub"), StandardCharsets.UTF_8)));
assertNotEquals(0, new Launcher.LocalLauncher(StreamTaskListener.fromStderr()).launch().cmds(
"java", "-Duser.home=" + home, "-jar", jar.getAbsolutePath(), "-s", r.getURL().toString(), "-ssh", "-user", "admin", "-i", privkey.getAbsolutePath(), "-strictHostKey", "who-am-i"
).stdout(System.out).stderr(System.err).join());
@@ -162,7 +166,7 @@ public void interrupt() throws Exception {
SSHD.get().setPort(0);
File privkey = tmp.newFile("id_rsa");
FileUtils.copyURLToFile(CLITest.class.getResource("id_rsa"), privkey);
- User.get("admin").addProperty(new UserPropertyImpl(IOUtils.toString(CLITest.class.getResource("id_rsa.pub"))));
+ User.getById("admin", true).addProperty(new UserPropertyImpl(IOUtils.toString(CLITest.class.getResource("id_rsa.pub"), StandardCharsets.UTF_8)));
FreeStyleProject p = r.createFreeStyleProject("p");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
p.getBuildersList().add(new SleepBuilder(TimeUnit.MINUTES.toMillis(2)));
@@ -179,6 +183,28 @@ public void interrupt() throws Exception {
r.waitForCompletion(p.getLastBuild());
}
+ @Issue("JENKINS-68541")
+ @Test
+ public void outputStream() throws Exception {
+ home = tempHome();
+ grabCliJar();
+
+ r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
+ r.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().grant(Jenkins.ADMINISTER).everywhere().to("admin"));
+ SSHD.get().setPort(0);
+ File privkey = tmp.newFile("id_rsa");
+ FileUtils.copyURLToFile(CLITest.class.getResource("id_rsa"), privkey);
+ User.getById("admin", true).addProperty(new UserPropertyImpl(IOUtils.toString(CLITest.class.getResource("id_rsa.pub"), StandardCharsets.UTF_8)));
+ StreamTaskListener stl = StreamTaskListener.fromStderr();
+ List args = Arrays.asList("java", "-Duser.home=" + home, "-jar", jar.getAbsolutePath(), "-s", r.getURL().toString(), "-ssh", "-user", "admin", "-i", privkey.getAbsolutePath(), "close-stdout-stream");
+ int ret = new Launcher.LocalLauncher(stl).launch().cmds(args)
+ .stdout(System.out)
+ .stderr(System.err)
+ .start()
+ .joinWithTimeout(5, TimeUnit.SECONDS, stl);
+ assertEquals(0, ret);
+ }
+
@Test @Issue("JENKINS-44361")
public void reportNotJenkins() throws Exception {
home = tempHome();
@@ -241,9 +267,9 @@ public void redirectToEndpointShouldBeFollowed() throws Exception {
WebResponse rsp = wc.goTo("cli-proxy/").getWebResponse();
assertEquals(rsp.getContentAsString(), HttpURLConnection.HTTP_MOVED_TEMP, rsp.getStatusCode());
- assertEquals(rsp.getContentAsString(), null, rsp.getResponseHeaderValue("X-Jenkins"));
- assertEquals(rsp.getContentAsString(), null, rsp.getResponseHeaderValue("X-Jenkins-CLI-Port"));
- assertEquals(rsp.getContentAsString(), null, rsp.getResponseHeaderValue("X-SSH-Endpoint"));
+ assertNull(rsp.getContentAsString(), rsp.getResponseHeaderValue("X-Jenkins"));
+ assertNull(rsp.getContentAsString(), rsp.getResponseHeaderValue("X-Jenkins-CLI-Port"));
+ assertNull(rsp.getContentAsString(), rsp.getResponseHeaderValue("X-SSH-Endpoint"));
String url = r.getURL().toString() + "cli-proxy/";
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -296,4 +322,19 @@ public boolean process(HttpServletRequest request, HttpServletResponse response,
return true;
}
}
+
+ @Extension
+ public static class CloseStdoutStreamCommand extends CLICommand {
+
+ @Override
+ public String getShortDescription() {
+ return "Close stdout";
+ }
+
+ @Override
+ protected int run() {
+ stdout.close();
+ return 0;
+ }
+ }
}
diff --git a/src/test/java/org/jenkinsci/main/modules/cli/auth/ssh/UserPropertyImplTest.java b/src/test/java/org/jenkinsci/main/modules/cli/auth/ssh/UserPropertyImplTest.java
index 8ece9f5..cf41908 100644
--- a/src/test/java/org/jenkinsci/main/modules/cli/auth/ssh/UserPropertyImplTest.java
+++ b/src/test/java/org/jenkinsci/main/modules/cli/auth/ssh/UserPropertyImplTest.java
@@ -25,7 +25,7 @@ public void dsa() throws Exception {
private void testRoundtrip(String publicKey) throws Exception {
r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
- User foo = User.get("foo");
+ User foo = User.getById("foo", true);
foo.addProperty(new UserPropertyImpl(publicKey));
r.configRoundtrip(foo);
assertEquals(publicKey, foo.getProperty(UserPropertyImpl.class).authorizedKeys);