This is an automated email from the ASF dual-hosted git repository. twolf pushed a commit to branch dev_3.0 in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
commit 138bcfbe8d05cde28f6880230024fc5b3d1c0121 Author: Thomas Wolf <tw...@apache.org> AuthorDate: Wed Apr 23 20:08:13 2025 +0200 Simplify SshClient With the HostConfigEntry available on the session from the moment it is created, we don't need to pass along so many data through the internal methods of SshClient. We get a single point once the IoConnectFuture is done and has created the session where we can read data from the HostConfigEntry and configure the session. Split the actual session configuration in several overrideable methods so that user code can customize it easily. Remove no longer needed AttributeKeys. --- .../java/org/apache/sshd/client/SshClient.java | 86 +++++++++++----------- .../sshd/client/auth/pubkey/UserAuthPublicKey.java | 14 ---- 2 files changed, 44 insertions(+), 56 deletions(-) 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 9687237f6..54d85a853 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 @@ -56,7 +56,6 @@ import org.apache.sshd.client.auth.password.PasswordAuthenticationReporter; import org.apache.sshd.client.auth.password.PasswordIdentityProvider; import org.apache.sshd.client.auth.password.UserAuthPasswordFactory; import org.apache.sshd.client.auth.pubkey.PublicKeyAuthenticationReporter; -import org.apache.sshd.client.auth.pubkey.UserAuthPublicKey; import org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyFactory; import org.apache.sshd.client.config.hosts.HostConfigEntry; import org.apache.sshd.client.config.hosts.HostConfigEntryResolver; @@ -561,15 +560,9 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa AttributeRepository context, SocketAddress localAddress) throws IOException { Objects.requireNonNull(hostConfig, "No host configuration"); - String host = ValidateUtils.checkNotNullAndNotEmpty(hostConfig.getHostName(), "No target host"); + ValidateUtils.checkNotNullAndNotEmpty(hostConfig.getHostName(), "No target host"); int port = hostConfig.getPort(); ValidateUtils.checkTrue(port > 0, "Invalid port: %d", port); - Collection<String> hostIds = hostConfig.getIdentities(); - Collection<PathResource> idFiles = GenericUtils.stream(hostIds) - .map(Paths::get) - .map(PathResource::new) - .collect(Collectors.toCollection(() -> new ArrayList<>(hostIds.size()))); - KeyIdentityProvider keys = preloadClientIdentities(idFiles); String username = hostConfig.getUsername(); InetSocketAddress targetAddress = new InetSocketAddress(hostConfig.getHostName(), hostConfig.getPort()); if (GenericUtils.isNotEmpty(jumps)) { @@ -613,7 +606,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa .createLocalPortForwardingTracker(SshdSocketAddress.LOCALHOST_ADDRESS, address); SshdSocketAddress bound = tracker.getBoundAddress(); ConnectFuture f4 = doConnect(hostConfig.getUsername(), bound.toInetSocketAddress(), null, - context, localAddress, keys, hostConfig); + context, localAddress, hostConfig); toCancel.set(f4); if (connectFuture.isCanceled()) { f4.cancel(); @@ -654,14 +647,13 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa ProxyDataFactory factory = getProxyDataFactory(); ProxyData proxy = factory == null ? null : factory.get(targetAddress); return doConnect(hostConfig.getUsername(), targetAddress, proxy, - context, localAddress, keys, hostConfig); + context, localAddress, hostConfig); } } protected ConnectFuture doConnect( String username, InetSocketAddress targetAddress, ProxyData proxy, - AttributeRepository context, SocketAddress localAddress, - KeyIdentityProvider identities, HostConfigEntry hostConfig) + AttributeRepository context, SocketAddress localAddress, HostConfigEntry hostConfig) throws IOException { if (connector == null) { throw new IllegalStateException("SshClient not started. Please call start() method before connecting to a server"); @@ -682,8 +674,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa sessionContext = new ShadowingAttributeRepository(sessionContext, context); } ConnectFuture connectFuture = new DefaultConnectFuture(username + "@" + targetAddress, null); - SshFutureListener<IoConnectFuture> listener = createConnectCompletionListener( - connectFuture, username, targetAddress, identities, hostConfig); + SshFutureListener<IoConnectFuture> listener = createConnectCompletionListener(connectFuture, username, targetAddress); IoConnectFuture connectingFuture = connector.connect(connectAddress, sessionContext, localAddress); connectFuture.addListener(c -> { if (c.isCanceled()) { @@ -779,20 +770,8 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa return entry; } - protected KeyIdentityProvider preloadClientIdentities( - Collection<? extends NamedResource> locations) - throws IOException { - return GenericUtils.isEmpty(locations) - ? KeyIdentityProvider.EMPTY_KEYS_PROVIDER - : ClientIdentityLoader.asKeyIdentityProvider( - Objects.requireNonNull(getClientIdentityLoader(), "No ClientIdentityLoader"), - locations, getFilePasswordProvider(), - CoreModuleProperties.IGNORE_INVALID_IDENTITIES.getRequired(this)); - } - protected SshFutureListener<IoConnectFuture> createConnectCompletionListener( - ConnectFuture connectFuture, String username, SocketAddress address, - KeyIdentityProvider identities, HostConfigEntry hostConfig) { + ConnectFuture connectFuture, String username, SocketAddress address) { return new SshFutureListener<IoConnectFuture>() { @Override @SuppressWarnings("synthetic-access") @@ -815,7 +794,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa } else { IoSession ioSession = future.getSession(); try { - onConnectOperationComplete(ioSession, connectFuture, username, address, identities, hostConfig); + onConnectOperationComplete(ioSession, connectFuture, username, address); } catch (IOException | GeneralSecurityException | RuntimeException e) { warn("operationComplete({}@{}) failed ({}) to signal completion of session={}: {}", username, address, e.getClass().getSimpleName(), ioSession, e.getMessage(), e); @@ -834,24 +813,13 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa } protected void onConnectOperationComplete( - IoSession ioSession, ConnectFuture connectFuture, String username, - SocketAddress address, KeyIdentityProvider identities, HostConfigEntry hostConfig) + IoSession ioSession, ConnectFuture connectFuture, String username, SocketAddress address) throws IOException, GeneralSecurityException { AbstractClientSession session = (AbstractClientSession) AbstractSession.getSession(ioSession); session.setUsername(username); session.setConnectAddress(address); - boolean useDefaultIdentities = !hostConfig.isIdentitiesOnly(); - session.setAttribute(UserAuthPublicKey.USE_DEFAULT_IDENTITIES, Boolean.valueOf(useDefaultIdentities)); - String identityAgent = hostConfig.getProperty(HostConfigEntry.IDENTITY_AGENT); - session.setAttribute(UserAuthPublicKey.IDENTITY_AGENT, identityAgent == null ? "" : identityAgent); - - if (useDefaultIdentities) { - setupDefaultSessionIdentities(session, identities); - } else if (identities == null) { - session.setKeyIdentityProvider(KeyIdentityProvider.EMPTY_KEYS_PROVIDER); - } else { - session.setKeyIdentityProvider(ensureFilePasswordProvider(identities)); - } + + configure(session); connectFuture.setSession(session); if (session != connectFuture.getSession()) { @@ -867,6 +835,40 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa } } + protected void configure(ClientSession session) throws IOException, GeneralSecurityException { + setSessionKeyIdentities(session); + } + + protected void setSessionKeyIdentities(ClientSession session) + throws IOException, GeneralSecurityException { + HostConfigEntry hostConfig = session.getHostConfigEntry(); + KeyIdentityProvider fromHostConfig = getKeyIdentities(hostConfig); + if (!hostConfig.isIdentitiesOnly()) { + setupDefaultSessionIdentities(session, fromHostConfig); + } else if (fromHostConfig == null) { + session.setKeyIdentityProvider(KeyIdentityProvider.EMPTY_KEYS_PROVIDER); + } else { + session.setKeyIdentityProvider(ensureFilePasswordProvider(fromHostConfig)); + } + } + + protected KeyIdentityProvider getKeyIdentities(HostConfigEntry hostConfig) { + Collection<String> hostIds = hostConfig.getIdentities(); + Collection<PathResource> idFiles = GenericUtils.stream(hostIds) + .map(Paths::get) + .map(PathResource::new) + .collect(Collectors.toCollection(() -> new ArrayList<>(hostIds.size()))); + return preloadClientIdentities(idFiles); + } + + protected KeyIdentityProvider preloadClientIdentities(Collection<? extends NamedResource> locations) { + return GenericUtils.isEmpty(locations) + ? KeyIdentityProvider.EMPTY_KEYS_PROVIDER + : ClientIdentityLoader.asKeyIdentityProvider( + Objects.requireNonNull(getClientIdentityLoader(), "No ClientIdentityLoader"), locations, + getFilePasswordProvider(), CoreModuleProperties.IGNORE_INVALID_IDENTITIES.getRequired(this)); + } + /** * Sets this client's {@link FilePasswordProvider} on the {@link KeyIdentityProvider} if it is an * {@link AbstractResourceKeyPairProvider} or implements {@link FilePasswordProviderManager} and doesn't have one diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKey.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKey.java index 6bd9424a5..aaff791dc 100644 --- a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKey.java +++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKey.java @@ -35,7 +35,6 @@ import java.util.TreeSet; import org.apache.sshd.client.auth.AbstractUserAuth; import org.apache.sshd.client.auth.keyboard.UserInteraction; import org.apache.sshd.client.session.ClientSession; -import org.apache.sshd.common.AttributeRepository.AttributeKey; import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.RuntimeSshException; import org.apache.sshd.common.SshConstants; @@ -60,19 +59,6 @@ import org.apache.sshd.common.util.buffer.ByteArrayBuffer; public class UserAuthPublicKey extends AbstractUserAuth implements SignatureFactoriesManager { public static final String NAME = UserAuthPublicKeyFactory.NAME; - /** - * Is set on a {@link ClientSession} when it is created; if {@link Boolean#FALSE}, no default identities shall be - * used. - */ - public static final AttributeKey<Boolean> USE_DEFAULT_IDENTITIES = new AttributeKey<>(); - - /** - * Is set on a {@link ClientSession} when it is created; contains the value of the {@code IdentityAgent} SSH config - * setting. May be the empty string if not specified in the - * {@link org.apache.sshd.client.config.hosts.HostConfigEntry#IDENTITY_AGENT HostConfigEntry}. - */ - public static final AttributeKey<String> IDENTITY_AGENT = new AttributeKey<>(); - protected final Deque<String> currentAlgorithms = new LinkedList<>(); protected Iterator<PublicKeyIdentity> keys;