You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2015/12/14 10:14:06 UTC
[2/4] mina-sshd git commit: [SSHD-613] Support a 'callback' method
for session password identities
[SSHD-613] Support a 'callback' method for session password identities
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/13818de0
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/13818de0
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/13818de0
Branch: refs/heads/master
Commit: 13818de0c58decf8bf7e04f47517d35f9e6eb1c9
Parents: 1539faa
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Mon Dec 14 10:47:46 2015 +0200
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Mon Dec 14 10:47:46 2015 +0200
----------------------------------------------------------------------
pom.xml | 2 +-
.../client/ClientAuthenticationManager.java | 41 +++-
.../java/org/apache/sshd/client/SshClient.java | 144 +++++++++++--
.../sshd/client/auth/AbstractUserAuth.java | 4 +-
.../auth/AuthenticationIdentitiesProvider.java | 141 +++++++++++++
.../client/auth/PasswordIdentityProvider.java | 146 ++++++++++++++
.../org/apache/sshd/client/auth/UserAuth.java | 5 +-
.../auth/UserAuthKeyboardInteractive.java | 16 +-
.../sshd/client/auth/UserAuthPassword.java | 41 ++--
.../sshd/client/auth/UserAuthPublicKey.java | 100 +++------
.../client/auth/pubkey/KeyAgentIdentity.java | 19 +-
.../client/auth/pubkey/KeyPairIdentity.java | 10 +-
.../auth/pubkey/SessionKeyPairIterator.java | 60 ++++++
.../auth/pubkey/SshAgentPublicKeyIterator.java | 73 +++++++
.../auth/pubkey/UserAuthPublicKeyIterator.java | 147 ++++++++++++++
.../sshd/client/session/ClientSession.java | 30 +--
.../sshd/client/session/ClientSessionImpl.java | 16 +-
.../client/session/ClientUserAuthService.java | 9 +-
.../common/keyprovider/KeyIdentityProvider.java | 149 ++++++++++++++
.../common/keyprovider/KeyPairProvider.java | 9 +-
.../apache/sshd/common/util/GenericUtils.java | 73 +++++++
.../server/session/ServerUserAuthService.java | 3 +-
.../client/ClientAuthenticationManagerTest.java | 201 +++++++++++++++++++
.../java/org/apache/sshd/client/ClientTest.java | 4 +-
.../auth/PasswordIdentityProviderTest.java | 69 +++++++
.../BuiltinClientIdentitiesWatcherTest.java | 3 +-
.../client/config/keys/ClientIdentityTest.java | 4 +-
.../client/session/ClientSessionImplTest.java | 144 -------------
.../sshd/common/auth/AuthenticationTest.java | 153 +++++++++-----
.../java/org/apache/sshd/util/test/Utils.java | 5 +-
30 files changed, 1435 insertions(+), 386 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 18697cf..d1fe10e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -530,7 +530,7 @@
<!-- Checks for Size Violations. -->
<!-- See http://checkstyle.sf.net/config_sizes.html -->
<module name="AnonInnerLength">
- <property name="max" value="40" />
+ <property name="max" value="50" />
</module>
<module name="ExecutableStatementCount">
<property name="max" value="100" />
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java b/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java
index de41b9a..ab1f154 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java
@@ -19,18 +19,22 @@
package org.apache.sshd.client;
+import java.security.KeyPair;
import java.util.List;
+import org.apache.sshd.client.auth.AuthenticationIdentitiesProvider;
+import org.apache.sshd.client.auth.PasswordIdentityProvider;
import org.apache.sshd.client.auth.UserAuth;
import org.apache.sshd.client.auth.UserInteraction;
import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.keyprovider.KeyPairProviderHolder;
/**
* Holds information required for the client to perform authentication with the server
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public interface ClientAuthenticationManager {
+public interface ClientAuthenticationManager extends KeyPairProviderHolder {
/**
* Ordered comma separated list of authentications methods.
@@ -51,6 +55,41 @@ public interface ClientAuthenticationManager {
*/
int DEFAULT_PASSWORD_PROMPTS = 3;
+ AuthenticationIdentitiesProvider getRegisteredIdentities();
+
+ PasswordIdentityProvider getPasswordIdentityProvider();
+ void setPasswordIdentityProvider(PasswordIdentityProvider provider);
+
+ /**
+ * @param password Password to be added - may not be {@code null}/empty.
+ * <B>Note:</B> this password is <U>in addition</U> to whatever passwords
+ * are available via the {@link PasswordIdentityProvider} (if any)
+ */
+ void addPasswordIdentity(String password);
+
+ /**
+ * @param password The password to remove - ignored if {@code null}/empty
+ * @return The removed password - same one that was added via
+ * {@link #addPasswordIdentity(String)} - or {@code null} if no
+ * match found
+ */
+ String removePasswordIdentity(String password);
+
+ /**
+ * @param key The {@link KeyPair} to add - may not be {@code null}
+ * <B>Note:</B> this key is <U>in addition</U> to whatever keys
+ * are available via the {@link org.apache.sshd.common.keyprovider.KeyIdentityProvider} (if any)
+ */
+ void addPublicKeyIdentity(KeyPair key);
+
+ /**
+ * @param kp The {@link KeyPair} to remove - ignored if {@code null}
+ * @return The removed {@link KeyPair} - same one that was added via
+ * {@link #addPublicKeyIdentity(KeyPair)} - or {@code null} if no
+ * match found
+ */
+ KeyPair removePublicKeyIdentity(KeyPair kp);
+
/**
* Retrieve the server key verifier to be used to check the key when connecting
* to an SSH server.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
index 9081d82..e64b4cb 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
@@ -38,9 +38,11 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.ConsoleHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
@@ -49,6 +51,8 @@ import java.util.logging.LogRecord;
import java.util.logging.Logger;
import org.apache.sshd.agent.SshAgentFactory;
+import org.apache.sshd.client.auth.AuthenticationIdentitiesProvider;
+import org.apache.sshd.client.auth.PasswordIdentityProvider;
import org.apache.sshd.client.auth.UserAuth;
import org.apache.sshd.client.auth.UserAuthKeyboardInteractiveFactory;
import org.apache.sshd.client.auth.UserAuthPasswordFactory;
@@ -178,9 +182,12 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
private HostConfigEntryResolver hostConfigEntryResolver;
private ClientIdentityLoader clientIdentityLoader;
private FilePasswordProvider filePasswordProvider;
+ private PasswordIdentityProvider passwordIdentityProvider;
+ private final List<Object> identities = new CopyOnWriteArrayList<>();
+ private final AuthenticationIdentitiesProvider identitiesProvider;
public SshClient() {
- super();
+ identitiesProvider = AuthenticationIdentitiesProvider.Utils.wrap(identities);
}
public SessionFactory getSessionFactory() {
@@ -252,6 +259,72 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
}
@Override
+ public AuthenticationIdentitiesProvider getRegisteredIdentities() {
+ return identitiesProvider;
+ }
+
+ @Override
+ public PasswordIdentityProvider getPasswordIdentityProvider() {
+ return passwordIdentityProvider;
+ }
+
+ @Override
+ public void setPasswordIdentityProvider(PasswordIdentityProvider provider) {
+ passwordIdentityProvider = provider;
+ }
+
+ @Override
+ public void addPasswordIdentity(String password) {
+ identities.add(ValidateUtils.checkNotNullAndNotEmpty(password, "No password provided"));
+ if (log.isDebugEnabled()) { // don't show the password in the log
+ log.debug("addPasswordIdentity({}) {}", this, KeyUtils.getFingerPrint(password));
+ }
+ }
+
+ @Override
+ public String removePasswordIdentity(String password) {
+ if (GenericUtils.isEmpty(password)) {
+ return null;
+ }
+
+ int index = AuthenticationIdentitiesProvider.Utils.findIdentityIndex(
+ identities, AuthenticationIdentitiesProvider.Utils.PASSWORD_IDENTITY_COMPARATOR, password);
+ if (index >= 0) {
+ return (String) identities.remove(index);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void addPublicKeyIdentity(KeyPair kp) {
+ ValidateUtils.checkNotNull(kp, "No key-pair to add");
+ ValidateUtils.checkNotNull(kp.getPublic(), "No public key");
+ ValidateUtils.checkNotNull(kp.getPrivate(), "No private key");
+
+ identities.add(kp);
+
+ if (log.isDebugEnabled()) {
+ log.debug("addPublicKeyIdentity({}) {}", this, KeyUtils.getFingerPrint(kp.getPublic()));
+ }
+ }
+
+ @Override
+ public KeyPair removePublicKeyIdentity(KeyPair kp) {
+ if (kp == null) {
+ return null;
+ }
+
+ int index = AuthenticationIdentitiesProvider.Utils.findIdentityIndex(
+ identities, AuthenticationIdentitiesProvider.Utils.KEYPAIR_IDENTITY_COMPARATOR, kp);
+ if (index >= 0) {
+ return (KeyPair) identities.remove(index);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
protected void checkConfig() {
super.checkConfig();
@@ -501,29 +574,18 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
session.setUsername(username);
if (useDefaultIdentities) {
- // check if session listener intervened
- KeyPairProvider kpSession = session.getKeyPairProvider();
- KeyPairProvider kpClient = ValidateUtils.checkNotNull(getKeyPairProvider(), "No default key-pair provider");
- if (kpSession == null) {
- session.setKeyPairProvider(kpClient);
- } else {
- if (kpSession != kpClient) {
- if (log.isDebugEnabled()) {
- log.debug("onConnectOperationComplete({}) key-pair provider override", session);
- }
- }
- }
+ setupDefaultSessionIdentities(session);
}
int numIds = GenericUtils.size(identities);
if (numIds > 0) {
if (log.isDebugEnabled()) {
- log.debug("onConnectOperationComplete({}@{}) adding {} identities", username, address, numIds);
+ log.debug("onConnectOperationComplete({}) adding {} identities", session, numIds);
}
for (KeyPair kp : identities) {
if (log.isTraceEnabled()) {
- log.trace("onConnectOperationComplete({}@{}) add identity type={}, fingerprint={}",
- username, address, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint(kp.getPublic()));
+ log.trace("onConnectOperationComplete({}) add identity type={}, fingerprint={}",
+ session, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint(kp.getPublic()));
}
session.addPublicKeyIdentity(kp);
}
@@ -532,6 +594,56 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
connectFuture.setSession(session);
}
+ protected void setupDefaultSessionIdentities(ClientSession session) {
+ // check if session listener intervened
+ KeyPairProvider kpSession = session.getKeyPairProvider();
+ KeyPairProvider kpClient = getKeyPairProvider();
+ if (kpSession == null) {
+ session.setKeyPairProvider(kpClient);
+ } else {
+ if (kpSession != kpClient) {
+ if (log.isDebugEnabled()) {
+ log.debug("setupDefaultSessionIdentities({}) key-pair provider override", session);
+ }
+ }
+ }
+
+ PasswordIdentityProvider passSession = session.getPasswordIdentityProvider();
+ PasswordIdentityProvider passClient = getPasswordIdentityProvider();
+ if (passSession == null) {
+ session.setPasswordIdentityProvider(passClient);
+ } else {
+ if (passSession != passClient) {
+ if (log.isDebugEnabled()) {
+ log.debug("setupDefaultSessionIdentities({}) password provider override", session);
+ }
+ }
+ }
+
+ AuthenticationIdentitiesProvider idsClient = getRegisteredIdentities();
+ for (Iterator<?> iter = GenericUtils.iteratorOf((idsClient == null) ? null : idsClient.loadIdentities()); iter.hasNext();) {
+ Object id = iter.next();
+ if (id instanceof String) {
+ if (log.isTraceEnabled()) {
+ log.trace("setupDefaultSessionIdentities({}) add password fingerprint={}",
+ session, KeyUtils.getFingerPrint(id.toString()));
+ }
+ session.addPasswordIdentity((String) id);
+ } else if (id instanceof KeyPair) {
+ KeyPair kp = (KeyPair) id;
+ if (log.isTraceEnabled()) {
+ log.trace("setupDefaultSessionIdentities({}) add identity type={}, fingerprint={}",
+ session, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint(kp.getPublic()));
+ }
+ session.addPublicKeyIdentity(kp);
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug("setupDefaultSessionIdentities({}) ignored identity={}", session, id);
+ }
+ }
+ }
+ }
+
protected IoConnector createConnector() {
return getIoServiceFactory().createConnector(getSessionFactory());
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/client/auth/AbstractUserAuth.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/AbstractUserAuth.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/AbstractUserAuth.java
index 7cc3348..9b22eb7 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/AbstractUserAuth.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/AbstractUserAuth.java
@@ -19,8 +19,6 @@
package org.apache.sshd.client.auth;
-import java.util.Collection;
-
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.buffer.Buffer;
@@ -58,7 +56,7 @@ public abstract class AbstractUserAuth extends AbstractLoggingBean implements Us
}
@Override
- public void init(ClientSession session, String service, Collection<?> identities) throws Exception {
+ public void init(ClientSession session, String service) throws Exception {
this.clientSession = ValidateUtils.checkNotNull(session, "No client session");
this.service = ValidateUtils.checkNotNullAndNotEmpty(service, "No service");
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
new file mode 100644
index 0000000..2d0637b
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.auth;
+
+import java.security.KeyPair;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface AuthenticationIdentitiesProvider extends KeyIdentityProvider, PasswordIdentityProvider {
+ /**
+ * @return All the currently available identities - passwords, keys, etc...
+ */
+ Iterable<?> loadIdentities();
+
+ /**
+ * A helper class for identity provider related operations
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+ // CHECKSTYLE:OFF
+ final class Utils {
+ // CHECKSTYLE:ON
+ /**
+ * Compares 2 password identities - returns zero ONLY if <U>both</U> compared
+ * objects are {@link String}s and equal to each other
+ */
+ public static final Comparator<Object> PASSWORD_IDENTITY_COMPARATOR = new Comparator<Object>() {
+ @Override
+ public int compare(Object o1, Object o2) {
+ if (!(o1 instanceof String) || !(o2 instanceof String)) {
+ return -1;
+ } else {
+ return ((String) o1).compareTo((String) o2);
+ }
+ }
+ };
+
+ /**
+ * Compares 2 {@link KeyPair} identities - returns zero ONLY if <U>both</U> compared
+ * objects are {@link KeyPair}s and equal to each other
+ */
+ public static final Comparator<Object> KEYPAIR_IDENTITY_COMPARATOR = new Comparator<Object>() {
+ @Override
+ public int compare(Object o1, Object o2) {
+ if ((!(o1 instanceof KeyPair)) || (!(o2 instanceof KeyPair))) {
+ return -1;
+ } else if (KeyUtils.compareKeyPairs((KeyPair) o1, (KeyPair) o2)) {
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+ };
+
+ private Utils() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+
+ public static int findIdentityIndex(List<?> identities, Comparator<? super Object> comp, Object target) {
+ for (int index = 0; index < identities.size(); index++) {
+ Object value = identities.get(index);
+ if (comp.compare(value, target) == 0) {
+ return index;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * @param identities The {@link Iterable} identities - OK if {@code null}/empty
+ * @return An {@link AuthenticationIdentitiesProvider} wrapping the identities
+ */
+ public static AuthenticationIdentitiesProvider wrap(final Iterable<?> identities) {
+ return new AuthenticationIdentitiesProvider() {
+ @Override
+ public Iterable<KeyPair> loadKeys() {
+ return selectIdentities(KeyPair.class);
+ }
+
+ @Override
+ public Iterable<String> loadPasswords() {
+ return selectIdentities(String.class);
+ }
+
+ @Override
+ public Iterable<?> loadIdentities() {
+ return selectIdentities(Object.class);
+ }
+
+ // NOTE: returns a NEW Collection on every call so that the original
+ // identities remain unchanged
+ private <T> Collection<T> selectIdentities(Class<T> type) {
+ Collection<T> matches = null;
+ for (Iterator<?> iter = GenericUtils.iteratorOf(identities); iter.hasNext();) {
+ Object o = iter.next();
+ Class<?> t = o.getClass();
+ if (!type.isAssignableFrom(t)) {
+ continue;
+ }
+
+ if (matches == null) {
+ matches = new LinkedList<T>();
+ }
+
+ matches.add(type.cast(o));
+ }
+
+ return (matches == null) ? Collections.<T>emptyList() : matches;
+ }
+ };
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/client/auth/PasswordIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/PasswordIdentityProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/PasswordIdentityProvider.java
new file mode 100644
index 0000000..26811db
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/PasswordIdentityProvider.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.auth;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.Supplier;
+import org.apache.sshd.common.util.Transformer;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface PasswordIdentityProvider {
+ PasswordIdentityProvider EMPTY_PASSWORDS_PROVIDER = new PasswordIdentityProvider() {
+ @Override
+ public Iterable<String> loadPasswords() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public String toString() {
+ return "EMPTY";
+ }
+ };
+
+ /**
+ * @return The currently available passwords - never {@code null}
+ */
+ Iterable<String> loadPasswords();
+
+ /**
+ * A helper class for password identity provider related operations
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+ // CHECKSTYLE:OFF
+ final class Utils {
+ // CHECKSTYLE:ON
+ public static final Transformer<PasswordIdentityProvider, Iterable<String>> LOADER =
+ new Transformer<PasswordIdentityProvider, Iterable<String>>() {
+ @Override
+ public Iterable<String> transform(PasswordIdentityProvider p) {
+ return (p == null) ? null : p.loadPasswords();
+ }
+ };
+
+ private Utils() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+
+ public static Iterator<String> iteratorOf(ClientSession session) {
+ ValidateUtils.checkNotNull(session, "No session");
+ return iteratorOf(session.getRegisteredIdentities(), session.getPasswordIdentityProvider());
+ }
+
+ public static Iterator<String> iteratorOf(PasswordIdentityProvider identities, PasswordIdentityProvider passwords) {
+ return iteratorOf(resolvePasswordIdentityProvider(identities, passwords));
+ }
+
+ /**
+ * Resolves a non-{@code null} iterator of the available passwords
+ *
+ * @param provider The {@link PasswordIdentityProvider} - ignored if {@code null}
+ * @return A non-{@code null} iterator - which may be empty if no provider or no passwords
+ */
+ public static Iterator<String> iteratorOf(PasswordIdentityProvider provider) {
+ return GenericUtils.iteratorOf((provider == null) ? null : provider.loadPasswords());
+ }
+
+ public static PasswordIdentityProvider resolvePasswordIdentityProvider(PasswordIdentityProvider identities, PasswordIdentityProvider passwords) {
+ if ((passwords == null) || (identities == passwords)) {
+ return identities;
+ } else if (identities == null) {
+ return passwords;
+ } else {
+ return multiProvider(identities, passwords);
+ }
+ }
+
+ public static PasswordIdentityProvider multiProvider(PasswordIdentityProvider ... providers) {
+ return multiProvider(GenericUtils.isEmpty(providers) ? Collections.<PasswordIdentityProvider>emptyList() : Arrays.asList(providers));
+ }
+
+ public static PasswordIdentityProvider multiProvider(Collection<? extends PasswordIdentityProvider> providers) {
+ return wrap(iterableOf(providers));
+ }
+
+ public static Iterable<String> iterableOf(Collection<? extends PasswordIdentityProvider> providers) {
+ if (GenericUtils.isEmpty(providers)) {
+ return Collections.emptyList();
+ }
+
+ Collection<Supplier<Iterable<String>>> suppliers = new ArrayList<Supplier<Iterable<String>>>(providers.size());
+ for (final PasswordIdentityProvider p : providers) {
+ if (p == null) {
+ continue;
+ }
+
+ suppliers.add(new Supplier<Iterable<String>>() {
+ @Override
+ public Iterable<String> get() {
+ return p.loadPasswords();
+ }
+ });
+ }
+
+ if (GenericUtils.isEmpty(suppliers)) {
+ return Collections.emptyList();
+ }
+
+ return GenericUtils.multiIterableSuppliers(suppliers);
+ }
+
+ public static PasswordIdentityProvider wrap(final Iterable<String> passwords) {
+ return new PasswordIdentityProvider() {
+ @Override
+ public Iterable<String> loadPasswords() {
+ return passwords;
+ }
+ };
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuth.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuth.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuth.java
index 44a111a..633e761 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuth.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuth.java
@@ -18,8 +18,6 @@
*/
package org.apache.sshd.client.auth;
-import java.util.Collection;
-
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.client.session.ClientSessionHolder;
import org.apache.sshd.common.auth.UserAuthInstance;
@@ -34,10 +32,9 @@ public interface UserAuth extends ClientSessionHolder, UserAuthInstance<ClientSe
/**
* @param session The {@link ClientSession}
* @param service The requesting service name
- * @param identities The currently available identities - e.g., password, keys, etc.
* @throws Exception If failed to initialize the mechanism
*/
- void init(ClientSession session, String service, Collection<?> identities) throws Exception;
+ void init(ClientSession session, String service) throws Exception;
/**
* @param buffer The {@link Buffer} to process - {@code null} if not a response buffer,
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java
index 1863379..9327236 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java
@@ -18,11 +18,8 @@
*/
package org.apache.sshd.client.auth;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Iterator;
-import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -87,16 +84,9 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
}
@Override
- public void init(ClientSession session, String service, Collection<?> identities) throws Exception {
- super.init(session, service, identities);
-
- List<String> pwds = new ArrayList<>();
- for (Object o : identities) {
- if (o instanceof String) {
- pwds.add((String) o);
- }
- }
- passwords = pwds.iterator();
+ public void init(ClientSession session, String service) throws Exception {
+ super.init(session, service);
+ passwords = PasswordIdentityProvider.Utils.iteratorOf(session);
maxTrials = PropertyResolverUtils.getIntProperty(session, ClientAuthenticationManager.PASSWORD_PROMPTS, ClientAuthenticationManager.DEFAULT_PASSWORD_PROMPTS);
ValidateUtils.checkTrue(maxTrials > 0, "Non-positive max. trials: %d", maxTrials);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPassword.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPassword.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPassword.java
index 449d10a..d4ed6f7 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPassword.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPassword.java
@@ -19,10 +19,7 @@
package org.apache.sshd.client.auth;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.Iterator;
-import java.util.List;
import java.util.Objects;
import org.apache.sshd.client.session.ClientSession;
@@ -32,8 +29,7 @@ import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.BufferUtils;
/**
- * TODO Add javadoc
- *
+ * Implements the "password" authentication mechanism
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public class UserAuthPassword extends AbstractUserAuth {
@@ -47,34 +43,27 @@ public class UserAuthPassword extends AbstractUserAuth {
}
@Override
- public void init(ClientSession session, String service, Collection<?> identities) throws Exception {
- super.init(session, service, identities);
-
- List<String> pwds = new ArrayList<>();
- for (Object o : identities) {
- if (o instanceof String) {
- pwds.add((String) o);
- }
- }
- this.passwords = pwds.iterator();
+ public void init(ClientSession session, String service) throws Exception {
+ super.init(session, service);
+ passwords = PasswordIdentityProvider.Utils.iteratorOf(session);
}
@Override
protected boolean sendAuthDataRequest(ClientSession session, String service) throws Exception {
- if (passwords.hasNext()) {
- current = passwords.next();
- String username = session.getUsername();
- Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST,
- username.length() + service.length() + getName().length() + current.length() + Integer.SIZE);
- sendPassword(buffer, session, current, current);
- return true;
- }
+ if ((passwords == null) || (!passwords.hasNext())) {
+ if (log.isDebugEnabled()) {
+ log.debug("sendAuthDataRequest({})[{}] no more passwords to send", session, service);
+ }
- if (log.isDebugEnabled()) {
- log.debug("sendAuthDataRequest({})[{}] no more passwords to send", session, service);
+ return false;
}
- return false;
+ current = passwords.next();
+ String username = session.getUsername();
+ Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST,
+ username.length() + service.length() + getName().length() + current.length() + Integer.SIZE);
+ sendPassword(buffer, session, current, current);
+ return true;
}
@Override
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java
index 8cbfddf..991c0d7 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java
@@ -18,41 +18,27 @@
*/
package org.apache.sshd.client.auth;
+import java.io.Closeable;
import java.io.IOException;
-import java.security.KeyPair;
import java.security.PublicKey;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.Iterator;
-import java.util.List;
-
-import org.apache.sshd.agent.SshAgent;
-import org.apache.sshd.agent.SshAgentFactory;
-import org.apache.sshd.client.auth.pubkey.KeyAgentIdentity;
-import org.apache.sshd.client.auth.pubkey.KeyPairIdentity;
import org.apache.sshd.client.auth.pubkey.PublicKeyIdentity;
+import org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyIterator;
import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.FactoryManager;
import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.kex.KeyExchange;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.Pair;
-import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.BufferUtils;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
/**
- * TODO Add javadoc
- *
+ * Implements the "publickey" authentication mechanism
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public class UserAuthPublicKey extends AbstractUserAuth {
public static final String NAME = UserAuthPublicKeyFactory.NAME;
- private SshAgent agent;
private Iterator<PublicKeyIdentity> keys;
private PublicKeyIdentity current;
@@ -61,51 +47,15 @@ public class UserAuthPublicKey extends AbstractUserAuth {
}
@Override
- public void init(ClientSession session, String service, Collection<?> identities) throws Exception {
- super.init(session, service, identities);
-
- List<PublicKeyIdentity> ids = new ArrayList<>();
- for (Object o : identities) {
- if (o instanceof KeyPair) {
- ids.add(new KeyPairIdentity(session, (KeyPair) o));
- }
- }
-
- FactoryManager manager = ValidateUtils.checkNotNull(session.getFactoryManager(), "No session factory manager");
- SshAgentFactory factory = manager.getAgentFactory();
- if (factory != null) {
- this.agent = ValidateUtils.checkNotNull(factory.createClient(manager), "No agent created");
- Collection<Pair<PublicKey, String>> agentKeys = agent.getIdentities();
- if (GenericUtils.size(agentKeys) > 0) {
- for (Pair<PublicKey, String> pair : agentKeys) {
- PublicKey key = pair.getFirst();
- if (log.isDebugEnabled()) {
- log.debug("init({}) add agent public key type={}: comment={}, fingerprint={}",
- session, pair.getSecond(), KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
- }
- ids.add(new KeyAgentIdentity(agent, key));
- }
- }
- } else {
- this.agent = null;
- }
-
- KeyPairProvider provider = session.getKeyPairProvider();
- if (provider != null) {
- for (KeyPair kp : provider.loadKeys()) {
- if (log.isDebugEnabled()) {
- log.debug("init({}) add provider public key type={}: {}",
- session, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint(kp.getPublic()));
- }
- ids.add(new KeyPairIdentity(session, kp));
- }
- }
- this.keys = ids.iterator();
+ public void init(ClientSession session, String service) throws Exception {
+ super.init(session, service);
+ releaseKeys(); // just making sure in case multiple calls to the method
+ keys = new UserAuthPublicKeyIterator(session);
}
@Override
protected boolean sendAuthDataRequest(ClientSession session, String service) throws Exception {
- if (!keys.hasNext()) {
+ if ((keys == null) || (!keys.hasNext())) {
if (log.isDebugEnabled()) {
log.debug("sendAuthDataRequest({})[{}] no more keys to send", session, service);
}
@@ -114,6 +64,10 @@ public class UserAuthPublicKey extends AbstractUserAuth {
}
current = keys.next();
+ if (log.isTraceEnabled()) {
+ log.trace("sendAuthDataRequest({})[{}] current key details: {}", session, service, current);
+ }
+
PublicKey key = current.getPublicKey();
String algo = KeyUtils.getKeyType(key);
String name = getName();
@@ -121,7 +75,6 @@ public class UserAuthPublicKey extends AbstractUserAuth {
log.debug("sendAuthDataRequest({})[{}] send SSH_MSG_USERAUTH_REQUEST request {} type={} - fingerprint={}",
session, service, name, algo, KeyUtils.getFingerPrint(key));
}
-
Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST);
buffer.putString(session.getUsername());
buffer.putString(service);
@@ -145,7 +98,7 @@ public class UserAuthPublicKey extends AbstractUserAuth {
String algo = KeyUtils.getKeyType(key);
String name = getName();
if (log.isDebugEnabled()) {
- log.debug("processAuthDataRequest({})[{}] send SSH_MSG_USERAUTH_PK_OK reply for {}: type={}, fingerprint={}",
+ log.debug("processAuthDataRequest({})[{}] send SSH_MSG_USERAUTH_PK_OK reply {} type={} - fingerprint={}",
session, service, name, algo, KeyUtils.getFingerPrint(key));
}
@@ -181,16 +134,25 @@ public class UserAuthPublicKey extends AbstractUserAuth {
@Override
public void destroy() {
- if (agent != null) {
- try {
- agent.close();
- } catch (IOException e) {
- throw new RuntimeException("Failed (" + e.getClass().getSimpleName() + ") to close agent: " + e.getMessage(), e);
- } finally {
- agent = null;
- }
+ try {
+ releaseKeys();
+ } catch (IOException e) {
+ throw new RuntimeException("Failed (" + e.getClass().getSimpleName() + ") to close agent: " + e.getMessage(), e);
+ }
+
+ super.destroy(); // for logging
+ }
- super.destroy(); // for logging
+ protected void releaseKeys() throws IOException {
+ try {
+ if (keys instanceof Closeable) {
+ if (log.isTraceEnabled()) {
+ log.trace("releaseKeys({}) closing {}", getClientSession(), keys);
+ }
+ ((Closeable) keys).close();
+ }
+ } finally {
+ keys = null;
}
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyAgentIdentity.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyAgentIdentity.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyAgentIdentity.java
index e8f09a3..a583e39 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyAgentIdentity.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyAgentIdentity.java
@@ -21,6 +21,7 @@ package org.apache.sshd.client.auth.pubkey;
import java.security.PublicKey;
import org.apache.sshd.agent.SshAgent;
+import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.util.ValidateUtils;
/**
@@ -31,10 +32,12 @@ import org.apache.sshd.common.util.ValidateUtils;
public class KeyAgentIdentity implements PublicKeyIdentity {
private final SshAgent agent;
private final PublicKey key;
+ private final String comment;
- public KeyAgentIdentity(SshAgent agent, PublicKey key) {
+ public KeyAgentIdentity(SshAgent agent, PublicKey key, String comment) {
this.agent = ValidateUtils.checkNotNull(agent, "No signing agent");
this.key = ValidateUtils.checkNotNull(key, "No public key");
+ this.comment = comment;
}
@Override
@@ -42,8 +45,20 @@ public class KeyAgentIdentity implements PublicKeyIdentity {
return key;
}
+ public String getComment() {
+ return comment;
+ }
+
@Override
public byte[] sign(byte[] data) throws Exception {
- return agent.sign(key, data);
+ return agent.sign(getPublicKey(), data);
+ }
+
+ @Override
+ public String toString() {
+ PublicKey pubKey = getPublicKey();
+ return getClass().getSimpleName() + "[" + KeyUtils.getKeyType(pubKey) + "]"
+ + " fingerprint=" + KeyUtils.getFingerPrint(pubKey)
+ + ", comment=" + getComment();
}
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java
index e84195f..f24b65f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java
@@ -48,7 +48,7 @@ public class KeyPairIdentity implements PublicKeyIdentity {
@Override
public byte[] sign(byte[] data) throws Exception {
- String keyType = KeyUtils.getKeyType(pair);
+ String keyType = KeyUtils.getKeyType(getPublicKey());
Signature verifier = ValidateUtils.checkNotNull(
NamedFactory.Utils.create(manager.getSignatureFactories(), keyType),
"No signer could be located for key type=%s",
@@ -57,4 +57,12 @@ public class KeyPairIdentity implements PublicKeyIdentity {
verifier.update(data, 0, data.length);
return verifier.sign();
}
+
+ @Override
+ public String toString() {
+ PublicKey pubKey = getPublicKey();
+ return getClass().getSimpleName() + "[" + manager + "]"
+ + " type=" + KeyUtils.getKeyType(pubKey)
+ + ", fingerprint=" + KeyUtils.getFingerPrint(pubKey);
+ }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/SessionKeyPairIterator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/SessionKeyPairIterator.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/SessionKeyPairIterator.java
new file mode 100644
index 0000000..5ff50b4
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/SessionKeyPairIterator.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.auth.pubkey;
+
+import java.security.KeyPair;
+import java.util.Iterator;
+
+import org.apache.sshd.common.kex.KexFactoryManager;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SessionKeyPairIterator implements Iterator<KeyPairIdentity> {
+
+ private final KexFactoryManager manager;
+ private final Iterator<KeyPair> keys;
+
+ public SessionKeyPairIterator(KexFactoryManager manager, Iterator<KeyPair> keys) {
+ this.manager = ValidateUtils.checkNotNull(manager, "No KEX factory manager");
+ this.keys = keys; // OK if null
+ }
+
+ @Override
+ public boolean hasNext() {
+ return (keys != null) && keys.hasNext();
+ }
+
+ @Override
+ public KeyPairIdentity next() {
+ return new KeyPairIdentity(manager, keys.next());
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("No removal allowed");
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[" + manager + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/SshAgentPublicKeyIterator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/SshAgentPublicKeyIterator.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/SshAgentPublicKeyIterator.java
new file mode 100644
index 0000000..8becc4b
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/SshAgentPublicKeyIterator.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.auth.pubkey;
+
+import java.io.IOException;
+import java.security.PublicKey;
+import java.util.Iterator;
+
+import org.apache.sshd.agent.SshAgent;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.client.session.ClientSessionHolder;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.Pair;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SshAgentPublicKeyIterator implements Iterator<KeyAgentIdentity>, ClientSessionHolder {
+ private final ClientSession clientSession;
+ private final SshAgent agent;
+ private final Iterator<Pair<PublicKey, String>> keys;
+
+ public SshAgentPublicKeyIterator(ClientSession session, SshAgent agent) throws IOException {
+ this.clientSession = ValidateUtils.checkNotNull(session, "No session");
+ this.agent = ValidateUtils.checkNotNull(agent, "No agent");
+ keys = GenericUtils.iteratorOf(agent.getIdentities());
+ }
+
+ @Override
+ public ClientSession getClientSession() {
+ return clientSession;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return (keys != null) && keys.hasNext();
+ }
+
+ @Override
+ public KeyAgentIdentity next() {
+ Pair<PublicKey, String> kp = keys.next();
+ return new KeyAgentIdentity(agent, kp.getFirst(), kp.getSecond());
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("No removal allowed");
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[" + getClientSession() + "]";
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
new file mode 100644
index 0000000..83ed94c
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.auth.pubkey;
+
+import java.io.IOException;
+import java.nio.channels.Channel;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.sshd.agent.SshAgent;
+import org.apache.sshd.agent.SshAgentFactory;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.client.session.ClientSessionHolder;
+import org.apache.sshd.common.FactoryManager;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class UserAuthPublicKeyIterator implements Iterator<PublicKeyIdentity>, ClientSessionHolder, Channel {
+
+ private final AtomicBoolean open = new AtomicBoolean(true);
+ private final ClientSession clientSession;
+ private final Iterator<Iterator<? extends PublicKeyIdentity>> iterators;
+ private Iterator<? extends PublicKeyIdentity> current;
+ private SshAgent agent;
+
+ public UserAuthPublicKeyIterator(ClientSession session) throws Exception {
+ clientSession = ValidateUtils.checkNotNull(session, "No session");
+
+ Collection<Iterator<? extends PublicKeyIdentity>> identities = new LinkedList<>();
+ identities.add(new SessionKeyPairIterator(session, KeyIdentityProvider.Utils.iteratorOf(session)));
+
+ FactoryManager manager = ValidateUtils.checkNotNull(session.getFactoryManager(), "No session factory manager");
+ SshAgentFactory factory = manager.getAgentFactory();
+ if (factory != null) {
+ try {
+ agent = ValidateUtils.checkNotNull(factory.createClient(manager), "No agent created");
+ identities.add(new SshAgentPublicKeyIterator(session, agent));
+ } catch (Exception e) {
+ try {
+ closeAgent();
+ } catch (Exception err) {
+ e.addSuppressed(err);
+ }
+
+ throw e;
+ }
+ }
+
+ iterators = GenericUtils.iteratorOf(identities);
+ current = nextIterator(iterators);
+ }
+
+ @Override
+ public ClientSession getClientSession() {
+ return clientSession;
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (!isOpen()) {
+ return false;
+ }
+
+ return current != null;
+ }
+
+ @Override
+ public PublicKeyIdentity next() {
+ if (!isOpen()) {
+ throw new NoSuchElementException("Iterator is closed");
+ }
+
+ PublicKeyIdentity pki = current.next();
+ if (!current.hasNext()) {
+ current = nextIterator(iterators);
+ }
+ return pki;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("No removal allowed");
+ }
+
+ @Override
+ public boolean isOpen() {
+ return open.get();
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (open.getAndSet(false)) {
+ closeAgent();
+ }
+ }
+
+ protected void closeAgent() throws IOException {
+ if (agent != null) {
+ try {
+ agent.close();
+ } finally {
+ agent = null;
+ }
+ }
+ }
+
+ protected Iterator<? extends PublicKeyIdentity> nextIterator(
+ Iterator<? extends Iterator<? extends PublicKeyIdentity>> available) {
+ while ((available != null) && available.hasNext()) {
+ Iterator<? extends PublicKeyIdentity> iter = available.next();
+ if (iter.hasNext()) {
+ return iter;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[" + getClientSession() + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
index 6ffb863..9506ee7 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
@@ -20,7 +20,6 @@ package org.apache.sshd.client.session;
import java.io.IOException;
import java.nio.file.FileSystem;
-import java.security.KeyPair;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
@@ -38,7 +37,6 @@ import org.apache.sshd.client.subsystem.sftp.SftpClient;
import org.apache.sshd.client.subsystem.sftp.SftpVersionSelector;
import org.apache.sshd.common.SshdSocketAddress;
import org.apache.sshd.common.future.KeyExchangeFuture;
-import org.apache.sshd.common.keyprovider.KeyPairProviderHolder;
import org.apache.sshd.common.scp.ScpTransferEventListener;
import org.apache.sshd.common.session.Session;
@@ -70,7 +68,7 @@ import org.apache.sshd.common.session.Session;
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public interface ClientSession extends Session, KeyPairProviderHolder, ClientAuthenticationManager {
+public interface ClientSession extends Session, ClientAuthenticationManager {
enum ClientSessionEvent {
TIMEOUT,
CLOSED,
@@ -79,32 +77,6 @@ public interface ClientSession extends Session, KeyPairProviderHolder, ClientAut
}
/**
- * @param password Password to be added - may not be {@code null}/empty
- */
- void addPasswordIdentity(String password);
-
- /**
- * @param password The password to remove - ignored if {@code null}/empty
- * @return The removed password - same one that was added via
- * {@link #addPasswordIdentity(String)} - or {@code null} if no
- * match found
- */
- String removePasswordIdentity(String password);
-
- /**
- * @param key The {@link KeyPair} to add - may not be {@code null}
- */
- void addPublicKeyIdentity(KeyPair key);
-
- /**
- * @param kp The {@link KeyPair} to remove - ignored if {@code null}
- * @return The removed {@link KeyPair} - same one that was added via
- * {@link #addPublicKeyIdentity(KeyPair)} - or {@code null} if no
- * match found
- */
- KeyPair removePublicKeyIdentity(KeyPair kp);
-
- /**
* Starts the authentication process.
* User identities will be tried until the server successfully authenticate the user.
* User identities must be provided before calling this method using
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
index 5a0a0f5..8166f2b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
@@ -127,10 +127,7 @@ public class ClientSessionImpl extends AbstractClientSession {
ClientUserAuthService authService = getUserAuthService();
synchronized (lock) {
String serviceName = nextServiceName();
- authFuture = ValidateUtils.checkNotNull(
- authService.auth(getRegisteredIdentities(), serviceName),
- "No auth future generated by service=%s",
- serviceName);
+ authFuture = ValidateUtils.checkNotNull(authService.auth(serviceName), "No auth future generated by service=%s", serviceName);
return authFuture;
}
}
@@ -381,7 +378,7 @@ public class ClientSessionImpl extends AbstractClientSession {
@Override
protected void sendSessionEvent(SessionListener.Event event) throws IOException {
- if (event == SessionListener.Event.KeyEstablished) {
+ if (SessionListener.Event.KeyEstablished.equals(event)) {
sendInitialServiceRequest();
}
synchronized (lock) {
@@ -395,12 +392,13 @@ public class ClientSessionImpl extends AbstractClientSession {
return;
}
initialServiceRequestSent = true;
+ String serviceName = currentServiceFactory.getName();
if (log.isDebugEnabled()) {
- log.debug("sendInitialServiceRequest({}) Send SSH_MSG_SERVICE_REQUEST for {}",
- this, currentServiceFactory.getName());
+ log.debug("sendInitialServiceRequest({}) Send SSH_MSG_SERVICE_REQUEST for {}", this, serviceName);
}
- Buffer request = createBuffer(SshConstants.SSH_MSG_SERVICE_REQUEST);
- request.putString(currentServiceFactory.getName());
+
+ Buffer request = createBuffer(SshConstants.SSH_MSG_SERVICE_REQUEST, serviceName.length() + Byte.SIZE);
+ request.putString(serviceName);
writePacket(request);
// Assuming that MINA-SSHD only implements "explicit server authentication" it is permissible
// for the client's service to start sending data before the service-accept has been received.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java
index 34c6cf5..ae5bf7a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java
@@ -36,6 +36,7 @@ import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.SshException;
import org.apache.sshd.common.session.Session;
import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.closeable.AbstractCloseable;
@@ -54,7 +55,6 @@ public class ClientUserAuthService extends AbstractCloseable implements Service,
private final ClientSessionImpl clientSession;
- private List<Object> identities;
private String service;
private List<NamedFactory<UserAuth>> authFactories;
@@ -115,9 +115,8 @@ public class ClientUserAuthService extends AbstractCloseable implements Service,
// ignored
}
- public AuthFuture auth(List<Object> identities, String service) throws IOException {
- this.identities = new ArrayList<>(identities);
- this.service = service;
+ public AuthFuture auth(String service) throws IOException {
+ this.service = ValidateUtils.checkNotNullAndNotEmpty(service, "No service");
ClientSession session = getClientSession();
String username = session.getUsername();
@@ -284,7 +283,7 @@ public class ClientUserAuthService extends AbstractCloseable implements Service,
log.debug("tryNext({}) attempting method={}", session, method);
}
- userAuth.init(session, service, identities);
+ userAuth.init(session, service);
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
new file mode 100644
index 0000000..9a153aa
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.keyprovider;
+
+import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.Supplier;
+import org.apache.sshd.common.util.Transformer;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface KeyIdentityProvider {
+ KeyIdentityProvider EMPTY_KEYS_PROVIDER = new KeyIdentityProvider() {
+ @Override
+ public Iterable<KeyPair> loadKeys() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public String toString() {
+ return "EMPTY";
+ }
+ };
+
+ /**
+ * Load available keys.
+ *
+ * @return an {@link Iterable} instance of available keys - ignored if {@code null}
+ */
+ Iterable<KeyPair> loadKeys();
+
+ /**
+ * A helper class for key identity provider related operations
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+ // CHECKSTYLE:OFF
+ final class Utils {
+ // CHECKSTYLE:ON
+ public static final Transformer<KeyIdentityProvider, Iterable<KeyPair>> LOADER =
+ new Transformer<KeyIdentityProvider, Iterable<KeyPair>>() {
+ @Override
+ public Iterable<KeyPair> transform(KeyIdentityProvider p) {
+ return (p == null) ? null : p.loadKeys();
+ }
+ };
+
+ private Utils() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+
+ public static Iterator<KeyPair> iteratorOf(ClientSession session) {
+ ValidateUtils.checkNotNull(session, "No session");
+ return iteratorOf(session.getRegisteredIdentities(), session.getKeyPairProvider());
+ }
+
+ public static Iterator<KeyPair> iteratorOf(KeyIdentityProvider identities, KeyIdentityProvider keys) {
+ return iteratorOf(resolveKeyIdentityProvider(identities, keys));
+ }
+
+ /**
+ * Resolves a non-{@code null} iterator of the available keys
+ *
+ * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
+ * @return A non-{@code null} iterator - which may be empty if no provider or no keys
+ */
+ public static Iterator<KeyPair> iteratorOf(KeyIdentityProvider provider) {
+ return GenericUtils.iteratorOf((provider == null) ? null : provider.loadKeys());
+ }
+
+ public static KeyIdentityProvider resolveKeyIdentityProvider(KeyIdentityProvider identities, KeyIdentityProvider keys) {
+ if ((keys == null) || (identities == keys)) {
+ return identities;
+ } else if (identities == null) {
+ return keys;
+ } else {
+ return multiProvider(identities, keys);
+ }
+ }
+
+ public static KeyIdentityProvider multiProvider(KeyIdentityProvider ... providers) {
+ return multiProvider(GenericUtils.isEmpty(providers) ? Collections.<KeyIdentityProvider>emptyList() : Arrays.asList(providers));
+ }
+
+ public static KeyIdentityProvider multiProvider(Collection<? extends KeyIdentityProvider> providers) {
+ return wrap(iterableOf(providers));
+ }
+
+ public static Iterable<KeyPair> iterableOf(Collection<? extends KeyIdentityProvider> providers) {
+ if (GenericUtils.isEmpty(providers)) {
+ return Collections.emptyList();
+ }
+
+ Collection<Supplier<Iterable<KeyPair>>> suppliers = new ArrayList<Supplier<Iterable<KeyPair>>>(providers.size());
+ for (final KeyIdentityProvider p : providers) {
+ if (p == null) {
+ continue;
+ }
+
+ suppliers.add(new Supplier<Iterable<KeyPair>>() {
+ @Override
+ public Iterable<KeyPair> get() {
+ return p.loadKeys();
+ }
+ });
+ }
+
+ if (GenericUtils.isEmpty(suppliers)) {
+ return Collections.emptyList();
+ }
+
+ return GenericUtils.multiIterableSuppliers(suppliers);
+ }
+
+ public static KeyIdentityProvider wrap(final Iterable<KeyPair> keys) {
+ return new KeyIdentityProvider() {
+ @Override
+ public Iterable<KeyPair> loadKeys() {
+ return keys;
+ }
+ };
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
index a768a86..b4e7256 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
@@ -29,7 +29,7 @@ import org.apache.sshd.common.cipher.ECCurves;
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public interface KeyPairProvider {
+public interface KeyPairProvider extends KeyIdentityProvider {
/**
* SSH identifier for RSA keys
@@ -83,13 +83,6 @@ public interface KeyPairProvider {
};
/**
- * Load available keys.
- *
- * @return an {@link Iterable} instance of available keys, never {@code null}
- */
- Iterable<KeyPair> loadKeys();
-
- /**
* Load a key of the specified type which can be "ssh-rsa", "ssh-dss", or
* "ecdsa-sha2-nistp{256,384,521}". If there is no key of this type, return
* {@code null}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
index ed5593e..ef06c54 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
@@ -31,6 +31,7 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.SortedMap;
@@ -565,4 +566,76 @@ public final class GenericUtils {
}
};
}
+
+ /**
+ * Resolves to an always non-{@code null} iterator
+ *
+ * @param iterable The {@link Iterable} instance
+ * @return A non-{@code null} iterator which may be empty if no iterable
+ * instance or no iterator returned from it
+ * @see #iteratorOf(Iterator)
+ */
+ public static <T> Iterator<T> iteratorOf(Iterable<T> iterable) {
+ return iteratorOf((iterable == null) ? null : iterable.iterator());
+ }
+
+ /**
+ * Resolves to an always non-{@code null} iterator
+ *
+ * @param iter The {@link Iterator} instance
+ * @return A non-{@code null} iterator which may be empty if no iterator instance
+ * @see Collections#emptyIterator()
+ */
+ public static <T> Iterator<T> iteratorOf(Iterator<T> iter) {
+ return (iter == null) ? Collections.<T>emptyIterator() : iter;
+ }
+
+ public static <T> Iterable<T> multiIterableSuppliers(final Iterable<? extends Supplier<? extends Iterable<? extends T>>> providers) {
+ return new Iterable<T>() {
+ @Override
+ public Iterator<T> iterator() {
+ return new Iterator<T>() {
+ private final Iterator<? extends Supplier<? extends Iterable<? extends T>>> iter = iteratorOf(providers);
+ private Iterator<? extends T> current = nextIterator();
+
+ @Override
+ public boolean hasNext() {
+ return current != null;
+ }
+
+ @Override
+ public T next() {
+ if (current == null) {
+ throw new NoSuchElementException("No more elements");
+ }
+
+ T value = current.next();
+ if (!current.hasNext()) {
+ current = nextIterator();
+ }
+
+ return value;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("remove");
+ }
+
+ private Iterator<? extends T> nextIterator() {
+ while (iter.hasNext()) {
+ Supplier<? extends Iterable<? extends T>> supplier = iter.next();
+ Iterator<? extends T> values = iteratorOf((supplier == null) ? null : supplier.get());
+ if (values.hasNext()) {
+ return values;
+ }
+ }
+
+ return null;
+ }
+ };
+ }
+
+ };
+ }
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java
index 9d17c4d..518b319 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java
@@ -65,7 +65,8 @@ public class ServerUserAuthService extends AbstractCloseable implements Service,
serverSession = (ServerSession) s;
maxAuthRequests = PropertyResolverUtils.getIntProperty(s, ServerAuthenticationManager.MAX_AUTH_REQUESTS, ServerAuthenticationManager.DEFAULT_MAX_AUTH_REQUESTS);
- List<NamedFactory<UserAuth>> factories = serverSession.getUserAuthFactories();
+ List<NamedFactory<UserAuth>> factories = ValidateUtils.checkNotNullAndNotEmpty(
+ serverSession.getUserAuthFactories(), "No user auth factories for %s", s);
userAuthFactories = new ArrayList<>(factories);
// Get authentication methods
authMethods = new ArrayList<>();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/test/java/org/apache/sshd/client/ClientAuthenticationManagerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/ClientAuthenticationManagerTest.java b/sshd-core/src/test/java/org/apache/sshd/client/ClientAuthenticationManagerTest.java
new file mode 100644
index 0000000..2b6246d
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/client/ClientAuthenticationManagerTest.java
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import org.apache.sshd.client.auth.PasswordIdentityProvider;
+import org.apache.sshd.client.auth.UserInteraction;
+import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.client.session.ClientSessionImpl;
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.channel.Channel;
+import org.apache.sshd.common.channel.ChannelListener;
+import org.apache.sshd.common.forward.DefaultTcpipForwarderFactory;
+import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.random.JceRandomFactory;
+import org.apache.sshd.common.random.Random;
+import org.apache.sshd.common.random.SingletonRandomFactory;
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.SessionListener;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.BaseTestSupport;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+import org.mockito.Mockito;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class ClientAuthenticationManagerTest extends BaseTestSupport {
+ public ClientAuthenticationManagerTest() {
+ super();
+ }
+
+ @Test
+ public void testAddRemoveClientSessionIdentities() throws Exception {
+ try (ClientSession session = createMockClientSession()) {
+ testClientAuthenticationManager(session);
+ }
+ }
+
+ @Test
+ public void testAddRemoveSshClientIdentities() throws Exception {
+ try (SshClient client = SshClient.setUpDefaultClient()) {
+ testClientAuthenticationManager(client);
+ }
+ }
+
+ @Test
+ public void testClientProvidersPropagation() throws Exception {
+ try (SshClient client = SshClient.setUpDefaultClient()) {
+ client.setServiceFactories(SshClient.DEFAULT_SERVICE_FACTORIES);
+ client.setUserAuthFactories(SshClient.DEFAULT_USER_AUTH_FACTORIES);
+
+ try (ClientSession session = createMockClientSession(client)) {
+ for (Class<?> provider : new Class<?>[] {
+ PasswordIdentityProvider.class,
+ ServerKeyVerifier.class,
+ UserInteraction.class,
+ KeyPairProvider.class
+ }) {
+ testClientProvidersPropagation(provider, client, session);
+ }
+ }
+ }
+ }
+
+ private void testClientProvidersPropagation(Class<?> type, ClientAuthenticationManager client, ClientAuthenticationManager session) throws Exception {
+ String baseName = type.getSimpleName();
+ outputDebugMessage("testClientProvidersPropagation(%s)", baseName);
+ assertTrue(baseName + ": not an interface", type.isInterface());
+
+ Method getter = ClientAuthenticationManager.class.getMethod("get" + baseName);
+ Method setter = ClientAuthenticationManager.class.getMethod("set" + baseName, type);
+ Object clientProvider = Mockito.mock(type);
+ setter.invoke(client, clientProvider);
+ assertSame(baseName + ": mismatched client-only provider", clientProvider, getter.invoke(session));
+
+ Object sessionProvider = Mockito.mock(type);
+ setter.invoke(session, sessionProvider);
+ assertSame(baseName + ": mismatched session override provider", sessionProvider, getter.invoke(session));
+
+ setter.invoke(session, new Object[] { null });
+ assertSame(baseName + ": mismatched nullified session provider", clientProvider, getter.invoke(session));
+ }
+
+ private <M extends ClientAuthenticationManager> M testClientAuthenticationManager(M manager) {
+ {
+ String expected = getCurrentTestName();
+ assertNull("Unexpected initial password identity", manager.removePasswordIdentity(expected));
+ manager.addPasswordIdentity(expected);
+
+ String actual = manager.removePasswordIdentity(expected);
+ assertSame("Mismatched removed password identity", expected, actual);
+ assertNull("Password identity not removed", manager.removePasswordIdentity(expected));
+ }
+
+ {
+ KeyPair expected = new KeyPair(Mockito.mock(PublicKey.class), Mockito.mock(PrivateKey.class));
+ assertNull("Unexpected initial pubket identity", manager.removePublicKeyIdentity(expected));
+ manager.addPublicKeyIdentity(expected);
+
+ KeyPair actual = manager.removePublicKeyIdentity(expected);
+ assertSame("Mismatched removed pubkey identity", expected, actual);
+ assertNull("Pubkey identity not removed", manager.removePublicKeyIdentity(expected));
+ }
+
+ return manager;
+ }
+
+ private ClientSession createMockClientSession() throws Exception {
+ ClientFactoryManager client = Mockito.mock(ClientFactoryManager.class);
+ Mockito.when(client.getTcpipForwarderFactory()).thenReturn(DefaultTcpipForwarderFactory.INSTANCE);
+ Mockito.when(client.getSessionListenerProxy()).thenReturn(new SessionListener() {
+ @Override
+ public void sessionEvent(Session session, Event event) {
+ // ignored
+ }
+
+ @Override
+ public void sessionCreated(Session session) {
+ // ignored
+ }
+
+ @Override
+ public void sessionClosed(Session session) {
+ // ignored
+ }
+ });
+ Mockito.when(client.getChannelListenerProxy()).thenReturn(new ChannelListener() {
+ @Override
+ public void channelOpenSuccess(Channel channel) {
+ // ignored
+ }
+
+ @Override
+ public void channelOpenFailure(Channel channel, Throwable reason) {
+ // ignored
+ }
+
+ @Override
+ public void channelInitialized(Channel channel) {
+ // ignored
+ }
+
+ @Override
+ public void channelClosed(Channel channel) {
+ // ignored
+ }
+ });
+ Factory<Random> randomFactory = new SingletonRandomFactory(JceRandomFactory.INSTANCE);
+ Mockito.when(client.getRandomFactory()).thenReturn(randomFactory);
+
+ Mockito.when(client.getServiceFactories()).thenReturn(SshClient.DEFAULT_SERVICE_FACTORIES);
+ Mockito.when(client.getUserAuthFactories()).thenReturn(SshClient.DEFAULT_USER_AUTH_FACTORIES);
+ return createMockClientSession(client);
+ }
+
+ private ClientSession createMockClientSession(ClientFactoryManager client) throws Exception {
+ return new ClientSessionImpl(client, Mockito.mock(IoSession.class)) {
+ @Override
+ protected void sendClientIdentification() {
+ // ignored
+ }
+
+ @Override
+ protected byte[] sendKexInit() throws IOException {
+ return GenericUtils.EMPTY_BYTE_ARRAY;
+ }
+
+ @Override
+ public void close() throws IOException {
+ // ignored
+ }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/13818de0/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java b/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java
index 26fb505..4d1b9a7 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java
@@ -363,7 +363,7 @@ public class ClientTest extends BaseTestSupport {
channel.setIn(inPipe);
channel.setOut(out);
channel.setErr(err);
- channel.open().verify(3L, TimeUnit.SECONDS);
+ channel.open().verify(6L, TimeUnit.SECONDS);
break; // 1st success means all methods have been invoked
}
} catch (IOException e) {
@@ -1295,7 +1295,7 @@ public class ClientTest extends BaseTestSupport {
channel.open().verify(9L, TimeUnit.SECONDS);
AbstractSession cs = (AbstractSession) session;
- Buffer buffer = cs.createBuffer(SshConstants.SSH_MSG_DISCONNECT);
+ Buffer buffer = cs.createBuffer(SshConstants.SSH_MSG_DISCONNECT, Integer.SIZE);
buffer.putInt(SshConstants.SSH2_DISCONNECT_BY_APPLICATION);
buffer.putString("Cancel");
buffer.putString(""); // TODO add language tag