import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.DoNotCall;
-import io.netty.channel.EventLoopGroup;
import java.security.cert.Certificate;
+import java.util.concurrent.ScheduledExecutorService;
import org.opendaylight.netconf.shaded.sshd.client.ClientBuilder;
import org.opendaylight.netconf.shaded.sshd.client.SshClient;
import org.opendaylight.netconf.shaded.sshd.client.auth.UserAuthFactory;
import org.opendaylight.netconf.shaded.sshd.client.auth.pubkey.UserAuthPublicKeyFactory;
import org.opendaylight.netconf.shaded.sshd.client.keyverifier.ServerKeyVerifier;
import org.opendaylight.netconf.shaded.sshd.common.keyprovider.KeyIdentityProvider;
+import org.opendaylight.netconf.shaded.sshd.netty.NettyIoServiceFactoryFactory;
import org.opendaylight.netconf.transport.api.UnsupportedConfigurationException;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.crypto.types.rev230417.password.grouping.password.type.CleartextPassword;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.client.rev230417.ssh.client.grouping.ClientIdentity;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.client.rev230417.ssh.client.grouping.Keepalives;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.client.rev230417.ssh.client.grouping.ServerAuthentication;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.common.rev230417.TransportParamsGrouping;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.crypto.types.rev231228.password.grouping.password.type.CleartextPassword;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.client.rev231228.ssh.client.grouping.ClientIdentity;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.client.rev231228.ssh.client.grouping.Keepalives;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.client.rev231228.ssh.client.grouping.ServerAuthentication;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.common.rev231228.TransportParamsGrouping;
/**
* Our internal-use {@link SshClient}. We reuse all the properties and logic of an {@link SshClient}, but we never allow
* {@code ietf-netconf-client.yang} configuration.
*/
static final class Builder extends ClientBuilder {
- private final EventLoopGroup group;
+ private final NettyIoServiceFactoryFactory ioServiceFactory;
+ private final ScheduledExecutorService executorService;
+ private ClientFactoryManagerConfigurator configurator;
private Keepalives keepAlives;
private ClientIdentity clientIdentity;
- Builder(final EventLoopGroup group) {
- this.group = requireNonNull(group);
+ Builder(final NettyIoServiceFactoryFactory ioServiceFactory, final ScheduledExecutorService executorService) {
+ this.ioServiceFactory = requireNonNull(ioServiceFactory);
+ this.executorService = requireNonNull(executorService);
}
Builder transportParams(final TransportParamsGrouping params) throws UnsupportedConfigurationException {
return this;
}
- Builder keepAlives(final Keepalives newkeepAlives) {
- keepAlives = newkeepAlives;
+ Builder keepAlives(final Keepalives newKeepAlives) {
+ keepAlives = newKeepAlives;
return this;
}
return this;
}
+ Builder configurator(final ClientFactoryManagerConfigurator newConfigurator) {
+ configurator = newConfigurator;
+ return this;
+ }
+
TransportSshClient buildChecked() throws UnsupportedConfigurationException {
final var ret = (TransportSshClient) super.build(true);
if (keepAlives != null) {
} else {
ConfigUtils.setKeepAlives(ret, null, null);
}
+ if (clientIdentity == null) {
+ throw new UnsupportedConfigurationException("Client parameters are required");
+ }
+ final var username = clientIdentity.getUsername();
+ if (username == null) {
+ throw new UnsupportedConfigurationException("Client parameters are missing username");
+ }
+
if (clientIdentity != null && clientIdentity.getNone() == null) {
- setClientIdentity(ret, clientIdentity);
+ setClientIdentity(ret, clientIdentity, configurator == null);
+ }
+ if (configurator != null) {
+ configurator.configureClientFactoryManager(ret);
}
- ret.setScheduledExecutorService(group);
+ ret.setIoServiceFactoryFactory(ioServiceFactory);
+ ret.setScheduledExecutorService(executorService);
try {
ret.checkConfig();
} catch (IllegalArgumentException e) {
throw new UnsupportedConfigurationException("Inconsistent client configuration", e);
}
+
+ ret.setSessionFactory(new TransportClientSessionFactory(ret, username));
return ret;
}
return super.fillWithDefaultValues();
}
- private static void setClientIdentity(final TransportSshClient client, final ClientIdentity clientIdentity)
- throws UnsupportedConfigurationException {
+ private static void setClientIdentity(final TransportSshClient client, final ClientIdentity clientIdentity,
+ final boolean throwExceptionIfNoAuthMethodDefined) throws UnsupportedConfigurationException {
final var authFactoriesListBuilder = ImmutableList.<UserAuthFactory>builder();
final var password = clientIdentity.getPassword();
if (password != null) {
var factory = new UserAuthHostBasedFactory();
factory.setClientHostKeys(HostKeyIdentityProvider.wrap(keyPair));
factory.setClientUsername(clientIdentity.getUsername());
- factory.setClientHostname(null); // not provided via config
+ // not provided via config
+ factory.setClientHostname(null);
factory.setSignatureFactories(client.getSignatureFactories());
authFactoriesListBuilder.add(factory);
}
authFactoriesListBuilder.add(factory);
}
// FIXME implement authentication using X509 certificate
+
final var userAuthFactories = authFactoriesListBuilder.build();
- if (userAuthFactories.isEmpty()) {
+ if (!userAuthFactories.isEmpty()) {
+ client.setUserAuthFactories(userAuthFactories);
+ } else if (throwExceptionIfNoAuthMethodDefined) {
throw new UnsupportedConfigurationException("Client Identity has no authentication mechanism defined");
}
- client.setUserAuthFactories(userAuthFactories);
}
}
}