Merge "Remove the default loopback mount from the old connector feature"
authorTomas Cere <tcere@cisco.com>
Fri, 27 Oct 2017 14:43:25 +0000 (14:43 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Fri, 27 Oct 2017 14:43:25 +0000 (14:43 +0000)
31 files changed:
netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeMountSessionContext.java
netconf/netconf-client/src/test/java/org/opendaylight/netconf/client/TestingNetconfClient.java
netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfConnectDeviceCommand.java
netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/impl/NetconfCommandsImpl.java
netconf/netconf-console/src/test/resources/schemas/netconf-node-topology.yang
netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/LoginPasswordHandler.java [moved from netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/LoginPassword.java with 58% similarity]
netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/PublicKeyAuth.java
netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/LoginPasswordHandlerTest.java [moved from netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/LoginPasswordTest.java with 80% similarity]
netconf/netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/SshProxyServer.java
netconf/netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/SshProxyServerConfiguration.java
netconf/netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/SshProxyServerConfigurationBuilder.java
netconf/netconf-ssh/src/test/java/org/opendaylight/netconf/netty/SSHTest.java
netconf/netconf-topology-config/src/main/resources/org/opendaylight/blueprint/netconf-topology.xml
netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImpl.java
netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImplTest.java
netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/AbstractNetconfTopology.java
netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/NetconfConnectorFactoryImpl.java
netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImplTest.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/auth/DatastoreBackedPublicKeyAuth.java [new file with mode: 0644]
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfKeystoreAdapter.java [new file with mode: 0644]
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/util/NetconfSalKeystoreService.java [new file with mode: 0644]
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/util/NetconfTopologyRPCProvider.java
netconf/sal-netconf-connector/src/main/yang/netconf-keystore.yang [new file with mode: 0644]
netconf/sal-netconf-connector/src/main/yang/netconf-node-topology.yang
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/util/NetconfTopologyRPCProviderTest.java
netconf/tools/netconf-cli/src/main/java/org/opendaylight/netconf/cli/Main.java
netconf/tools/netconf-cli/src/main/java/org/opendaylight/netconf/cli/commands/local/Connect.java
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/NetconfDeviceSimulator.java
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/stress/StressClientCallable.java
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/rpchandler/RpcHandlerDefault.java

index 539f6ab3158d66a933c7af6fd825684bccae536c..9c0de4d55c6cda8b51060c142410ce0dcd243923 100644 (file)
@@ -21,7 +21,7 @@ import org.opendaylight.netconf.client.NetconfClientSessionListener;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordDeprecatedBuilder;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
@@ -67,7 +67,7 @@ class CallHomeMountSessionContext {
         node.setHost(new Host(key.getIpAddress()));
         node.setPort(key.getPort());
         node.setTcpOnly(Boolean.FALSE);
-        node.setCredentials(new LoginPasswordBuilder().setUsername("ommited").setPassword("ommited").build());
+        node.setCredentials(new LoginPasswordDeprecatedBuilder().setUsername("ommited").setPassword("ommited").build());
         node.setSchemaless(Boolean.FALSE);
         return node.build();
     }
index 10f7ae893d8aa6e4d9e1dc8d69703b20863700a7..7f25c250314e6b3583341d826e17279d873b7f68 100644 (file)
@@ -30,7 +30,7 @@ import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration.NetconfClientProtocol;
 import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder;
 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
-import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
+import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
 import org.opendaylight.protocol.framework.NeverReconnectStrategy;
 
 
@@ -108,7 +108,7 @@ public class TestingNetconfClient implements Closeable {
         NioEventLoopGroup nettyGroup = new NioEventLoopGroup();
         NetconfClientDispatcherImpl netconfClientDispatcher = new NetconfClientDispatcherImpl(nettyGroup, nettyGroup,
                 hashedWheelTimer);
-        LoginPassword authHandler = new LoginPassword("admin", "admin");
+        LoginPasswordHandler authHandler = new LoginPasswordHandler("admin", "admin");
         TestingNetconfClient client = new TestingNetconfClient("client", netconfClientDispatcher,
                 getClientConfig("127.0.0.1", 1830, true, Optional.of(authHandler)));
         System.console().writer().println(client.getCapabilities());
index 6ecaec63b2e6718e6bd8dd68351401388d922f01..6cffd1b164aedb74db928ba5ef6d633189921e7b 100644 (file)
@@ -20,7 +20,7 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordDeprecatedBuilder;
 
 @Command(name = "netconf:connect-device", scope = "netconf", description = "Connect to a netconf device.")
 public class NetconfConnectDeviceCommand extends AbstractAction {
@@ -95,7 +95,8 @@ public class NetconfConnectDeviceCommand extends AbstractAction {
 
         final boolean isTcpOnly = connectionType.equals("true");
         final boolean isSchemaless = schemaless.equals("true");
-        final Credentials credentials = new LoginPasswordBuilder().setPassword(password).setUsername(username).build();
+        final Credentials credentials =
+                new LoginPasswordDeprecatedBuilder().setPassword(password).setUsername(username).build();
 
         final NetconfNode netconfNode = new NetconfNodeBuilder()
                                         .setHost(new Host(new IpAddress(new Ipv4Address(deviceIp))))
index 9a9fa6e1ee09c7bf665027827c2f1144cc2c428a..be851a1b236355a799bf338c866157fd1ada15bd 100644 (file)
@@ -37,7 +37,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev15
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordDeprecatedBuilder;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
@@ -223,7 +223,7 @@ public class NetconfCommandsImpl implements NetconfCommands {
                     ? updated.get(NetconfConsoleConstants.PASSWORD) : password;
 
             final Credentials credentials =
-                    new LoginPasswordBuilder().setPassword(newPassword).setUsername(newUsername).build();
+                    new LoginPasswordDeprecatedBuilder().setPassword(newPassword).setUsername(newUsername).build();
             final NetconfNode updatedNetconfNode = new NetconfNodeBuilder()
                     .setHost(new Host(new IpAddress(new Ipv4Address(deviceIp))))
                     .setPort(new PortNumber(Integer.decode(devicePort)))
index d52b93dd25b2e2490cf40ff791036451d9cf2b79..ffddaa2463cb718f608c55d23c4872ef0f258635 100644 (file)
@@ -15,16 +15,44 @@ module netconf-node-topology {
         }
     }
 
-    grouping netconf-node-credentials {
+    grouping username-password {
+        leaf username {
+            type string;
+        }
+
+        leaf password {
+            type string;
+        }
+    }
 
+    grouping netconf-node-credentials {
         choice credentials {
             config true;
-            case login-password {
-                leaf username {
-                    type string;
+            case login-password-deprecated {
+                description "Deprecated way of storing credentials, unencrypted.";
+
+                status deprecated;
+                uses username-password;
+            }
+            case login-pw {
+                description "login-password credentials, encrypted.";
+
+
+                container login-password {
+                    uses username-password;
                 }
+            }
+            case login-pw-unencrypted {
+                description "login-password credentials, not encrypted.";
+
+                container login-password-unencrypted {
+                    uses username-password;
+                }
+            }
+            case key-based {
+                description "key-pair based authentication, use the id for the pair thats stored in the keystore.";
 
-                leaf password {
+                leaf pair-id {
                     type string;
                 }
             }
@@ -143,6 +171,15 @@ module netconf-node-topology {
             description "Limit of concurrent messages that can be send before reply messages are received.
                          If value <1 is provided, no limit will be enforced";
         }
+
+        leaf actor-response-wait-time {
+                    config true;
+                    type uint16 {
+                      range "1..max";
+                    }
+                    default 5;
+                    description "Time that slave actor will wait for response from master.";
+        }
     }
 
     grouping netconf-node-connection-status {
@@ -263,10 +300,20 @@ module netconf-node-topology {
 
     }
 
+    rpc add-netconf-node {
+        input {
+            uses netconf-node-fields;
+            leaf node-id {
+                type string;
+            }
+        }
+    }
+
     augment "/nt:network-topology/nt:topology/nt:node" {
         when "../../nt:topology-types/topology-netconf";
         ext:augment-identifier "netconf-node";
 
         uses netconf-node-fields;
     }
+
 }
@@ -11,25 +11,18 @@ package org.opendaylight.netconf.nettyutil.handler.ssh.authentication;
 import java.io.IOException;
 import org.apache.sshd.ClientSession;
 import org.apache.sshd.client.future.AuthFuture;
-import org.opendaylight.aaa.encrypt.AAAEncryptionService;
 
 /**
  * Class Providing username/password authentication option to
  * {@link org.opendaylight.netconf.nettyutil.handler.ssh.client.AsyncSshHandler}.
  */
-public class LoginPassword extends AuthenticationHandler {
+public class LoginPasswordHandler extends AuthenticationHandler {
     protected final String username;
     protected final String password;
-    protected final AAAEncryptionService encryptionService;
 
-    public LoginPassword(String username, String password) {
-        this(username, password, null);
-    }
-
-    public LoginPassword(final String username, final String password, final AAAEncryptionService encryptionService) {
+    public LoginPasswordHandler(final String username, final String password) {
         this.username = username;
         this.password = password;
-        this.encryptionService = encryptionService;
     }
 
     @Override
@@ -39,12 +32,7 @@ public class LoginPassword extends AuthenticationHandler {
 
     @Override
     public AuthFuture authenticate(final ClientSession session) throws IOException {
-        if (encryptionService != null) {
-            String decryptedPassword = encryptionService.decrypt(password);
-            session.addPasswordIdentity(decryptedPassword);
-        } else {
-            session.addPasswordIdentity(password);
-        }
+        session.addPasswordIdentity(password);
         return session.auth();
     }
 }
index 420a216bd647f02caff739019969080a38262061..70367c04ceb5d8ca0f06a1059b5cfd847518fca4 100644 (file)
@@ -12,7 +12,6 @@ import java.io.IOException;
 import java.security.KeyPair;
 import org.apache.sshd.ClientSession;
 import org.apache.sshd.client.future.AuthFuture;
-import org.opendaylight.aaa.encrypt.AAAEncryptionService;
 import org.opendaylight.aaa.encrypt.PKIUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -20,13 +19,13 @@ import org.slf4j.LoggerFactory;
 /**
  * Represents Auth information for the public key based authentication for netconf.
  */
-public class PublicKeyAuth extends LoginPassword {
+public class PublicKeyAuth extends LoginPasswordHandler {
     private KeyPair keyPair = null;
     private static final Logger LOG = LoggerFactory.getLogger(PublicKeyAuth.class);
 
     public PublicKeyAuth(String username, String password, String keyPath,
-            String passPhrase, AAAEncryptionService encryptionService) {
-        super(username, password, encryptionService);
+                         String passPhrase) {
+        super(username, password);
         try {
             boolean isKeyPathAbsent = Strings.isNullOrEmpty(keyPath);
             passPhrase = Strings.isNullOrEmpty(passPhrase) ? "" : passPhrase;
@@ -18,17 +18,17 @@ import org.apache.sshd.ClientSession;
 import org.apache.sshd.client.future.AuthFuture;
 import org.junit.Test;
 
-public class LoginPasswordTest {
+public class LoginPasswordHandlerTest {
 
     @Test
     public void testLoginPassword() throws Exception {
-        final LoginPassword loginPassword = new LoginPassword("user", "pwd");
-        assertEquals("user", loginPassword.getUsername());
+        final LoginPasswordHandler loginPasswordHandler = new LoginPasswordHandler("user", "pwd");
+        assertEquals("user", loginPasswordHandler.getUsername());
 
         final ClientSession session = mock(ClientSession.class);
         doNothing().when(session).addPasswordIdentity("pwd");
         doReturn(mock(AuthFuture.class)).when(session).auth();
-        loginPassword.authenticate(session);
+        loginPasswordHandler.authenticate(session);
 
         verify(session).addPasswordIdentity("pwd");
         verify(session).auth();
index 17af06e796d9eec8ea16fb77d6558c737611a837..f525674436d9a9e01eef95575199edfc032e9187 100644 (file)
@@ -70,6 +70,8 @@ public class SshProxyServer implements AutoCloseable {
             (username, password, session)
                 -> sshProxyServerConfiguration.getAuthenticator().authenticated(username, password));
 
+        sshProxyServerConfiguration.getPublickeyAuthenticator().ifPresent(sshServer::setPublickeyAuthenticator);
+
         sshServer.setKeyPairProvider(sshProxyServerConfiguration.getKeyPairProvider());
 
         sshServer.setIoServiceFactoryFactory(nioServiceWithPoolFactoryFactory);
index 9fe57a40b692105fcaa2568dba7446c160ad5dd6..75692ba575f604f314a45ebde1a577acdeeb0b5c 100644 (file)
@@ -11,7 +11,9 @@ package org.opendaylight.netconf.ssh;
 import com.google.common.base.Preconditions;
 import io.netty.channel.local.LocalAddress;
 import java.net.InetSocketAddress;
+import java.util.Optional;
 import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.server.PublickeyAuthenticator;
 import org.opendaylight.netconf.auth.AuthProvider;
 
 public final class SshProxyServerConfiguration {
@@ -20,9 +22,16 @@ public final class SshProxyServerConfiguration {
     private final AuthProvider authenticator;
     private final KeyPairProvider keyPairProvider;
     private final int idleTimeout;
+    private final Optional<PublickeyAuthenticator> publickeyAuthenticator;
 
     SshProxyServerConfiguration(final InetSocketAddress bindingAddress, final LocalAddress localAddress,
                     final AuthProvider authenticator, final KeyPairProvider keyPairProvider, final int idleTimeout) {
+        this(bindingAddress, localAddress, authenticator, null, keyPairProvider, idleTimeout);
+    }
+
+    SshProxyServerConfiguration(final InetSocketAddress bindingAddress, final LocalAddress localAddress,
+                                final AuthProvider authenticator, final PublickeyAuthenticator publickeyAuthenticator,
+                                final KeyPairProvider keyPairProvider, final int idleTimeout) {
         this.bindingAddress = Preconditions.checkNotNull(bindingAddress);
         this.localAddress = Preconditions.checkNotNull(localAddress);
         this.authenticator = Preconditions.checkNotNull(authenticator);
@@ -30,6 +39,7 @@ public final class SshProxyServerConfiguration {
         // Idle timeout cannot be disabled in the sshd by using =< 0 value
         Preconditions.checkArgument(idleTimeout > 0, "Idle timeout has to be > 0");
         this.idleTimeout = idleTimeout;
+        this.publickeyAuthenticator = Optional.ofNullable(publickeyAuthenticator);
     }
 
     public InetSocketAddress getBindingAddress() {
@@ -52,5 +62,7 @@ public final class SshProxyServerConfiguration {
         return idleTimeout;
     }
 
-
+    public Optional<PublickeyAuthenticator> getPublickeyAuthenticator() {
+        return publickeyAuthenticator;
+    }
 }
index 5b7948ed6fde4a803e10e2a96c2cbafd19d9224a..6d9a364af9aadacd70601fd4b48851a431949a21 100644 (file)
@@ -11,6 +11,7 @@ package org.opendaylight.netconf.ssh;
 import io.netty.channel.local.LocalAddress;
 import java.net.InetSocketAddress;
 import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.server.PublickeyAuthenticator;
 import org.opendaylight.netconf.auth.AuthProvider;
 
 public final class SshProxyServerConfigurationBuilder {
@@ -19,6 +20,7 @@ public final class SshProxyServerConfigurationBuilder {
     private AuthProvider authenticator;
     private KeyPairProvider keyPairProvider;
     private int idleTimeout;
+    private PublickeyAuthenticator publicKeyAuthenticator = null;
 
     public SshProxyServerConfigurationBuilder setBindingAddress(final InetSocketAddress bindingAddress) {
         this.bindingAddress = bindingAddress;
@@ -35,6 +37,11 @@ public final class SshProxyServerConfigurationBuilder {
         return this;
     }
 
+    public SshProxyServerConfigurationBuilder setPublickeyAuthenticator(final PublickeyAuthenticator authenticator) {
+        this.publicKeyAuthenticator = authenticator;
+        return this;
+    }
+
     public SshProxyServerConfigurationBuilder setKeyPairProvider(final KeyPairProvider keyPairProvider) {
         this.keyPairProvider = keyPairProvider;
         return this;
@@ -46,7 +53,7 @@ public final class SshProxyServerConfigurationBuilder {
     }
 
     public SshProxyServerConfiguration createSshProxyServerConfiguration() {
-        return new SshProxyServerConfiguration(bindingAddress, localAddress, authenticator,
+        return new SshProxyServerConfiguration(bindingAddress, localAddress, authenticator, publicKeyAuthenticator,
                 keyPairProvider, idleTimeout);
     }
 
index d9a0756978ecf528f70a449cf8a138c856c208af..a2da0dafb761e61744b319c6cd7290a90a5f27e4 100644 (file)
@@ -31,7 +31,7 @@ import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.opendaylight.netconf.netty.EchoClientHandler.State;
-import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
+import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
 import org.opendaylight.netconf.nettyutil.handler.ssh.client.AsyncSshHandler;
 import org.opendaylight.netconf.ssh.SshProxyServer;
 import org.opendaylight.netconf.ssh.SshProxyServerConfigurationBuilder;
@@ -109,7 +109,7 @@ public class SSHTest {
         final ChannelInitializer<NioSocketChannel> channelInitializer = new ChannelInitializer<NioSocketChannel>() {
             @Override
             public void initChannel(final NioSocketChannel ch) throws Exception {
-                ch.pipeline().addFirst(AsyncSshHandler.createForNetconfSubsystem(new LoginPassword("a", "a")));
+                ch.pipeline().addFirst(AsyncSshHandler.createForNetconfSubsystem(new LoginPasswordHandler("a", "a")));
                 ch.pipeline().addLast(echoClientHandler);
             }
         };
index 72ca8da1afae7b194e94f0cbb5e5dd5029d5f6c9..477a90b247af56a096221cacd365acd2f2ed51e9 100755 (executable)
 
     <odl:rpc-implementation ref="netconfNodeRegisterEncryptedRPC"/>
 
+    <bean id="netconfKeystoreProvider"
+          class="org.opendaylight.netconf.sal.connect.util.NetconfSalKeystoreService">
+        <argument ref="dataBroker"/>
+        <argument ref="encryptionService"/>
+    </bean>
+
+    <odl:rpc-implementation ref="netconfKeystoreProvider"/>
+
 </blueprint>
index 826342d23f2f4fe12881860238d385c74eb4dc60..6ca3eb4a51d1ec8eebb89a368f11bae50de55345 100644 (file)
@@ -35,18 +35,20 @@ import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
-import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.PublicKeyAuth;
+import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
 import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
 import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice;
 import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder;
 import org.opendaylight.netconf.sal.connect.netconf.SchemalessNetconfDevice;
+import org.opendaylight.netconf.sal.connect.netconf.auth.DatastoreBackedPublicKeyAuth;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences;
 import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade;
+import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfKeystoreAdapter;
 import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.netconf.topology.singleton.api.RemoteDeviceConnector;
@@ -61,6 +63,13 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability.CapabilityOrigin;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.KeyAuth;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordDeprecated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencrypted;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.key.auth.KeyBased;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPassword;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencrypted;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
@@ -84,6 +93,7 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector {
     private final String privateKeyPassphrase;
     private final AAAEncryptionService encryptionService;
     private NetconfConnectorDTO deviceCommunicatorDTO;
+    private final NetconfKeystoreAdapter keystoreAdapter;
 
     public RemoteDeviceConnectorImpl(final NetconfTopologySetup netconfTopologyDeviceSetup,
                                      final RemoteDeviceId remoteDeviceId, final Timeout actorResponseWaitTime,
@@ -96,6 +106,7 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector {
         this.privateKeyPath = netconfTopologyDeviceSetup.getPrivateKeyPath();
         this.privateKeyPassphrase = netconfTopologyDeviceSetup.getPrivateKeyPassphrase();
         this.encryptionService = netconfTopologyDeviceSetup.getEncryptionService();
+        keystoreAdapter = new NetconfKeystoreAdapter(netconfTopologyDeviceSetup.getDataBroker());
     }
 
     @Override
@@ -279,20 +290,7 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector {
                         betweenAttemptsTimeoutMillis, sleepFactor);
         final ReconnectStrategy strategy = sf.createReconnectStrategy();
 
-        final AuthenticationHandler authHandler;
-        final Credentials credentials = node.getCredentials();
-        if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf
-                .node.credentials.credentials.LoginPassword) {
-            authHandler = new PublicKeyAuth(
-                    ((org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf
-                            .node.credentials.credentials.LoginPassword) credentials).getUsername(),
-                    ((org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf
-                            .node.credentials.credentials.LoginPassword) credentials).getPassword(),
-                    this.privateKeyPath, this.privateKeyPassphrase, encryptionService);
-
-        } else {
-            throw new IllegalStateException(remoteDeviceId + ": Only login/password authentication is supported");
-        }
+        final AuthenticationHandler authHandler = getHandlerFromCredentials(node.getCredentials());
 
         return NetconfReconnectingClientConfigurationBuilder.create()
                 .withAddress(socketAddress)
@@ -307,6 +305,29 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector {
                 .build();
     }
 
+    private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) {
+        if (credentials instanceof LoginPasswordDeprecated) {
+            final LoginPasswordDeprecated loginPassword = (LoginPasswordDeprecated) credentials;
+            return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
+        }
+        if (credentials instanceof LoginPwUnencrypted) {
+            final LoginPasswordUnencrypted loginPassword =
+                    ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted();
+            return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
+        }
+        if (credentials instanceof LoginPw) {
+            final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword();
+            return new LoginPasswordHandler(loginPassword.getUsername(),
+                    encryptionService.decrypt(loginPassword.getPassword()));
+        }
+        if (credentials instanceof KeyAuth) {
+            final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased();
+            return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(),
+                    keystoreAdapter, encryptionService);
+        }
+        throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
+    }
+
     private static final class TimedReconnectStrategyFactory implements ReconnectStrategyFactory {
         private final Long connectionAttempts;
         private final EventExecutor executor;
index 9745e87c2db5e9e4ed746b0d26f724a6d781844b..72de5a21c84ef6ce2b3ce7eeb0a1d85c884caed2 100644 (file)
@@ -60,7 +60,7 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordDeprecatedBuilder;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
@@ -134,7 +134,8 @@ public class RemoteDeviceConnectorImplTest {
 
     @Test
     public void testStopRemoteDeviceConnection() {
-        final Credentials credentials = new LoginPasswordBuilder().setPassword("admin").setUsername("admin").build();
+        final Credentials credentials = new LoginPasswordDeprecatedBuilder()
+                .setPassword("admin").setUsername("admin").build();
         final NetconfNode netconfNode = new NetconfNodeBuilder()
                 .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1"))))
                 .setPort(new PortNumber(9999))
@@ -173,7 +174,8 @@ public class RemoteDeviceConnectorImplTest {
         final ExecutorService executorService = mock(ExecutorService.class);
         doReturn(executorService).when(processingExecutor).getExecutor();
 
-        final Credentials credentials = new LoginPasswordBuilder().setPassword("admin").setUsername("admin").build();
+        final Credentials credentials = new LoginPasswordDeprecatedBuilder()
+                .setPassword("admin").setUsername("admin").build();
         final NetconfNode netconfNode = new NetconfNodeBuilder()
                 .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1"))))
                 .setPort(new PortNumber(9999))
@@ -203,7 +205,8 @@ public class RemoteDeviceConnectorImplTest {
         final ExecutorService executorService = mock(ExecutorService.class);
         doReturn(executorService).when(processingExecutor).getExecutor();
 
-        final Credentials credentials = new LoginPasswordBuilder().setPassword("admin").setUsername("admin").build();
+        final Credentials credentials = new LoginPasswordDeprecatedBuilder()
+                .setPassword("admin").setUsername("admin").build();
         final NetconfNode netconfNode = new NetconfNodeBuilder()
                 .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1"))))
                 .setPort(new PortNumber(9999))
@@ -242,7 +245,7 @@ public class RemoteDeviceConnectorImplTest {
                 .setDefaultRequestTimeoutMillis(2000L)
                 .setHost(host)
                 .setPort(portNumber)
-                .setCredentials(new LoginPasswordBuilder()
+                .setCredentials(new LoginPasswordDeprecatedBuilder()
                         .setUsername("testuser")
                         .setPassword("testpassword").build())
                 .setTcpOnly(true)
index d430ce2f1c4cc2243cd12c0adfddbeaa513358ef..511146c3017724fd63cc3dda268f70577e2e1483 100644 (file)
@@ -37,7 +37,7 @@ import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
-import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.PublicKeyAuth;
+import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
 import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
@@ -45,11 +45,13 @@ import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice;
 import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder;
 import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemasResolverImpl;
 import org.opendaylight.netconf.sal.connect.netconf.SchemalessNetconfDevice;
+import org.opendaylight.netconf.sal.connect.netconf.auth.DatastoreBackedPublicKeyAuth;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences;
 import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade;
+import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfKeystoreAdapter;
 import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.netconf.topology.api.NetconfTopology;
@@ -62,6 +64,13 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability.CapabilityOrigin;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.KeyAuth;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordDeprecated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencrypted;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.key.auth.KeyBased;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPassword;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencrypted;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
@@ -162,6 +171,7 @@ public abstract class AbstractNetconfTopology implements NetconfTopology {
     protected final SharedSchemaRepository sharedSchemaRepository;
     protected final DataBroker dataBroker;
     protected final DOMMountPointService mountPointService;
+    private final NetconfKeystoreAdapter keystoreAdapter;
     protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
     protected SchemaRepository schemaRepository = DEFAULT_SCHEMA_REPOSITORY;
     protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
@@ -185,6 +195,8 @@ public abstract class AbstractNetconfTopology implements NetconfTopology {
         this.dataBroker = dataBroker;
         this.mountPointService = mountPointService;
         this.encryptionService = encryptionService;
+
+        this.keystoreAdapter = new NetconfKeystoreAdapter(dataBroker);
     }
 
     public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) {
@@ -434,19 +446,7 @@ public abstract class AbstractNetconfTopology implements NetconfTopology {
                 maxConnectionAttempts, betweenAttemptsTimeoutMillis, sleepFactor);
         final ReconnectStrategy strategy = sf.createReconnectStrategy();
 
-        final AuthenticationHandler authHandler;
-        final Credentials credentials = node.getCredentials();
-        if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114
-                .netconf.node.credentials.credentials.LoginPassword) {
-            authHandler = new PublicKeyAuth(
-                    ((org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114
-                            .netconf.node.credentials.credentials.LoginPassword) credentials).getUsername(),
-                    ((org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114
-                            .netconf.node.credentials.credentials.LoginPassword) credentials).getPassword(),
-                     privateKeyPath, privateKeyPassphrase, encryptionService);
-        } else {
-            throw new IllegalStateException("Only login/password authentification is supported");
-        }
+        final AuthenticationHandler authHandler = getHandlerFromCredentials(node.getCredentials());
 
         return NetconfReconnectingClientConfigurationBuilder.create()
                 .withAddress(socketAddress)
@@ -460,6 +460,29 @@ public abstract class AbstractNetconfTopology implements NetconfTopology {
                 .build();
     }
 
+    private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) {
+        if (credentials instanceof LoginPasswordDeprecated) {
+            final LoginPasswordDeprecated loginPassword = (LoginPasswordDeprecated) credentials;
+            return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
+        }
+        if (credentials instanceof LoginPwUnencrypted) {
+            final LoginPasswordUnencrypted loginPassword =
+                    ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted();
+            return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
+        }
+        if (credentials instanceof LoginPw) {
+            final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword();
+            return new LoginPasswordHandler(loginPassword.getUsername(),
+                    encryptionService.decrypt(loginPassword.getPassword()));
+        }
+        if (credentials instanceof KeyAuth) {
+            final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased();
+            return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(),
+                    keystoreAdapter, encryptionService);
+        }
+        throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
+    }
+
     protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id);
 
     private InetSocketAddress getSocketAddress(final Host host, final int port) {
index c644286504d6248fb034bffd700df7b455c42126..7017d807bc2cc005158a7d8d1d230b1095f47d3a 100644 (file)
@@ -23,7 +23,8 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPasswordBuilder;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
@@ -58,9 +59,12 @@ public class NetconfConnectorFactoryImpl implements NetconfConnectorFactory {
 
         final NodeId nodeId = new NodeId(instanceName);
         final NodeKey nodeKey = new NodeKey(nodeId);
-        final Credentials credentials = new LoginPasswordBuilder()
-                .setUsername(username)
-                .setPassword(password)
+        final Credentials credentials = new LoginPwBuilder()
+                .setLoginPassword(
+                        new LoginPasswordBuilder()
+                                .setUsername(username)
+                                .setPassword(password)
+                                .build())
                 .build();
         final Host host = HostBuilder.getDefaultInstance(address);
         final PortNumber portNumber = new PortNumber(port);
index fa4ac32e158553dd37cb4198cc46bf3f6a4b6906..c92fb9a45660ad6a55740dd1a7d9fb37c4c95784 100644 (file)
@@ -49,7 +49,7 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordDeprecatedBuilder;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopologyBuilder;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
@@ -161,7 +161,8 @@ public class NetconfTopologyImplTest {
                 .setBetweenAttemptsTimeoutMillis(100)
                 .setKeepaliveDelay(1000L)
                 .setTcpOnly(true)
-                .setCredentials(new LoginPasswordBuilder().setUsername("testuser").setPassword("testpassword").build())
+                .setCredentials(new LoginPasswordDeprecatedBuilder()
+                        .setUsername("testuser").setPassword("testpassword").build())
                 .build();
 
         final NodeBuilder nn = new NodeBuilder().addAugmentation(NetconfNode.class, testingNode);
diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/auth/DatastoreBackedPublicKeyAuth.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/auth/DatastoreBackedPublicKeyAuth.java
new file mode 100644 (file)
index 0000000..7c750cc
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2017 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.sal.connect.netconf.auth;
+
+import com.google.common.base.Strings;
+import java.io.IOException;
+import java.io.StringReader;
+import java.security.KeyPair;
+import java.util.Optional;
+import org.apache.sshd.ClientSession;
+import org.apache.sshd.client.future.AuthFuture;
+import org.opendaylight.aaa.encrypt.AAAEncryptionService;
+import org.opendaylight.aaa.encrypt.PKIUtil;
+import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
+import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfKeystoreAdapter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.keystore.entry.KeyCredential;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DatastoreBackedPublicKeyAuth extends AuthenticationHandler {
+
+    private static final Logger LOG = LoggerFactory.getLogger(DatastoreBackedPublicKeyAuth.class);
+
+    private final String username;
+    private final String pairId;
+    private final NetconfKeystoreAdapter keystoreAdapter;
+    private final AAAEncryptionService encryptionService;
+
+    private Optional<KeyPair> keyPair = Optional.empty();
+
+    public DatastoreBackedPublicKeyAuth(final String username, final String pairId,
+                                        final NetconfKeystoreAdapter keystoreAdapter,
+                                        final AAAEncryptionService encryptionService) {
+        this.username = username;
+        this.pairId = pairId;
+        this.keystoreAdapter = keystoreAdapter;
+        this.encryptionService = encryptionService;
+
+        // try to immediately retrieve the pair from the adapter
+        tryToSetKeyPair();
+    }
+
+    @Override
+    public String getUsername() {
+        return username;
+    }
+
+    @Override
+    public AuthFuture authenticate(ClientSession session) throws IOException {
+        // if we have keypair set the identity, otherwise retry the retrieval from the adapter
+        // if successful set the identity.
+        if (keyPair.isPresent() || tryToSetKeyPair()) {
+            session.addPublicKeyIdentity(keyPair.get());
+        }
+        return session.auth();
+    }
+
+    private boolean tryToSetKeyPair() {
+        LOG.debug("Trying to retrieve keypair for: {}", pairId);
+        final Optional<KeyCredential> keypairOptional = keystoreAdapter.getKeypairFromId(pairId);
+
+        if (keypairOptional.isPresent()) {
+            final KeyCredential dsKeypair = keypairOptional.get();
+            final String passPhrase = Strings.isNullOrEmpty(dsKeypair.getPassphrase()) ? "" : dsKeypair.getPassphrase();
+
+            try {
+                this.keyPair = Optional.of(
+                        new PKIUtil().decodePrivateKey(
+                                new StringReader(encryptionService.decrypt(dsKeypair.getPrivateKey())),
+                                encryptionService.decrypt(passPhrase)));
+            } catch (IOException exception) {
+                LOG.warn("Unable to decode private key, id={}", pairId, exception);
+                return false;
+            }
+            return true;
+        }
+        LOG.debug("Unable to retrieve keypair for: {}", pairId);
+        return false;
+    }
+}
diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfKeystoreAdapter.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfKeystoreAdapter.java
new file mode 100644 (file)
index 0000000..8363c54
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.sal.connect.netconf.sal;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.Keystore;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.keystore.entry.KeyCredential;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NetconfKeystoreAdapter implements ClusteredDataTreeChangeListener<Keystore> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(NetconfKeystoreAdapter.class);
+
+    private final InstanceIdentifier<Keystore> keystoreIid = InstanceIdentifier.create(Keystore.class);
+
+    private final DataBroker dataBroker;
+    private final Map<String, KeyCredential> pairs = Collections.synchronizedMap(new HashMap<>());
+
+    public NetconfKeystoreAdapter(final DataBroker dataBroker) {
+        this.dataBroker = dataBroker;
+
+        dataBroker.registerDataTreeChangeListener(
+                new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, keystoreIid), this);
+    }
+
+    public Optional<KeyCredential> getKeypairFromId(final String keyId) {
+        final KeyCredential keypair = pairs.get(keyId);
+        return Optional.ofNullable(keypair);
+    }
+
+    @Override
+    public void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<Keystore>> changes) {
+        LOG.debug("Keystore updated: {}", changes);
+        final Keystore dataAfter = changes.iterator().next().getRootNode().getDataAfter();
+
+        pairs.clear();
+        if (dataAfter != null) {
+            dataAfter.getKeyCredential().forEach(pair -> pairs.put(pair.getKey().getKeyId(), pair));
+        }
+    }
+}
diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/util/NetconfSalKeystoreService.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/util/NetconfSalKeystoreService.java
new file mode 100644 (file)
index 0000000..7cb9216
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2017 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.sal.connect.util;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.SettableFuture;
+import java.util.List;
+import java.util.concurrent.Future;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import org.opendaylight.aaa.encrypt.AAAEncryptionService;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddKeystoreEntryInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.Keystore;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.KeystoreBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.NetconfKeystoreService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveKeystoreEntryInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.keystore.entry.KeyCredential;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.keystore.entry.KeyCredentialBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.keystore.entry.KeyCredentialKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NetconfSalKeystoreService implements NetconfKeystoreService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(NetconfSalKeystoreService.class);
+
+    private final DataBroker dataBroker;
+    private final AAAEncryptionService encryptionService;
+
+    private final InstanceIdentifier<Keystore> keystoreIid = InstanceIdentifier.create(Keystore.class);
+
+    public NetconfSalKeystoreService(final DataBroker dataBroker,
+                                     final AAAEncryptionService encryptionService) {
+        LOG.info("Starting NETCONF keystore service.");
+
+        this.dataBroker = dataBroker;
+        this.encryptionService = encryptionService;
+
+        initKeystore();
+    }
+
+    private void initKeystore() {
+        final Keystore keystore = new KeystoreBuilder().build();
+
+        final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
+        writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, keystoreIid, keystore);
+
+        final CheckedFuture<Void, TransactionCommitFailedException> submit = writeTransaction.submit();
+
+        try {
+            submit.checkedGet();
+            LOG.debug("init keystore done");
+        } catch (TransactionCommitFailedException exception) {
+            LOG.error("Unable to initialize Netconf key-pair store.", exception);
+        }
+    }
+
+    @Override
+    public Future<RpcResult<Void>> removeKeystoreEntry(final RemoveKeystoreEntryInput input) {
+        LOG.debug("Removing keypairs: {}", input);
+
+        final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
+        final List<String> ids = input.getKeyId();
+
+        for (final String id : ids) {
+            writeTransaction.delete(LogicalDatastoreType.CONFIGURATION,
+                    keystoreIid.child(KeyCredential.class, new KeyCredentialKey(id)));
+        }
+
+        final SettableFuture<RpcResult<Void>> rpcResult = SettableFuture.create();
+
+        final CheckedFuture<Void, TransactionCommitFailedException> submit = writeTransaction.submit();
+        Futures.addCallback(submit, new FutureCallback<Void>() {
+            @Override
+            public void onSuccess(@Nullable final Void result) {
+                LOG.debug("remove-key-pair success. Input: {}");
+                final RpcResult<Void> success = RpcResultBuilder.<Void>success().build();
+                rpcResult.set(success);
+            }
+
+            @Override
+            public void onFailure(final Throwable throwable) {
+                LOG.warn("remove-key-pair failed. Input: {}", input, throwable);
+                rpcResult.setException(throwable);
+            }
+        }, MoreExecutors.directExecutor());
+
+        return rpcResult;
+    }
+
+    @Override
+    public Future<RpcResult<Void>> addKeystoreEntry(final AddKeystoreEntryInput input) {
+        LOG.debug("Adding keypairs: {}", input);
+
+        final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
+        final List<KeyCredential> keypairs = input.getKeyCredential().stream().map(keypair ->
+                new KeyCredentialBuilder(keypair)
+                        .setPrivateKey(encryptionService.encrypt(keypair.getPrivateKey()))
+                        .setPassphrase(encryptionService.encrypt(keypair.getPassphrase()))
+                        .build()).collect(Collectors.toList());
+
+        for (KeyCredential keypair : keypairs) {
+            writeTransaction.merge(LogicalDatastoreType.CONFIGURATION,
+                    keystoreIid.child(KeyCredential.class, keypair.getKey()), keypair);
+        }
+
+        final SettableFuture<RpcResult<Void>> rpcResult = SettableFuture.create();
+
+        final CheckedFuture<Void, TransactionCommitFailedException> submit = writeTransaction.submit();
+        Futures.addCallback(submit, new FutureCallback<Void>() {
+            @Override
+            public void onSuccess(@Nullable final Void result) {
+                LOG.debug("add-key-pair success. Input: {}");
+                final RpcResult<Void> success = RpcResultBuilder.<Void>success().build();
+                rpcResult.set(success);
+            }
+
+            @Override
+            public void onFailure(final Throwable throwable) {
+                LOG.warn("add-key-pair failed. Input: {}", input, throwable);
+                rpcResult.setException(throwable);
+            }
+        }, MoreExecutors.directExecutor());
+
+        return rpcResult;
+    }
+}
index 4edbedf6c265830028c82a72af1527aadd75afe5..c963022e06d7c00820fac5e6b792b6695117ef9b 100644 (file)
@@ -7,10 +7,11 @@
  */
 package org.opendaylight.netconf.sal.connect.util;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
-import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.common.util.concurrent.SettableFuture;
 import java.util.concurrent.Future;
@@ -18,13 +19,16 @@ import org.opendaylight.aaa.encrypt.AAAEncryptionService;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.AddNetconfNodeInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.CreateDeviceInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.DeleteDeviceInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeTopologyService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPassword;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPassword;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPasswordBuilder;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
@@ -53,56 +57,99 @@ public class NetconfTopologyRPCProvider implements NetconfNodeTopologyService {
     }
 
     @Override
-    public Future<RpcResult<Void>> addNetconfNode(AddNetconfNodeInput input) {
-        NetconfNode node = this.encryptPassword(input);
+    public Future<RpcResult<Void>> createDevice(final CreateDeviceInput input) {
+        final NetconfNode node = this.encryptPassword(input);
         final SettableFuture<RpcResult<Void>> futureResult = SettableFuture.create();
-        NodeId nodeId = new NodeId(input.getNodeId());
+        final NodeId nodeId = new NodeId(input.getNodeId());
         writeToConfigDS(node, nodeId, topologyId, futureResult);
         return futureResult;
     }
 
-    private NetconfNode encryptPassword(AddNetconfNodeInput input) {
-        NetconfNodeBuilder builder = new NetconfNodeBuilder();
+    @VisibleForTesting
+    public NetconfNode encryptPassword(final CreateDeviceInput input) {
+        final NetconfNodeBuilder builder = new NetconfNodeBuilder();
         builder.fieldsFrom(input);
 
-        boolean encrypt = input.isEncrypt();
-        LoginPassword loginPassword = (LoginPassword) input.getCredentials();
-        if (encrypt) {
-            String encryptedPassword = encryptionService.encrypt(loginPassword.getPassword());
-            LoginPassword newCreds = new LoginPasswordBuilder().setPassword(encryptedPassword)
-                    .setUsername(loginPassword.getUsername()).build();
-            builder.setCredentials(newCreds);
+        final Credentials credentials = handleEncryption(input.getCredentials());
+        builder.setCredentials(credentials);
+
+        return builder.build();
+    }
+
+    private Credentials handleEncryption(final Credentials credentials) {
+        if (credentials instanceof LoginPw) {
+            final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword();
+            final String encryptedPassword =
+                    encryptionService.encrypt(loginPassword.getPassword());
+
+            return new LoginPwBuilder().setLoginPassword(new LoginPasswordBuilder()
+                    .setPassword(encryptedPassword)
+                    .setUsername(loginPassword.getUsername()).build()).build();
         }
 
-        NetconfNode node = builder.build();
-        return node;
+        // nothing else needs to be encrypted
+        return credentials;
     }
 
-    private void writeToConfigDS(NetconfNode node, NodeId nodeId, String topologyId,
+    private void writeToConfigDS(final NetconfNode node, final NodeId nodeId, final String topologyId,
                                  final SettableFuture<RpcResult<Void>> futureResult) {
 
-        WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
+        final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
         final InstanceIdentifier<NetworkTopology> networkTopologyId =
                 InstanceIdentifier.builder(NetworkTopology.class).build();
         final InstanceIdentifier<NetconfNode> niid = networkTopologyId.child(Topology.class,
                 new TopologyKey(new TopologyId(topologyId))).child(Node.class,
                 new NodeKey(nodeId)).augmentation(NetconfNode.class);
         writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, niid, node, true);
-        final CheckedFuture<Void, TransactionCommitFailedException> future = writeTransaction.submit();
+        final ListenableFuture<Void> future = writeTransaction.submit();
         Futures.addCallback(future, new FutureCallback<Void>() {
 
             @Override
-            public void onSuccess(Void result) {
+            public void onSuccess(final Void result) {
                 LOG.info("add-netconf-node RPC: Added netconf node successfully.");
                 futureResult.set(RpcResultBuilder.<Void>success().build());
             }
 
             @Override
-            public void onFailure(Throwable exception) {
+            public void onFailure(final Throwable exception) {
                 LOG.error("add-netconf-node RPC: Unable to add netconf node.", exception);
                 futureResult.setException(exception);
             }
         }, MoreExecutors.directExecutor());
     }
 
+
+    @Override
+    public Future<RpcResult<Void>> deleteDevice(final DeleteDeviceInput input) {
+        final NodeId nodeId = new NodeId(input.getNodeId());
+
+        final InstanceIdentifier<NetworkTopology> networkTopologyId =
+                InstanceIdentifier.builder(NetworkTopology.class).build();
+        final InstanceIdentifier<Node> niid = networkTopologyId.child(Topology.class,
+                new TopologyKey(new TopologyId(topologyId))).child(Node.class,
+                new NodeKey(nodeId));
+
+        final WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
+        wtx.delete(LogicalDatastoreType.CONFIGURATION, niid);
+
+        final ListenableFuture<Void> future = wtx.submit();
+        final SettableFuture<RpcResult<Void>> rpcFuture = SettableFuture.create();
+
+        Futures.addCallback(future, new FutureCallback<Void>() {
+
+            @Override
+            public void onSuccess(final Void result) {
+                LOG.info("delete-device RPC: Removed netconf node successfully.");
+                rpcFuture.set(RpcResultBuilder.<Void>success().build());
+            }
+
+            @Override
+            public void onFailure(final Throwable exception) {
+                LOG.error("delete-device RPC: Unable to remove netconf node.", exception);
+                rpcFuture.setException(exception);
+            }
+        }, MoreExecutors.directExecutor());
+
+        return rpcFuture;
+    }
 }
diff --git a/netconf/sal-netconf-connector/src/main/yang/netconf-keystore.yang b/netconf/sal-netconf-connector/src/main/yang/netconf-keystore.yang
new file mode 100644 (file)
index 0000000..d3da9f4
--- /dev/null
@@ -0,0 +1,60 @@
+module netconf-keystore {
+    namespace "urn:opendaylight:netconf:keystore";
+    prefix "keystore";
+
+    revision "2017-10-17" {
+        description "Initial revision of the Netconf SBP keystore.";
+    }
+
+    description "Store used for key based Credentials for Netconf SBP. Before a connector with key based authentication
+                 is created it needs to have a record for the key pair it uses. All the records here need to be
+                 encrypted as they contain sensitive data. Therefore NEVER do direct writes and only use the provided
+                 RPC's for adding/removing key entries.";
+
+    grouping keystore-entry {
+        list key-credential {
+            key key-id;
+
+            leaf key-id {
+                type string;
+            }
+
+            leaf private-key {
+                description "Base64 encoded private key that should be used for authentication with a netconf device.
+                             Do not include a public key as that is calculated from the private key.
+                             DO NOT write this directly into the datastore, use the provided rpc's as these will
+                             encrypt the key before the entry is written into the datastore.";
+                type string;
+            }
+
+            leaf passphrase {
+                description "If the provided key is encrypted by a passphrase this needs to be included. Leave empty
+                             if the key does not have a passphrase.
+                             DO NOT write write this directly into the datastore, use the provided rpc's as these will
+                             encrypt the passhprase before the entry is written into the datastore.";
+                type string;
+            }
+        }
+    }
+
+    container keystore {
+        uses keystore-entry;
+    }
+
+    rpc add-keystore-entry {
+        description "Use this rpc to add a single or multiple new keys into the keystore. The private key
+                     and passphrase will both be encrypted before they are written into the datastore.";
+        input {
+            uses keystore-entry;
+        }
+    }
+
+    rpc remove-keystore-entry {
+        description "Use this rpc to remove a single or multiple keys from the datastore.";
+        input {
+            leaf-list key-id {
+                type string;
+            }
+        }
+    }
+}
\ No newline at end of file
index 7839d508d237e5d649ee7c37b340996fe6582d96..91bda040f916e1b17e5250101d25c350323148ce 100644 (file)
@@ -15,17 +15,51 @@ module netconf-node-topology {
         }
     }
 
-    grouping netconf-node-credentials {
+    grouping username-password {
+        leaf username {
+            type string;
+        }
+
+        leaf password {
+            type string;
+        }
+    }
 
+    grouping netconf-node-credentials {
         choice credentials {
             config true;
-            case login-password {
-                leaf username {
-                    type string;
+            case login-password-deprecated {
+                description "Deprecated way of storing credentials, unencrypted.";
+
+                status deprecated;
+                uses username-password;
+            }
+            case login-pw {
+                description "login-password credentials, encrypted.";
+
+
+                container login-password {
+                    uses username-password;
                 }
+            }
+            case login-pw-unencrypted {
+                description "login-password credentials, not encrypted.";
 
-                leaf password {
-                    type string;
+                container login-password-unencrypted {
+                    uses username-password;
+                }
+            }
+            case key-auth {
+                description "key-based authentication, use the id for the pair thats stored in the keystore.";
+
+                container key-based {
+                    leaf key-id {
+                        type string;
+                    }
+
+                    leaf username {
+                        type string;
+                    }
                 }
             }
         }
@@ -272,15 +306,19 @@ module netconf-node-topology {
 
     }
 
-    rpc add-netconf-node {
+    rpc create-device {
         input {
             uses netconf-node-fields;
             leaf node-id {
                 type string;
             }
-            leaf encrypt {
-                type boolean;
-                default false;
+        }
+    }
+
+    rpc delete-device {
+        input {
+            leaf node-id {
+                type string;
             }
         }
     }
index da24dd0e5107c3cc693b3124ba37ff45da5ca5ba..f9724cf74ee12e2e48e85880e273401e75ef2505 100644 (file)
@@ -58,7 +58,7 @@ import org.opendaylight.netconf.client.NetconfClientSession;
 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
-import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
+import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
 import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
@@ -397,7 +397,7 @@ public class NetconfDeviceCommunicatorTest {
                     .withAddress(new InetSocketAddress("localhost", 65000))
                     .withReconnectStrategy(reconnectStrategy)
                     .withConnectStrategyFactory(() -> reconnectStrategy)
-                    .withAuthHandler(new LoginPassword("admin", "admin"))
+                    .withAuthHandler(new LoginPasswordHandler("admin", "admin"))
                     .withConnectionTimeoutMillis(10000)
                     .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH)
                     .withSessionListener(listener)
index 5ece53f83150f330cbaad3da26f3a628027cf36c..72ef9007d1cfc110a98d96df30125d39d2dc7cfd 100644 (file)
@@ -8,10 +8,9 @@
 package org.opendaylight.netconf.sal.connect.netconf.util;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.when;
 
-import java.lang.reflect.Method;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -23,11 +22,16 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.AddNetconfNodeInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.AddNetconfNodeInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.CreateDeviceInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.CreateDeviceInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPassword;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencrypted;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencryptedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPasswordBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencryptedBuilder;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
 
 public class NetconfTopologyRPCProviderTest {
@@ -42,7 +46,7 @@ public class NetconfTopologyRPCProviderTest {
     @Mock
     private AAAEncryptionService encryptionService;
 
-    NetconfTopologyRPCProvider rpcProvider ;
+    private NetconfTopologyRPCProvider rpcProvider ;
 
     @Before
     public void setUp() {
@@ -54,34 +58,42 @@ public class NetconfTopologyRPCProviderTest {
     @Test
     public void testEncryptPassword() throws Exception {
 
-        NetconfNode node = invokeEncryption(true);
-        assertNotEquals(TEST_PWD, ((LoginPassword)node.getCredentials()).getPassword());
+        final NetconfNode encryptedPwNode = rpcProvider.encryptPassword(getInput(true));
 
-        node = invokeEncryption(false);
-        assertEquals(TEST_PWD, ((LoginPassword)node.getCredentials()).getPassword());
-    }
-
-    private NetconfNode invokeEncryption(boolean encrypt) throws Exception {
-        Method method = null;
+        final Credentials credentials = encryptedPwNode.getCredentials();
+        assertTrue(credentials instanceof LoginPw);
+        final LoginPw loginPw = (LoginPw) credentials;
 
-        method = NetconfTopologyRPCProvider.class.getDeclaredMethod("encryptPassword", AddNetconfNodeInput.class);
+        assertEquals(ENC_PWD, loginPw.getLoginPassword().getPassword());
+    }
 
-        method.setAccessible(true);
-        NetconfNode node = null;
+    @Test
+    public void testNoEncryption() throws Exception {
+        final NetconfNode encryptedPwNode = rpcProvider.encryptPassword(getInput(false));
 
-        node = (NetconfNode)method.invoke(rpcProvider, getInput(encrypt));
+        final Credentials credentials = encryptedPwNode.getCredentials();
+        assertTrue(credentials instanceof LoginPwUnencrypted);
+        final LoginPwUnencrypted loginPw = (LoginPwUnencrypted) credentials;
 
-        return node;
+        assertEquals(TEST_PWD, loginPw.getLoginPasswordUnencrypted().getPassword());
     }
 
-    private AddNetconfNodeInput getInput(boolean encrypt) {
-        AddNetconfNodeInputBuilder builder = new AddNetconfNodeInputBuilder();
-        builder.setCredentials(new LoginPasswordBuilder().setPassword(TEST_PWD).setUsername("test").build());
+    private CreateDeviceInput getInput(boolean encrypt) {
+        CreateDeviceInputBuilder builder = new CreateDeviceInputBuilder();
+        final Credentials credentials;
+        if (encrypt) {
+            credentials = new LoginPwBuilder().setLoginPassword(
+                    new LoginPasswordBuilder().setUsername("test").setPassword(TEST_PWD).build()).build();
+        } else {
+            credentials = new LoginPwUnencryptedBuilder().setLoginPasswordUnencrypted(
+                    new LoginPasswordUnencryptedBuilder().setUsername("test").setPassword(TEST_PWD).build()).build();
+        }
+
+        builder.setCredentials(credentials);
         builder.setHost(new Host(new IpAddress(new Ipv4Address("10.18.16.188"))));
         builder.setPort(new PortNumber(830));
         builder.setTcpOnly(false);
         builder.setNodeId(NODE_ID.toString());
-        builder.setEncrypt(encrypt);
         return builder.build();
     }
 
index 97b045abe7511dc98d9a02d4a4918c50418fd5b0..b852544a5ac09b38c074403ee426264c46384e9e 100644 (file)
@@ -25,7 +25,7 @@ import org.opendaylight.netconf.cli.io.ConsoleIO;
 import org.opendaylight.netconf.cli.io.ConsoleIOImpl;
 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
 import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder;
-import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
+import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 /**
@@ -214,8 +214,8 @@ public class Main {
             return parsed.getString(key);
         }
 
-        public LoginPassword getCredentials() {
-            return new LoginPassword(getUsername(), getPassword());
+        public LoginPasswordHandler getCredentials() {
+            return new LoginPasswordHandler(getUsername(), getPassword());
         }
 
         public String getPassword() {
index 3bffa568a4af238be424f87a7ea76d5b164524d5..ac8b7f451ce3452e462cebee6cc55eb6a72f35bd 100644 (file)
@@ -25,7 +25,7 @@ import org.opendaylight.netconf.cli.commands.output.Output;
 import org.opendaylight.netconf.cli.commands.output.OutputDefinition;
 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
 import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder;
-import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
+import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
 import org.opendaylight.protocol.framework.NeverReconnectStrategy;
 import org.opendaylight.protocol.framework.ReconnectStrategy;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -102,7 +102,7 @@ public class Connect extends AbstractCommand {
         return NetconfClientConfigurationBuilder.create().withAddress(inetAddress)
                 .withConnectionTimeoutMillis(connectionTimeout)
                 .withReconnectStrategy(strategy)
-                .withAuthHandler(new LoginPassword(username, passwd))
+                .withAuthHandler(new LoginPasswordHandler(username, passwd))
                 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH);
     }
 
index ab45f2d73743d5ce78823da1adeb07a3161b7630..bbf8af73dba4d8e134799644a732e4223345c33e 100644 (file)
@@ -273,6 +273,10 @@ public class NetconfDeviceSimulator implements Closeable {
                 .setBindingAddress(bindingAddress)
                 .setLocalAddress(tcpLocalAddress)
                 .setAuthenticator((username, password) -> true)
+                .setPublickeyAuthenticator(((username, key, session) -> {
+                    LOG.info("Auth with public key: {}", key);
+                    return true;
+                }))
                 .setKeyPairProvider(keyPairProvider)
                 .setIdleTimeout(Integer.MAX_VALUE)
                 .createSshProxyServerConfiguration();
index edaa1880327db33d23f97f26cad414cbf87373b9..db15bb2a02a301bcc6bdfabd70eee5cf990a0d94 100644 (file)
@@ -19,7 +19,7 @@ import org.opendaylight.netconf.client.NetconfClientDispatcherImpl;
 import org.opendaylight.netconf.client.NetconfClientSession;
 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
 import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder;
-import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
+import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
 import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
@@ -101,7 +101,7 @@ public class StressClientCallable implements Callable<Boolean> {
         }
         netconfClientConfigurationBuilder.withProtocol(params.ssh ? NetconfClientConfiguration.NetconfClientProtocol.SSH
             : NetconfClientConfiguration.NetconfClientProtocol.TCP);
-        netconfClientConfigurationBuilder.withAuthHandler(new LoginPassword(params.username, params.password));
+        netconfClientConfigurationBuilder.withAuthHandler(new LoginPasswordHandler(params.username, params.password));
         netconfClientConfigurationBuilder.withConnectionTimeoutMillis(20000L);
         netconfClientConfigurationBuilder.withReconnectStrategy(
             new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 5000));
index 5a84ea6acb04ab873252b3264873d1c32bdc97e0..f817575d26286e2c4ee2c7b557438334c17d6c42 100644 (file)
@@ -20,7 +20,7 @@ public class RpcHandlerDefault implements RpcHandler {
     @Override
     public Optional<Document> getResponse(XmlElement rpcElement) {
         LOG.info("getResponse: {}", rpcElement.toString());
-        return null;
+        return Optional.empty();
     }
 
 }