This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit 56f10dce77a5108a3624e898aa4526c4e4f12bca
Author: Lyor Goldstein <lgoldst...@apache.org>
AuthorDate: Thu Apr 1 21:22:59 2021 +0300

    [SSHD-1116] Provide SessionContext argument to 
PasswordIdentityProvider#loadPasswords
---
 CHANGES.md                                         |   1 +
 .../auth/AuthenticationIdentitiesProvider.java     |   2 +-
 .../auth/password/PasswordIdentityProvider.java    | 102 ++++++++++++++-------
 .../password/PasswordIdentityProviderTest.java     |  11 ++-
 .../InteractivePasswordIdentityProvider.java       |   4 +-
 .../InteractivePasswordIdentityProviderTest.java   |  10 +-
 .../apache/sshd/client/session/ClientSession.java  |  17 ++--
 .../common/auth/PasswordAuthenticationTest.java    |   2 +-
 8 files changed, 96 insertions(+), 53 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index d2e0fcf..f99e6ea 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -29,6 +29,7 @@
 * [SSHD-1110](https://issues.apache.org/jira/browse/SSHD-1110) Replace 
`Class#newInstance()` calls with `Class#getDefaultConstructor().newInstance()`
 * [SSHD-1111](https://issues.apache.org/jira/browse/SSHD-1111) Fixed 
SshClientCliSupport compression option detection
 * [SSHD-1116](https://issues.apache.org/jira/browse/SSHD-1116) Provide 
SessionContext argument to HostKeyIdentityProvider#loadHostKeys
+* [SSHD-1116](https://issues.apache.org/jira/browse/SSHD-1116) Provide 
SessionContext argument to PasswordIdentityProvider#loadPasswords
 * [SSHD-1125](https://issues.apache.org/jira/browse/SSHD-1125) Added option to 
require immediate close of channel in command `ExitCallback` invocation
 * [SSHD-1127](https://issues.apache.org/jira/browse/SSHD-1127) Consolidated 
`SftpSubsystem` support implementations into `SftpSubsystemConfigurator`
 * [SSHD-1148](https://issues.apache.org/jira/browse/SSHD-1148) Generate a 
unique thread name for each `SftpSubsystem` instance
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
 
b/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
index 3b54c9d..6234d33 100644
--- 
a/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
+++ 
b/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
@@ -88,7 +88,7 @@ public interface AuthenticationIdentitiesProvider extends 
KeyIdentityProvider, P
             }
 
             @Override
-            public Iterable<String> loadPasswords() {
+            public Iterable<String> loadPasswords(SessionContext session) {
                 return 
LazyMatchingTypeIterable.lazySelectMatchingTypes(identities, String.class);
             }
 
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java
 
b/sshd-common/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java
index a0d6eca..90db5c9 100644
--- 
a/sshd-common/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java
+++ 
b/sshd-common/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java
@@ -19,12 +19,14 @@
 
 package org.apache.sshd.client.auth.password;
 
+import java.io.IOException;
+import java.security.GeneralSecurityException;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
-import java.util.function.Function;
 import java.util.function.Supplier;
 
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
 
 /**
@@ -34,11 +36,12 @@ import org.apache.sshd.common.util.GenericUtils;
 public interface PasswordIdentityProvider {
 
     /**
-     * An &quot;empty&quot implementation of {@link PasswordIdentityProvider} 
that returns and empty group of passwords
+     * An &quot;empty&quot implementation of {@link PasswordIdentityProvider} 
that returns an empty group of passwords
      */
     PasswordIdentityProvider EMPTY_PASSWORDS_PROVIDER = new 
PasswordIdentityProvider() {
         @Override
-        public Iterable<String> loadPasswords() {
+        public Iterable<String> loadPasswords(SessionContext session)
+                throws IOException, GeneralSecurityException {
             return Collections.emptyList();
         }
 
@@ -49,37 +52,48 @@ public interface PasswordIdentityProvider {
     };
 
     /**
-     * Invokes {@link PasswordIdentityProvider#loadPasswords()} and returns 
the result. Ignores {@code null} providers
-     * (i.e., returns an empty iterable instance)
+     * @param  session                  The {@link SessionContext} for 
invoking this load command - may be {@code null}
+     *                                  if not invoked within a session 
context (e.g., offline tool).
+     * @return                          The currently available passwords - 
ignored if {@code null}
+     * @throws IOException              If failed to load the passwords
+     * @throws GeneralSecurityException If some security issue with the 
passwords
      */
-    Function<PasswordIdentityProvider, Iterable<String>> LOADER
-            = p -> (p == null) ? Collections.emptyList() : p.loadPasswords();
-
-    /**
-     * @return The currently available passwords - ignored if {@code null}
-     */
-    Iterable<String> loadPasswords();
+    Iterable<String> loadPasswords(SessionContext session)
+            throws IOException, GeneralSecurityException;
 
     /**
      * Creates a &quot;unified&quot; {@link Iterator} of passwords out of 2 
possible {@link PasswordIdentityProvider}
      *
-     * @param  identities The registered passwords
-     * @param  passwords  Extra available passwords
-     * @return            The wrapping iterator
-     * @see               
#resolvePasswordIdentityProvider(PasswordIdentityProvider, 
PasswordIdentityProvider)
+     * @param  session                  The {@link SessionContext} for 
invoking this load command - may be {@code null}
+     *                                  if not invoked within a session 
context (e.g., offline tool).
+     * @param  identities               The registered passwords
+     * @param  passwords                Extra available passwords
+     * @return                          The wrapping iterator
+     * @throws IOException              If failed to load the passwords
+     * @throws GeneralSecurityException If some security issue with the 
passwords
+     * @see                             
#resolvePasswordIdentityProvider(SessionContext, PasswordIdentityProvider,
+     *                                  PasswordIdentityProvider)
      */
-    static Iterator<String> iteratorOf(PasswordIdentityProvider identities, 
PasswordIdentityProvider passwords) {
-        return iteratorOf(resolvePasswordIdentityProvider(identities, 
passwords));
+    static Iterator<String> iteratorOf(
+            SessionContext session, PasswordIdentityProvider identities, 
PasswordIdentityProvider passwords)
+            throws IOException, GeneralSecurityException {
+        return iteratorOf(session, resolvePasswordIdentityProvider(session, 
identities, passwords));
     }
 
     /**
      * Resolves a non-{@code null} iterator of the available passwords
      *
-     * @param  provider The {@link PasswordIdentityProvider} - ignored if 
{@code null} (i.e., return an empty iterator)
-     * @return          A non-{@code null} iterator - which may be empty if no 
provider or no passwords
+     * @param  session                  The {@link SessionContext} for 
invoking this load command - may be {@code null}
+     *                                  if not invoked within a session 
context (e.g., offline tool).
+     * @param  provider                 The {@link PasswordIdentityProvider} - 
ignored if {@code null} (i.e., return an
+     *                                  empty iterator)
+     * @return                          A non-{@code null} iterator - which 
may be empty if no provider or no passwords
+     * @throws IOException              If failed to load the passwords
+     * @throws GeneralSecurityException If some security issue with the 
passwords
      */
-    static Iterator<String> iteratorOf(PasswordIdentityProvider provider) {
-        return GenericUtils.iteratorOf((provider == null) ? null : 
provider.loadPasswords());
+    static Iterator<String> iteratorOf(SessionContext session, 
PasswordIdentityProvider provider)
+            throws IOException, GeneralSecurityException {
+        return GenericUtils.iteratorOf((provider == null) ? null : 
provider.loadPasswords(session));
     }
 
     /**
@@ -93,53 +107,71 @@ public interface PasswordIdentityProvider {
      * <LI>If both are the same instance then use it.</U>
      * <LI>Otherwise, returns a wrapper that groups both providers.</LI>
      * </UL>
-     * 
+     *
+     * @param  session    The {@link SessionContext} for invoking this load 
command - may be {@code null} if not invoked
+     *                    within a session context (e.g., offline tool).
      * @param  identities The registered passwords
      * @param  passwords  The extra available passwords
      * @return            The resolved provider
-     * @see               #multiProvider(PasswordIdentityProvider...)
+     * @see               #multiProvider(SessionContext, 
PasswordIdentityProvider...)
      */
     static PasswordIdentityProvider resolvePasswordIdentityProvider(
-            PasswordIdentityProvider identities, PasswordIdentityProvider 
passwords) {
+            SessionContext session, PasswordIdentityProvider identities, 
PasswordIdentityProvider passwords) {
         if ((passwords == null) || (identities == passwords)) {
             return identities;
         } else if (identities == null) {
             return passwords;
         } else {
-            return multiProvider(identities, passwords);
+            return multiProvider(session, identities, passwords);
         }
     }
 
     /**
      * Wraps a group of {@link PasswordIdentityProvider} into a single one
      *
+     * @param  session   The {@link SessionContext} for invoking this load 
command - may be {@code null} if not invoked
+     *                   within a session context (e.g., offline tool).
      * @param  providers The providers - ignored if {@code null}/empty (i.e., 
returns {@link #EMPTY_PASSWORDS_PROVIDER}
      * @return           The wrapping provider
-     * @see              #multiProvider(Collection)
+     * @see              #multiProvider(SessionContext, Collection)
      */
-    static PasswordIdentityProvider multiProvider(PasswordIdentityProvider... 
providers) {
-        return multiProvider(GenericUtils.asList(providers));
+    static PasswordIdentityProvider multiProvider(
+            SessionContext session, PasswordIdentityProvider... providers) {
+        return multiProvider(session, GenericUtils.asList(providers));
     }
 
     /**
      * Wraps a group of {@link PasswordIdentityProvider} into a single one
      *
+     * @param  session   The {@link SessionContext} for invoking this load 
command - may be {@code null} if not invoked
+     *                   within a session context (e.g., offline tool).
      * @param  providers The providers - ignored if {@code null}/empty (i.e., 
returns {@link #EMPTY_PASSWORDS_PROVIDER}
      * @return           The wrapping provider
      */
-    static PasswordIdentityProvider multiProvider(Collection<? extends 
PasswordIdentityProvider> providers) {
-        return GenericUtils.isEmpty(providers) ? EMPTY_PASSWORDS_PROVIDER : 
wrapPasswords(iterableOf(providers));
+    static PasswordIdentityProvider multiProvider(
+            SessionContext session, Collection<? extends 
PasswordIdentityProvider> providers) {
+        return GenericUtils.isEmpty(providers) ? EMPTY_PASSWORDS_PROVIDER : 
wrapPasswords(iterableOf(session, providers));
     }
 
     /**
      * Wraps a group of {@link PasswordIdentityProvider} into an {@link 
Iterable} of their combined passwords
      *
+     * @param  session   The {@link SessionContext} for invoking this load 
command - may be {@code null} if not invoked
+     *                   within a session context (e.g., offline tool).
      * @param  providers The providers - ignored if {@code null}/empty (i.e., 
returns an empty iterable instance)
      * @return           The wrapping iterable
      */
-    static Iterable<String> iterableOf(Collection<? extends 
PasswordIdentityProvider> providers) {
-        Iterable<Supplier<Iterable<String>>> passwordSuppliers = 
GenericUtils.<PasswordIdentityProvider,
-                Supplier<Iterable<String>>> wrapIterable(providers, p -> 
p::loadPasswords);
+    static Iterable<String> iterableOf(
+            SessionContext session, Collection<? extends 
PasswordIdentityProvider> providers) {
+        Iterable<Supplier<Iterable<String>>> passwordSuppliers
+                = GenericUtils.<PasswordIdentityProvider, 
Supplier<Iterable<String>>> wrapIterable(
+                        providers, p -> () -> {
+                            try {
+                                return p.loadPasswords(session);
+                            } catch (IOException | GeneralSecurityException e) 
{
+                                throw new RuntimeException(e);
+                            }
+                        });
         return GenericUtils.multiIterableSuppliers(passwordSuppliers);
     }
 
@@ -161,6 +193,6 @@ public interface PasswordIdentityProvider {
      * @return           The provider wrapper
      */
     static PasswordIdentityProvider wrapPasswords(Iterable<String> passwords) {
-        return (passwords == null) ? EMPTY_PASSWORDS_PROVIDER : () -> 
passwords;
+        return (passwords == null) ? EMPTY_PASSWORDS_PROVIDER : session -> 
passwords;
     }
 }
diff --git 
a/sshd-common/src/test/java/org/apache/sshd/client/auth/password/PasswordIdentityProviderTest.java
 
b/sshd-common/src/test/java/org/apache/sshd/client/auth/password/PasswordIdentityProviderTest.java
index 5da42b8..679f515 100644
--- 
a/sshd-common/src/test/java/org/apache/sshd/client/auth/password/PasswordIdentityProviderTest.java
+++ 
b/sshd-common/src/test/java/org/apache/sshd/client/auth/password/PasswordIdentityProviderTest.java
@@ -19,6 +19,8 @@
 
 package org.apache.sshd.client.auth.password;
 
+import java.io.IOException;
+import java.security.GeneralSecurityException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -44,7 +46,7 @@ public class PasswordIdentityProviderTest extends 
JUnitTestSupport {
     }
 
     @Test
-    public void testMultiProvider() {
+    public void testMultiProvider() throws IOException, 
GeneralSecurityException {
         String[][] values = {
                 { getClass().getSimpleName(), getCurrentTestName() },
                 { new Date(System.currentTimeMillis()).toString() },
@@ -61,12 +63,13 @@ public class PasswordIdentityProviderTest extends 
JUnitTestSupport {
             providers.add(p);
         }
 
-        PasswordIdentityProvider p = 
PasswordIdentityProvider.multiProvider(providers);
+        PasswordIdentityProvider p = 
PasswordIdentityProvider.multiProvider(null, providers);
         assertProviderContents("Multi", p, expected);
     }
 
-    private static void assertProviderContents(String message, 
PasswordIdentityProvider p, Iterable<String> expected) {
+    private static void assertProviderContents(String message, 
PasswordIdentityProvider p, Iterable<String> expected)
+            throws IOException, GeneralSecurityException {
         assertNotNull(message + ": no provider", p);
-        assertEquals(message, expected, p.loadPasswords());
+        assertEquals(message, expected, p.loadPasswords(null));
     }
 }
diff --git 
a/sshd-contrib/src/main/java/org/apache/sshd/contrib/client/auth/password/InteractivePasswordIdentityProvider.java
 
b/sshd-contrib/src/main/java/org/apache/sshd/contrib/client/auth/password/InteractivePasswordIdentityProvider.java
index 769d656..7fd50ec 100644
--- 
a/sshd-contrib/src/main/java/org/apache/sshd/contrib/client/auth/password/InteractivePasswordIdentityProvider.java
+++ 
b/sshd-contrib/src/main/java/org/apache/sshd/contrib/client/auth/password/InteractivePasswordIdentityProvider.java
@@ -36,7 +36,7 @@ import org.apache.sshd.common.util.functors.UnaryEquator;
  * Helps implement a {@link PasswordIdentityProvider} by delegating calls to
  * {@link UserInteraction#getUpdatedPassword(ClientSession, String, String)}. 
The way to use it would be as follows:
  * </P>
- * 
+ *
  * <pre>
  * <code>
  * try (ClientSession session = client.connect(login, host, 
port).await().getSession()) {
@@ -148,6 +148,6 @@ public class InteractivePasswordIdentityProvider
         Objects.requireNonNull(clientSession, "No client session provided");
         Objects.requireNonNull(userInteraction, "No user interaction instance 
configured");
         Iterable<String> passwords = () -> new 
InteractivePasswordIdentityProvider(clientSession, userInteraction, prompt);
-        return () -> passwords;
+        return s -> passwords;
     }
 }
diff --git 
a/sshd-contrib/src/test/java/org/apache/sshd/contrib/client/auth/password/InteractivePasswordIdentityProviderTest.java
 
b/sshd-contrib/src/test/java/org/apache/sshd/contrib/client/auth/password/InteractivePasswordIdentityProviderTest.java
index ee381cb..94990c4 100644
--- 
a/sshd-contrib/src/test/java/org/apache/sshd/contrib/client/auth/password/InteractivePasswordIdentityProviderTest.java
+++ 
b/sshd-contrib/src/test/java/org/apache/sshd/contrib/client/auth/password/InteractivePasswordIdentityProviderTest.java
@@ -18,6 +18,8 @@
  */
 package org.apache.sshd.contrib.client.auth.password;
 
+import java.io.IOException;
+import java.security.GeneralSecurityException;
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -49,7 +51,7 @@ public class InteractivePasswordIdentityProviderTest extends 
BaseTestSupport {
     }
 
     @Test
-    public void testPasswordEnumerations() {
+    public void testPasswordEnumerations() throws IOException, 
GeneralSecurityException {
         List<String> expected
                 = Arrays.asList(getClass().getSimpleName(), 
getClass().getPackage().getName(), getCurrentTestName());
         ClientSession session = Mockito.mock(ClientSession.class);
@@ -77,7 +79,7 @@ public class InteractivePasswordIdentityProviderTest extends 
BaseTestSupport {
         Mockito.when(session.getUserInteraction()).thenReturn(userInteraction);
 
         PasswordIdentityProvider provider = 
InteractivePasswordIdentityProvider.providerOf(session, prompt);
-        Iterable<String> passwords = provider.loadPasswords();
+        Iterable<String> passwords = provider.loadPasswords(session);
         int expIndex = 0;
         for (String actValue : passwords) {
             String expValue = expected.get(expIndex);
@@ -90,7 +92,7 @@ public class InteractivePasswordIdentityProviderTest extends 
BaseTestSupport {
     }
 
     @Test
-    public void testInteractionAllowedConsultation() {
+    public void testInteractionAllowedConsultation() throws IOException, 
GeneralSecurityException {
         ClientSession session = Mockito.mock(ClientSession.class);
         UserInteraction userInteraction = Mockito.mock(UserInteraction.class);
         
Mockito.when(userInteraction.isInteractionAllowed(ArgumentMatchers.any(ClientSession.class))).thenReturn(Boolean.FALSE);
@@ -99,7 +101,7 @@ public class InteractivePasswordIdentityProviderTest extends 
BaseTestSupport {
                 .thenThrow(new UnsupportedOperationException("Unexpected 
call"));
         PasswordIdentityProvider provider
                 = InteractivePasswordIdentityProvider.providerOf(session, 
userInteraction, getCurrentTestName());
-        Iterable<String> passwords = provider.loadPasswords();
+        Iterable<String> passwords = provider.loadPasswords(session);
         for (String p : passwords) {
             fail("Unexpected password: " + p);
         }
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 2de7695..ff4c2f6 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
@@ -27,6 +27,7 @@ import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.rmi.RemoteException;
 import java.rmi.ServerException;
+import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.security.PublicKey;
 import java.time.Duration;
@@ -452,15 +453,19 @@ public interface ClientSession
      * Creates a &quot;unified&quot; {@link Iterator} of passwords out of the 
registered passwords and the extra
      * available ones as a single iterator of passwords
      *
-     * @param  session The {@link ClientSession} - ignored if {@code null} 
(i.e., empty iterator returned)
-     * @return         The wrapping iterator
-     * @see            ClientSession#getRegisteredIdentities()
-     * @see            ClientSession#getPasswordIdentityProvider()
+     * @param  session                  The {@link ClientSession} - ignored if 
{@code null} (i.e., empty iterator
+     *                                  returned)
+     * @return                          The wrapping iterator
+     * @throws IOException              If failed to load the passwords
+     * @throws GeneralSecurityException If some security issue with the 
passwords
+     * @see                             ClientSession#getRegisteredIdentities()
+     * @see                             
ClientSession#getPasswordIdentityProvider()
      */
-    static Iterator<String> passwordIteratorOf(ClientSession session) {
+    static Iterator<String> passwordIteratorOf(ClientSession session)
+            throws IOException, GeneralSecurityException {
         return (session == null)
                 ? Collections.<String> emptyIterator()
                 : PasswordIdentityProvider.iteratorOf(
-                        session.getRegisteredIdentities(), 
session.getPasswordIdentityProvider());
+                        session, session.getRegisteredIdentities(), 
session.getPasswordIdentityProvider());
     }
 }
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/common/auth/PasswordAuthenticationTest.java
 
b/sshd-core/src/test/java/org/apache/sshd/common/auth/PasswordAuthenticationTest.java
index 7c04231..c0a81a1 100644
--- 
a/sshd-core/src/test/java/org/apache/sshd/common/auth/PasswordAuthenticationTest.java
+++ 
b/sshd-core/src/test/java/org/apache/sshd/common/auth/PasswordAuthenticationTest.java
@@ -330,7 +330,7 @@ public class PasswordAuthenticationTest extends 
AuthenticationTestSupport {
         try (SshClient client = setupTestClient()) {
             List<String> passwords = 
Collections.singletonList(getCurrentTestName());
             AtomicInteger loadCount = new AtomicInteger(0);
-            PasswordIdentityProvider provider = () -> {
+            PasswordIdentityProvider provider = session -> {
                 loadCount.incrementAndGet();
                 outputDebugMessage("loadPasswords - count=%s", loadCount);
                 return passwords;

Reply via email to