diff --git a/pom.xml b/pom.xml index e13600c..a809178 100644 --- a/pom.xml +++ b/pom.xml @@ -122,11 +122,6 @@ - - org.jenkins-ci.plugins - trilead-api - true - org.jenkins-ci.plugins variant diff --git a/src/main/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/TrileadSSHPasswordAuthenticator.java b/src/main/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/TrileadSSHPasswordAuthenticator.java deleted file mode 100644 index 52398cd..0000000 --- a/src/main/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/TrileadSSHPasswordAuthenticator.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2011-2012, CloudBees, Inc., Stephen Connolly. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.cloudbees.jenkins.plugins.sshcredentials.impl; - -import com.cloudbees.jenkins.plugins.sshcredentials.SSHAuthenticator; -import com.cloudbees.jenkins.plugins.sshcredentials.SSHAuthenticatorFactory; -import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials; -import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; -import com.trilead.ssh2.Connection; -import edu.umd.cs.findbugs.annotations.CheckForNull; -import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; -import org.jenkinsci.plugins.variant.OptionalExtension; - -import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; -import java.util.logging.Logger; - -/** - * Does password auth with a {@link Connection}. - */ -public class TrileadSSHPasswordAuthenticator extends SSHAuthenticator { - - /** - * Our logger - */ - private static final Logger LOGGER = Logger.getLogger(TrileadSSHPasswordAuthenticator.class.getName()); - private static final String PASSWORD = "password"; - private static final String KEYBOARD_INTERACTIVE = "keyboard-interactive"; - - /** - * Constructor. - * - * @param connection the connection we will be authenticating. - * @deprecated - */ - @Deprecated - public TrileadSSHPasswordAuthenticator(Connection connection, StandardUsernamePasswordCredentials user) { - this(connection, user, null); - } - - /** - * Constructor. - * - * @param connection the connection we will be authenticating. - * @since 1.4 - */ - public TrileadSSHPasswordAuthenticator(@NonNull Connection connection, - @NonNull StandardUsernamePasswordCredentials user, - @CheckForNull String username) { - super(connection, user, username); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean canAuthenticate() { - try { - for (String authMethod : getConnection().getRemainingAuthMethods(getUsername())) { - if (PASSWORD.equals(authMethod)) { - // prefer password - return true; - } - if (KEYBOARD_INTERACTIVE.equals(authMethod)) { - return true; - } - } - } catch (IOException e) { - e.printStackTrace(getListener().error("Failed to authenticate")); - } - return false; - } - - /** - * {@inheritDoc} - */ - @Override - protected boolean doAuthenticate() { - final StandardUsernamePasswordCredentials user = getUser(); - final String username = getUsername(); - - try { - final Connection connection = getConnection(); - final String password = user.getPassword().getPlainText(); - boolean tried = false; - - List availableMethods = Arrays.asList(connection.getRemainingAuthMethods(username)); - if (availableMethods.contains(PASSWORD)) { - // prefer password - if (connection.authenticateWithPassword(username, password)) { - LOGGER.fine("Authentication with 'password' succeeded."); - return true; - } - getListener().error("Failed to authenticate as %s. Wrong password. (credentialId:%s/method:password)", - username, user.getId()); - tried = true; - } - if (availableMethods.contains(KEYBOARD_INTERACTIVE)) { - if (connection.authenticateWithKeyboardInteractive(username, (name, instruction, numPrompts, prompt, echo) -> { - // most SSH servers just use keyboard interactive to prompt for the password - // match "assword" is safer than "password"... you don't *want* to know why! - return prompt != null && prompt.length > 0 && prompt[0].toLowerCase(Locale.ENGLISH) - .contains("assword") - ? new String[]{password} - : new String[0]; - })) { - LOGGER.fine("Authentication with 'keyboard-interactive' succeeded."); - return true; - } - getListener() - .error("Failed to authenticate as %s. Wrong password. " - + "(credentialId:%s/method:keyboard-interactive)", - username, user.getId()); - tried = true; - } - - if (!tried) { - getListener().error("The server does not allow password authentication. Available options are %s", - availableMethods); - } - } catch (IOException e) { - e.printStackTrace(getListener() - .error("Unexpected error while trying to authenticate as %s with credential=%s", username, - user.getId())); - } - return false; - } - - /** - * {@inheritDoc} - */ - @OptionalExtension(requirePlugins = {"trilead-api"}) - public static class Factory extends SSHAuthenticatorFactory { - - /** - * {@inheritDoc} - */ - @Override - protected SSHAuthenticator newInstance(@NonNull C connection, - @NonNull U user) { - return newInstance(connection, user, null); - } - - /** - * {@inheritDoc} - */ - @Nullable - @Override - @SuppressWarnings("unchecked") - protected SSHAuthenticator newInstance(@NonNull C connection, - @NonNull U user, - @CheckForNull String - username) { - if (supports(connection.getClass(), user.getClass())) { - return (SSHAuthenticator) new TrileadSSHPasswordAuthenticator((Connection) connection, - (StandardUsernamePasswordCredentials) user, username); - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - protected boolean supports(@NonNull Class connectionClass, - @NonNull Class userClass) { - return Connection.class.isAssignableFrom(connectionClass) - && StandardUsernamePasswordCredentials.class.isAssignableFrom(userClass); - } - - private static final long serialVersionUID = 1L; - } -} diff --git a/src/main/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/TrileadSSHPublicKeyAuthenticator.java b/src/main/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/TrileadSSHPublicKeyAuthenticator.java deleted file mode 100644 index 8fb6aac..0000000 --- a/src/main/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/TrileadSSHPublicKeyAuthenticator.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2011-2012, CloudBees, Inc., Stephen Connolly. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.cloudbees.jenkins.plugins.sshcredentials.impl; - -import com.cloudbees.jenkins.plugins.sshcredentials.SSHAuthenticator; -import com.cloudbees.jenkins.plugins.sshcredentials.SSHAuthenticatorFactory; -import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey; -import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials; -import com.trilead.ssh2.Connection; -import edu.umd.cs.findbugs.annotations.CheckForNull; -import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; -import hudson.util.Secret; -import org.jenkinsci.plugins.variant.OptionalExtension; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.logging.Logger; - -/** - * Does public key auth with a {@link Connection}. - */ -public class TrileadSSHPublicKeyAuthenticator extends SSHAuthenticator { - - /** - * Our logger. - */ - private static final Logger LOGGER = Logger.getLogger(TrileadSSHPublicKeyAuthenticator.class.getName()); - private static final String PUBLICKEY = "publickey"; - - /** - * Constructor. - * - * @param connection the connection we will be authenticating. - */ - public TrileadSSHPublicKeyAuthenticator(Connection connection, SSHUserPrivateKey user) { - this(connection, user, null); - } - - /** - * Constructor. - * - * @param connection the connection we will be authenticating. - */ - public TrileadSSHPublicKeyAuthenticator(@NonNull Connection connection, - @NonNull SSHUserPrivateKey user, - @CheckForNull String username) { - super(connection, user, username); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean canAuthenticate() { - try { - return getRemainingAuthMethods().contains(PUBLICKEY); - } catch (IOException e) { - e.printStackTrace(getListener().error("Failed to authenticate")); - return false; - } - } - - private List getRemainingAuthMethods() throws IOException { - return Arrays.asList(getConnection().getRemainingAuthMethods(getUsername())); - } - - /** - * {@inheritDoc} - */ - @Override - protected boolean doAuthenticate() { - final SSHUserPrivateKey user = getUser(); - final String username = getUsername(); - try { - final Connection connection = getConnection(); - final Secret userPassphrase = user.getPassphrase(); - final String passphrase = userPassphrase == null ? null : userPassphrase.getPlainText(); - - Collection availableMethods = getRemainingAuthMethods(); - if (availableMethods.contains(PUBLICKEY)) { - int count = 0; - List ioe = new ArrayList<>(); - for (String privateKey : getPrivateKeys(user)) { - try { - if (connection.authenticateWithPublicKey(username, privateKey.toCharArray(), passphrase)) { - LOGGER.fine("Authentication with 'publickey' succeeded."); - return true; - } - } catch (IOException e) { - ioe.add(e); - } - count++; - getListener() - .error("Server rejected the %d private key(s) for %s (credentialId:%s/method:publickey)", - count, username, user.getId()); - } - for (IOException e : ioe) { - e.printStackTrace(getListener() - .error("Failed to authenticate as %s with credential=%s", username, getUser().getId())); - } - return false; - } else { - getListener().error("The server does not allow public key authentication. Available options are %s", - availableMethods); - return false; - } - } catch (IOException e) { - e.printStackTrace(getListener() - .error("Failed to authenticate as %s with credential=%s", username, getUser().getId())); - return false; - } - } - - /** - * {@inheritDoc} - */ - @OptionalExtension(requirePlugins = {"trilead-api"}) - public static class Factory extends SSHAuthenticatorFactory { - - /** - * {@inheritDoc} - */ - @Override - @SuppressWarnings("unchecked") - protected SSHAuthenticator newInstance(@NonNull C connection, - @NonNull U user) { - return newInstance(connection, user, null); - } - - /** - * {@inheritDoc} - */ - @Nullable - @Override - @SuppressWarnings("unchecked") - protected SSHAuthenticator newInstance(@NonNull C connection, - @NonNull U user, - @CheckForNull String - username) { - if (supports(connection.getClass(), user.getClass())) { - return (SSHAuthenticator) new TrileadSSHPublicKeyAuthenticator((Connection) connection, - (SSHUserPrivateKey) user, username); - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - protected boolean supports(@NonNull Class connectionClass, - @NonNull Class userClass) { - return Connection.class.isAssignableFrom(connectionClass) - && SSHUserPrivateKey.class.isAssignableFrom(userClass); - } - - private static final long serialVersionUID = 1L; - } -} diff --git a/src/test/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/TrileadSSHPasswordAuthenticatorTest.java b/src/test/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/TrileadSSHPasswordAuthenticatorTest.java deleted file mode 100644 index 62b3195..0000000 --- a/src/test/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/TrileadSSHPasswordAuthenticatorTest.java +++ /dev/null @@ -1,295 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2011-2012, CloudBees, Inc., Stephen Connolly. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.cloudbees.jenkins.plugins.sshcredentials.impl; - -import com.cloudbees.jenkins.plugins.sshcredentials.SSHAuthenticator; -import com.cloudbees.jenkins.plugins.sshcredentials.SSHAuthenticatorFactory; -import com.cloudbees.plugins.credentials.CredentialsScope; -import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; -import com.trilead.ssh2.Connection; -import com.trilead.ssh2.ServerHostKeyVerifier; -import hudson.model.Computer; -import hudson.model.Items; -import hudson.model.TaskListener; -import hudson.remoting.VirtualChannel; -import hudson.slaves.DumbSlave; -import jenkins.security.MasterToSlaveCallable; - -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.jvnet.hudson.test.JenkinsRule; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Proxy; -import java.util.Collections; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertNotNull; -import static org.hamcrest.MatcherAssert.assertThat; - -public class TrileadSSHPasswordAuthenticatorTest { - - private Connection connection; - private StandardUsernamePasswordCredentials user; - private Object sshd; - - @Rule public JenkinsRule r = new JenkinsRule(); - - @After - public void tearDown() { - if (connection != null) { - connection.close(); - connection = null; - } - if (sshd!=null) { - try { - invoke(sshd, "stop", new Class[] {Boolean.TYPE}, new Object[] {true}); - } catch (Throwable t) { - Logger.getLogger(getClass().getName()).log(Level.WARNING, "Problems shutting down ssh server", t); - } - } - } - - // disabled as Apache MINA sshd does not provide easy mech for giving a Keyboard Interactive authenticator - // so this test relies on having a local sshd which is keyboard interactive only - public void dontTestKeyboardInteractive() throws Exception { - connection = new Connection("localhost"); - connection.connect(new NoVerifier()); - TrileadSSHPasswordAuthenticator instance = - new TrileadSSHPasswordAuthenticator(connection, new BasicSSHUserPassword(CredentialsScope.SYSTEM, - null, "....", // <---- put your username here - "....", // <---- put your password here - null)); - assertThat(instance.canAuthenticate(), is(true)); - assertThat(instance.authenticate(), is(true)); - assertThat(instance.isAuthenticated(), is(true)); - } - - @Before - public void setUp() { - user =(StandardUsernamePasswordCredentials) Items.XSTREAM.fromXML(Items.XSTREAM.toXML(new BasicSSHUserPassword(CredentialsScope.SYSTEM, null, "foobar", "foomanchu", null))); - } - - @Test - public void testPassword() throws Exception { - sshd = createPasswordAuthenticatedSshServer(); - invoke(sshd, "start", null, null); - int port = (Integer)invoke(sshd, "getPort", null, null); - connection = new Connection("localhost", port); - connection.connect(new NoVerifier()); - TrileadSSHPasswordAuthenticator instance = - new TrileadSSHPasswordAuthenticator(connection, user); - assertThat(instance.getAuthenticationMode(), is(SSHAuthenticator.Mode.AFTER_CONNECT)); - assertThat(instance.canAuthenticate(), is(true)); - assertThat(instance.authenticate(), is(true)); - assertThat(instance.isAuthenticated(), is(true)); - } - - private Object createPasswordAuthenticatedSshServer() throws InvocationTargetException, NoSuchMethodException, ClassNotFoundException, InstantiationException, IllegalAccessException { - return createPasswordAuthenticatedSshServer(null); - } - - private Object createPasswordAuthenticatedSshServer(final String username) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, InstantiationException { - Object sshd = newDefaultSshServer(); - Class keyPairProviderClass = newKeyPairProviderClass(); - Object provider = newProvider(); - Class authenticatorClass = newAuthenticatorClass(); - Object authenticator = newAuthenticator(authenticatorClass, username); - Object factory = newFactory(); - - invoke(sshd, "setPort", new Class[] {Integer.TYPE}, new Object[] {0}); - invoke(sshd, "setKeyPairProvider", new Class[] {keyPairProviderClass}, new Object[] {provider}); - invoke(sshd, "setPasswordAuthenticator", new Class[] {authenticatorClass}, new Object[] {authenticator}); - invoke(sshd, "setUserAuthFactories", new Class[] {List.class}, new Object[] {Collections.singletonList(factory)}); - - return sshd; - } - - @Test - public void testFactory() throws Exception { - sshd = createPasswordAuthenticatedSshServer(); - invoke(sshd, "start", null, null); - int port = (Integer)invoke(sshd, "getPort", null, null); - connection = new Connection("localhost", port); - connection.connect(new NoVerifier()); - SSHAuthenticator instance = SSHAuthenticator.newInstance(connection, user); - assertThat(instance.getAuthenticationMode(), is(SSHAuthenticator.Mode.AFTER_CONNECT)); - assertThat(instance.canAuthenticate(), is(true)); - assertThat(instance.authenticate(), is(true)); - assertThat(instance.isAuthenticated(), is(true)); - } - - @Test - public void testFactoryAltUsername() throws Exception { - sshd = createPasswordAuthenticatedSshServer("bill"); - invoke(sshd, "start", null, null); - int port = (Integer)invoke(sshd, "getPort", null, null); - connection = new Connection("localhost", port); - connection.connect(new NoVerifier()); - SSHAuthenticator instance = SSHAuthenticator.newInstance(connection, user, null); - assertThat(instance.getAuthenticationMode(), is(SSHAuthenticator.Mode.AFTER_CONNECT)); - assertThat(instance.canAuthenticate(), is(true)); - assertThat(instance.authenticate(), is(false)); - assertThat(instance.isAuthenticated(), is(false)); - connection = new Connection("localhost", port); - connection.connect(new NoVerifier()); - instance = SSHAuthenticator.newInstance(connection, user, "bill"); - assertThat(instance.getAuthenticationMode(), is(SSHAuthenticator.Mode.AFTER_CONNECT)); - assertThat(instance.canAuthenticate(), is(true)); - assertThat(instance.authenticate(), is(true)); - assertThat(instance.isAuthenticated(), is(true)); - } - - /** - * Brings the {@link SSHAuthenticatorFactory} to a slave. - */ - @Test - public void testSlave() throws Exception { - Object sshd = createPasswordAuthenticatedSshServer(); - invoke(sshd, "start", null, null); - - DumbSlave s = r.createSlave(); - Computer c = s.toComputer(); - assertNotNull(c); - c.connect(false).get(); - - final int port = (Integer)invoke(sshd, "getPort", null, null); - - TaskListener l = r.createTaskListener(); - VirtualChannel channel = c.getChannel(); - assertNotNull(channel); - channel.call(new RemoteConnectionTest(port, user)); - } - - private static class NoVerifier implements ServerHostKeyVerifier { - public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, - byte[] serverHostKey) { - return true; - } - } - - private static final class RemoteConnectionTest extends MasterToSlaveCallable { - private final int port; - private final StandardUsernamePasswordCredentials user; - - public RemoteConnectionTest(int port, StandardUsernamePasswordCredentials user) { - this.port = port; - this.user = user; - } - - public Void call() throws Exception { - Connection connection = new Connection("localhost", port); - connection.connect(new NoVerifier()); - SSHAuthenticator instance = SSHAuthenticator.newInstance(connection,user); - - assertThat(instance.getAuthenticationMode(), is(SSHAuthenticator.Mode.AFTER_CONNECT)); - assertThat(instance.canAuthenticate(), is(true)); - instance.authenticateOrFail(); - assertThat(instance.isAuthenticated(), is(true)); - connection.close(); - return null; - } - - private static final long serialVersionUID = 1L; - } - - private Object invoke(Object target, String methodName, Class[] parameterTypes, Object[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - return target.getClass().getMethod(methodName, parameterTypes).invoke(target, args); - } - - private Object newDefaultSshServer() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - Object server = null; - Class serverClass; - try { - serverClass = Class.forName("org.apache.sshd.SshServer"); - } catch (ClassNotFoundException e) { - serverClass = Class.forName("org.apache.sshd.server.SshServer"); - } - - server = serverClass.getDeclaredMethod("setUpDefaultServer").invoke(null); - assertNotNull(server); - - return server; - } - - private Class newKeyPairProviderClass() throws ClassNotFoundException { - Class keyPairProviderClass; - try { - keyPairProviderClass = Class.forName("org.apache.sshd.common.KeyPairProvider"); - } catch (ClassNotFoundException e) { - keyPairProviderClass = Class.forName("org.apache.sshd.common.keyprovider.KeyPairProvider"); - } - - return keyPairProviderClass; - } - - private Object newProvider() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { - Class providerClass = Class.forName("org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider"); - Object provider = providerClass.getConstructor().newInstance(); - assertNotNull(provider); - - return provider; - } - - private Class newAuthenticatorClass() throws ClassNotFoundException { - Class authenticatorClass; - try { - authenticatorClass = Class.forName("org.apache.sshd.server.auth.password.PasswordAuthenticator"); - } catch(ClassNotFoundException e) { - authenticatorClass = Class.forName("org.apache.sshd.server.PasswordAuthenticator"); - } - - return authenticatorClass; - } - - private Object newAuthenticator(Class authenticatorClass, final String userName) throws IllegalArgumentException { - Object authenticator = Proxy.newProxyInstance( - authenticatorClass.getClassLoader(), new Class[]{authenticatorClass}, (proxy, method, args) -> - method.getName().equals("authenticate") ? - (userName == null || userName.equals(args[0])) && "foomanchu".equals(args[1]) : - null); - assertNotNull(authenticator); - return authenticator; - } - - private Object newFactory() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { - Object factory = null; - Class factoryClass; - try { - factoryClass = Class.forName("org.apache.sshd.server.auth.UserAuthPassword$Factory"); - } catch (ClassNotFoundException e) { - factoryClass = Class.forName("org.apache.sshd.server.auth.password.UserAuthPasswordFactory"); - } - - factory = factoryClass.getConstructor().newInstance(); - - assertNotNull(factory); - return factory; - } -} diff --git a/src/test/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/TrileadSSHPublicKeyAuthenticatorTest.java b/src/test/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/TrileadSSHPublicKeyAuthenticatorTest.java deleted file mode 100644 index 5094420..0000000 --- a/src/test/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/TrileadSSHPublicKeyAuthenticatorTest.java +++ /dev/null @@ -1,308 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2011-2012, CloudBees, Inc., Stephen Connolly. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.cloudbees.jenkins.plugins.sshcredentials.impl; - -import com.cloudbees.jenkins.plugins.sshcredentials.SSHAuthenticator; -import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey; -import com.cloudbees.plugins.credentials.CredentialsDescriptor; -import com.cloudbees.plugins.credentials.CredentialsScope; -import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials; -import com.trilead.ssh2.Connection; -import edu.umd.cs.findbugs.annotations.CheckForNull; -import edu.umd.cs.findbugs.annotations.NonNull; -import hudson.util.Secret; - -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.jvnet.hudson.test.JenkinsRule; - -import java.lang.reflect.InvocationTargetException; -import java.util.Collections; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static java.lang.reflect.Proxy.*; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertNotNull; - -public class TrileadSSHPublicKeyAuthenticatorTest { - - private Connection connection; - private SSHUserPrivateKey user; - - @Rule public JenkinsRule r = new JenkinsRule(); - - @After - public void tearDown() { - if (connection != null) { - connection.close(); - connection = null; - } - } - - @Before - public void setUp() { - user = new SSHUserPrivateKey() { - - @NonNull - public String getUsername() { - return "foobar"; - } - - @NonNull - public String getDescription() { - return ""; - } - - @NonNull - public String getId() { - return ""; - } - - public CredentialsScope getScope() { - return CredentialsScope.SYSTEM; - } - - @NonNull - public CredentialsDescriptor getDescriptor() { - return new CredentialsDescriptor() { - @NonNull - @Override - public String getDisplayName() { - return ""; - } - }; - } - - @CheckForNull - public Secret getPassphrase() { - return null; - } - - @NonNull - public List getPrivateKeys() { - // just want a valid key... I generated this and have thrown it away (other than here) - // do not use other than in this test - return List.of("-----BEGIN RSA PRIVATE KEY-----\n" - + "MIICWQIBAAKBgQDADDwooNPJNQB4N4bJPiBgq/rkWKMABApX0w4trSkkX5q+l+CL\n" - + "CuddGGAsAu6XPari8v49ipbBmHqRLP9+X3ARGWKU2gDvGTBr99/ReUl2YgVjCwy+\n" - + "KMrGCN7SNTgRo6StwVaPhh6pUpNTQciDe/kOwUnQFWSM6/lwkOD1Uod45wIBIwKB\n" - + "gHi3O8HELVnmzRhdaqphkLHLL/0/B18Ye4epPBy1/JqFPLJQ1kjFBnUIAe/HVCSN\n" - + "KZX30wIcmUZ9GdeYoJiTwsfTy9t2KwHjqrapTfiekVZAW+3iDBqRZMxQ5MoK7b6g\n" - + "w5HrrtrtPfYuAsBnYjIS6qsKAVT3vdolJ5eai/RlPO4LAkEA76YuUozC/dW7Ox+R\n" - + "1Njd6cWJsRVXGemkSYY/rSh0SbfHAebqL/bDg8xXim9UiuD9Hc6md3glHQj6iKvl\n" - + "BxWq4QJBAM0moKiM16WFSFJP1wVDj0Bnx6DkJYSpf5u+C0ghBVoqIYKq6/P/gRE2\n" - + "+ColsLu6AYftaEJVpAgxeTU/IsGoJMcCQHRmqMkCipiMYkFJ2R49cxnGWNJa0ojt\n" - + "03QrQ3/9tNNZQ2dS5sbW8UAEKoURgNW9vMVVvpHMpE/uaw8u65W6ESsCQDTAyjn4\n" - + "VLWIrDJsTTveLCaBFhNt3cMHA45ysnGiF1GzD+5mdzAdITBP9qvAjIgLQjjlRrH4\n" - + "w8eXsXQXjJgyjR0CQHfvhiMPG5pWwmXpsEOFo6GKSvOC/5sNEcnddenuO/2T7WWi\n" - + "o1LQh9naeuX8gti0vNR8+KtMEaIcJJeWnk56AVY=\n" - + "-----END RSA PRIVATE KEY-----\n"); - } - }; - } - - @Test - public void testAuthenticate() throws Exception { - Object sshd = newDefaultSshServer(); - Class keyPairProviderClass = newKeyPairProviderClass(); - Object provider = newProvider(); - Class authenticatorClass = newAuthenticatorClass(); - Object authenticator = newAuthenticator(authenticatorClass, "foobar"); - Object factory = newFactory(); - assertNotNull(factory); - - invoke(sshd, "setPort", new Class[] {Integer.TYPE}, new Object[] {0}); - invoke(sshd, "setKeyPairProvider", new Class[] {keyPairProviderClass}, new Object[] {provider}); - invoke(sshd, "setPublickeyAuthenticator", new Class[] {authenticatorClass}, new Object[] {authenticator}); - invoke(sshd, "setUserAuthFactories", new Class[] {List.class}, new Object[] {Collections.singletonList(factory)}); - - try { - invoke(sshd, "start", null, null); - int port = (Integer)invoke(sshd, "getPort", null, null); - connection = new Connection("localhost", port); - connection.connect((hostname, port1, serverHostKeyAlgorithm, serverHostKey) -> true); - TrileadSSHPublicKeyAuthenticator instance = - new TrileadSSHPublicKeyAuthenticator(connection, user); - assertThat(instance.getAuthenticationMode(), is(SSHAuthenticator.Mode.AFTER_CONNECT)); - assertThat(instance.canAuthenticate(), is(true)); - assertThat(instance.authenticate(), is(true)); - assertThat(instance.isAuthenticated(), is(true)); - } finally { - try { - invoke(sshd, "stop", new Class[] {Boolean.TYPE}, new Object[] {true}); - } catch (Throwable t) { - Logger.getLogger(getClass().getName()).log(Level.WARNING, "Problems shutting down ssh server", t); - } - } - } - - @Test - public void testFactory() throws Exception { - Object sshd = newDefaultSshServer(); - Class keyPairProviderClass = newKeyPairProviderClass(); - Object provider = newProvider(); - Class authenticatorClass = newAuthenticatorClass(); - Object authenticator = newAuthenticator(authenticatorClass, "foobar"); - Object factory = newFactory(); - assertNotNull(factory); - - invoke(sshd, "setPort", new Class[] {Integer.TYPE}, new Object[] {0}); - invoke(sshd, "setKeyPairProvider", new Class[] {keyPairProviderClass}, new Object[] {provider}); - invoke(sshd, "setPublickeyAuthenticator", new Class[] {authenticatorClass}, new Object[] {authenticator}); - invoke(sshd, "setUserAuthFactories", new Class[] {List.class}, new Object[] {Collections.singletonList(factory)}); - try { - invoke(sshd, "start", null, null); - int port = (Integer)invoke(sshd, "getPort", null, null); - connection = new Connection("localhost", port); - connection.connect((hostname, port1, serverHostKeyAlgorithm, serverHostKey) -> true); - SSHAuthenticator instance = SSHAuthenticator.newInstance(connection, user); - assertThat(instance.getAuthenticationMode(), is(SSHAuthenticator.Mode.AFTER_CONNECT)); - assertThat(instance.canAuthenticate(), is(true)); - assertThat(instance.authenticate(), is(true)); - assertThat(instance.isAuthenticated(), is(true)); - } finally { - try { - invoke(sshd, "stop", new Class[] {Boolean.TYPE}, new Object[] {true}); - } catch (Throwable t) { - Logger.getLogger(getClass().getName()).log(Level.WARNING, "Problems shutting down ssh server", t); - } - } - } - - @Test - public void testAltUsername() throws Exception { - Object sshd = newDefaultSshServer(); - Class keyPairProviderClass = newKeyPairProviderClass(); - Object provider = newProvider(); - Class authenticatorClass = newAuthenticatorClass(); - Object authenticator = newAuthenticator(authenticatorClass, "bill"); - Object factory = newFactory(); - - invoke(sshd, "setPort", new Class[] {Integer.TYPE}, new Object[] {0}); - invoke(sshd, "setKeyPairProvider", new Class[] {keyPairProviderClass}, new Object[] {provider}); - invoke(sshd, "setPublickeyAuthenticator", new Class[] {authenticatorClass}, new Object[] {authenticator}); - invoke(sshd, "setUserAuthFactories", new Class[] {List.class}, new Object[] {Collections.singletonList(factory)}); - try { - invoke(sshd, "start", null, null); - int port = (Integer)invoke(sshd, "getPort", null, null); - connection = new Connection("localhost", port); - connection.connect((hostname, port12, serverHostKeyAlgorithm, serverHostKey) -> true); - SSHAuthenticator instance = SSHAuthenticator.newInstance(connection, user, null); - assertThat(instance.getAuthenticationMode(), is(SSHAuthenticator.Mode.AFTER_CONNECT)); - assertThat(instance.canAuthenticate(), is(true)); - assertThat(instance.authenticate(), is(false)); - assertThat(instance.isAuthenticated(), is(false)); - connection = new Connection("localhost", port); - connection.connect((hostname, port1, serverHostKeyAlgorithm, serverHostKey) -> true); - instance = SSHAuthenticator.newInstance(connection, user, "bill"); - assertThat(instance.getAuthenticationMode(), is(SSHAuthenticator.Mode.AFTER_CONNECT)); - assertThat(instance.canAuthenticate(), is(true)); - assertThat(instance.authenticate(), is(true)); - assertThat(instance.isAuthenticated(), is(true)); - } finally { - try { - invoke(sshd, "stop", new Class[] {Boolean.TYPE}, new Object[] {true}); - } catch (Throwable t) { - Logger.getLogger(getClass().getName()).log(Level.WARNING, "Problems shutting down ssh server", t); - } - } - } - - private Object invoke(Object target, String methodName, Class[] parameterTypes, Object[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - return target.getClass().getMethod(methodName, parameterTypes).invoke(target, args); - } - - private Object newDefaultSshServer() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - Object sshd = null; - Class sshdClass; - try { - sshdClass = Class.forName("org.apache.sshd.SshServer"); - } catch (ClassNotFoundException e) { - sshdClass = Class.forName("org.apache.sshd.server.SshServer"); - } - - sshd = sshdClass.getDeclaredMethod("setUpDefaultServer").invoke(null); - assertNotNull(sshd); - - return sshd; - } - - private Class newKeyPairProviderClass() throws ClassNotFoundException { - Class keyPairProviderClass; - try { - keyPairProviderClass = Class.forName("org.apache.sshd.common.KeyPairProvider"); - } catch (ClassNotFoundException e) { - keyPairProviderClass = Class.forName("org.apache.sshd.common.keyprovider.KeyPairProvider"); - } - - return keyPairProviderClass; - } - - private Object newProvider() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { - Class providerClass = Class.forName("org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider"); - Object provider = providerClass.getConstructor().newInstance(); - assertNotNull(provider); - - return provider; - } - - private Class newAuthenticatorClass() throws ClassNotFoundException { - Class authenticatorClass; - try { - authenticatorClass = Class.forName("org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator"); - } catch(ClassNotFoundException e) { - authenticatorClass = Class.forName("org.apache.sshd.server.PublickeyAuthenticator"); - } - - return authenticatorClass; - } - - private Object newAuthenticator(Class authenticatorClass, final String userName) throws IllegalArgumentException { - Object authenticator = newProxyInstance( - authenticatorClass.getClassLoader(), new Class[]{authenticatorClass}, - (proxy, method, args) -> method.getName().equals("authenticate") ? userName.equals(args[0]) : null); - assertNotNull(authenticator); - return authenticator; - } - - private Object newFactory() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { - Object factory = null; - Class factoryClass; - try { - factoryClass = Class.forName("org.apache.sshd.server.auth.UserAuthPublicKey$Factory"); - } catch (ClassNotFoundException e) { - factoryClass = Class.forName("org.apache.sshd.server.auth.pubkey.UserAuthPublicKeyFactory"); - } - - factory = factoryClass.getConstructor().newInstance(); - - assertNotNull(factory); - return factory; - } -}