Refactor NetconfSalKeystoreRpcs 90/110090/5
authorRobert Varga <robert.varga@pantheon.tech>
Tue, 30 Jan 2024 04:36:15 +0000 (05:36 +0100)
committerRobert Varga <nite@hq.sk>
Wed, 31 Jan 2024 08:50:55 +0000 (08:50 +0000)
Move RPC implementations into keystore-legacy and wrap them in a
ClusterSingletonService.

JIRA: NETCONF-1237
Change-Id: I1fdc81500c90cb46235838c9e77e741d44160807
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
16 files changed:
keystore/keystore-legacy/pom.xml
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/AbstractNetconfKeystore.java
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/AbstractRpc.java [new file with mode: 0644]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultAddKeystoreEntry.java [new file with mode: 0644]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultAddPrivateKey.java [new file with mode: 0644]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultAddTrustedCertificate.java [new file with mode: 0644]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultRemoveKeystoreEntry.java [new file with mode: 0644]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultRemovePrivateKey.java [new file with mode: 0644]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultRemoveTrustedCertificate.java [new file with mode: 0644]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/RpcSingleton.java [new file with mode: 0644]
keystore/keystore-legacy/src/test/java/org/opendaylight/netconf/keystore/legacy/NetconfKeystoreRpcsTest.java [new file with mode: 0644]
keystore/keystore-legacy/src/test/resources/netconf-keystore.xml [new file with mode: 0644]
plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/impl/DefaultSslHandlerFactoryProvider.java
plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/impl/NetconfSalKeystoreRpcs.java [deleted file]
plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/impl/DefaultSslHandlerFactoryProviderTest.java
plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/impl/NetconfSalKeystoreRpcsTest.java [deleted file]

index 637f4008fcbf3039da514b627d9203d079b74318..94f2b5a5dfebb262da623123cce5fb0a6832c9d1 100644 (file)
     <description>Legacy NETCONF keystore</description>
 
     <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.aaa</groupId>
+            <artifactId>aaa-encrypt-service</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
             <artifactId>mdsal-binding-api</artifactId>
             <groupId>org.opendaylight.mdsal</groupId>
             <artifactId>mdsal-common-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-singleton-api</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>concepts</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.netconf</groupId>
+            <artifactId>netconf-api</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
index 3f98baea0676f21a3cfe07fee19ea4109d6d6c0d..d06a62a34e4293a5b72c7c891bb49c80a3713d60 100644 (file)
@@ -23,9 +23,12 @@ import java.util.function.Consumer;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.aaa.encrypt.AAAEncryptionService;
 import org.opendaylight.mdsal.binding.api.DataBroker;
 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
+import org.opendaylight.mdsal.binding.api.RpcProviderService;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.singleton.api.ClusterSingletonServiceProvider;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.Keystore;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017._private.keys.PrivateKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.trusted.certificates.TrustedCertificate;
@@ -93,16 +96,30 @@ public abstract class AbstractNetconfKeystore {
     private final SecurityHelper securityHelper = new SecurityHelper();
 
     private @Nullable Registration configListener;
+    private @Nullable Registration rpcSingleton;
 
-    protected final void start(final DataBroker dataBroker) {
+    protected final void start(final DataBroker dataBroker, final RpcProviderService rpcProvider,
+            final ClusterSingletonServiceProvider cssProvider, final AAAEncryptionService encryptionService) {
         if (configListener == null) {
             configListener = dataBroker.registerTreeChangeListener(
                 DataTreeIdentifier.of(LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(Keystore.class)),
                 new ConfigListener(this));
+            LOG.debug("NETCONF keystore configuration listener started");
         }
+        if (rpcSingleton == null) {
+            rpcSingleton = cssProvider.registerClusterSingletonService(
+                new RpcSingleton(dataBroker, rpcProvider, encryptionService));
+            LOG.debug("NETCONF keystore configuration singleton registered");
+        }
+        LOG.info("NETCONF keystore service started");
     }
 
     protected final void stop() {
+        final var singleton = rpcSingleton;
+        if (singleton != null) {
+            rpcSingleton = null;
+            singleton.close();
+        }
         final var listener = configListener;
         if (listener != null) {
             configListener = null;
diff --git a/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/AbstractRpc.java b/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/AbstractRpc.java
new file mode 100644 (file)
index 0000000..b2db630
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. 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.keystore.legacy;
+
+import static java.util.Objects.requireNonNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.api.WriteTransaction;
+
+abstract class AbstractRpc {
+    private final DataBroker dataBroker;
+
+    AbstractRpc(final DataBroker dataBroker) {
+        this.dataBroker = requireNonNull(dataBroker);
+    }
+
+    final @NonNull WriteTransaction newTransaction() {
+        return dataBroker.newWriteOnlyTransaction();
+    }
+}
diff --git a/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultAddKeystoreEntry.java b/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultAddKeystoreEntry.java
new file mode 100644 (file)
index 0000000..828ebaf
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. 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.keystore.legacy;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.util.ArrayList;
+import java.util.Base64;
+import org.opendaylight.aaa.encrypt.AAAEncryptionService;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddKeystoreEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddKeystoreEntryInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddKeystoreEntryOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddKeystoreEntryOutputBuilder;
+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.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.keystore.entry.KeyCredentialBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.ErrorType;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class DefaultAddKeystoreEntry extends AbstractRpc implements AddKeystoreEntry {
+    private static final Logger LOG = LoggerFactory.getLogger(DefaultAddKeystoreEntry.class);
+
+    private final AAAEncryptionService encryptionService;
+
+    DefaultAddKeystoreEntry(final DataBroker dataBroker, final AAAEncryptionService encryptionService) {
+        super(dataBroker);
+        this.encryptionService = requireNonNull(encryptionService);
+    }
+
+    @Override
+    public ListenableFuture<RpcResult<AddKeystoreEntryOutput>> invoke(final AddKeystoreEntryInput input) {
+        final var plain = input.getKeyCredential();
+        if (plain == null || plain.isEmpty()) {
+            return RpcResultBuilder.success(new AddKeystoreEntryOutputBuilder().build()).buildFuture();
+        }
+
+        LOG.debug("Adding keypairs: {}", plain);
+        final var encrypted = new ArrayList<KeyCredential>(plain.size());
+        for (var credential : plain.values()) {
+            final var keyId = credential.getKeyId();
+            try {
+                encrypted.add(new KeyCredentialBuilder(credential)
+                    .setPrivateKey(encryptString(credential.getPrivateKey()))
+                    .setPassphrase(encryptString(credential.getPassphrase()))
+                    .build());
+            } catch (GeneralSecurityException e) {
+                LOG.debug("Cannot decrypt key credential {}}", credential, e);
+                return RpcResultBuilder.<AddKeystoreEntryOutput>failed()
+                    .withError(ErrorType.APPLICATION, "Failed to decrypt key " + keyId, e)
+                    .buildFuture();
+            }
+        }
+
+        final var tx = newTransaction();
+        for (var keypair : encrypted) {
+            tx.put(LogicalDatastoreType.CONFIGURATION,
+                InstanceIdentifier.create(Keystore.class).child(KeyCredential.class, keypair.key()), keypair);
+        }
+
+        return tx.commit().transform(commitInfo -> {
+            LOG.debug("Updated keypairs: {}", plain.keySet());
+            return RpcResultBuilder.success(new AddKeystoreEntryOutputBuilder().build()).build();
+        }, MoreExecutors.directExecutor());
+    }
+
+    private String encryptString(final String plain) throws GeneralSecurityException {
+        return Base64.getEncoder().encodeToString(encryptionService.encrypt(plain.getBytes(StandardCharsets.UTF_8)));
+    }
+}
\ No newline at end of file
diff --git a/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultAddPrivateKey.java b/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultAddPrivateKey.java
new file mode 100644 (file)
index 0000000..a8e4251
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. 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.keystore.legacy;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKeyInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKeyOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKeyOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.Keystore;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017._private.keys.PrivateKey;
+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;
+
+final class DefaultAddPrivateKey extends AbstractRpc implements AddPrivateKey {
+    private static final Logger LOG = LoggerFactory.getLogger(DefaultAddPrivateKey.class);
+
+    DefaultAddPrivateKey(final DataBroker dataBroker) {
+        super(dataBroker);
+    }
+
+    @Override
+    public ListenableFuture<RpcResult<AddPrivateKeyOutput>> invoke(final AddPrivateKeyInput input) {
+        final var keys = input.getPrivateKey();
+        if (keys == null || keys.isEmpty()) {
+            return RpcResultBuilder.success(new AddPrivateKeyOutputBuilder().build()).buildFuture();
+        }
+
+        LOG.debug("Adding private keys: {}", keys);
+        final var tx = newTransaction();
+        for (var key : keys.values()) {
+            tx.put(LogicalDatastoreType.CONFIGURATION,
+                InstanceIdentifier.create(Keystore.class).child(PrivateKey.class, key.key()), key);
+        }
+
+        return tx.commit().transform(commitInfo -> {
+            LOG.debug("Added private keys: {}", keys.keySet());
+            return RpcResultBuilder.success(new AddPrivateKeyOutputBuilder().build()).build();
+        }, MoreExecutors.directExecutor());
+    }
+}
\ No newline at end of file
diff --git a/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultAddTrustedCertificate.java b/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultAddTrustedCertificate.java
new file mode 100644 (file)
index 0000000..795aeda
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. 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.keystore.legacy;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificateInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificateOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificateOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.Keystore;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.trusted.certificates.TrustedCertificate;
+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;
+
+final class DefaultAddTrustedCertificate extends AbstractRpc implements AddTrustedCertificate {
+    private static final Logger LOG = LoggerFactory.getLogger(DefaultAddTrustedCertificate.class);
+
+    DefaultAddTrustedCertificate(final DataBroker dataBroker) {
+        super(dataBroker);
+    }
+
+    @Override
+    public ListenableFuture<RpcResult<AddTrustedCertificateOutput>> invoke(final AddTrustedCertificateInput input) {
+        final var certs = input.getTrustedCertificate();
+        if (certs == null || certs.isEmpty()) {
+            return RpcResultBuilder.success(new AddTrustedCertificateOutputBuilder().build()).buildFuture();
+        }
+
+        LOG.debug("Updating trusted certificates: {}", certs);
+        final var tx = newTransaction();
+        for (var certificate : certs.values()) {
+            tx.put(LogicalDatastoreType.CONFIGURATION,
+                InstanceIdentifier.create(Keystore.class).child(TrustedCertificate.class, certificate.key()),
+                certificate);
+        }
+
+        return tx.commit().transform(commitInfo -> {
+            LOG.debug("Updated trusted certificates: {}", certs.keySet());
+            return RpcResultBuilder.success(new AddTrustedCertificateOutputBuilder().build()).build();
+        }, MoreExecutors.directExecutor());
+    }
+}
\ No newline at end of file
diff --git a/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultRemoveKeystoreEntry.java b/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultRemoveKeystoreEntry.java
new file mode 100644 (file)
index 0000000..d9c53b6
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. 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.keystore.legacy;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.stream.Collectors;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.Keystore;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveKeystoreEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveKeystoreEntryInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveKeystoreEntryOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveKeystoreEntryOutputBuilder;
+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.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;
+
+final class DefaultRemoveKeystoreEntry extends AbstractRpc implements RemoveKeystoreEntry {
+    private static final Logger LOG = LoggerFactory.getLogger(DefaultRemoveKeystoreEntry.class);
+
+    DefaultRemoveKeystoreEntry(final DataBroker dataBroker) {
+        super(dataBroker);
+    }
+
+    @Override
+    public ListenableFuture<RpcResult<RemoveKeystoreEntryOutput>> invoke(final RemoveKeystoreEntryInput input) {
+        final var keyIds = input.getKeyId();
+        if (keyIds == null || keyIds.isEmpty()) {
+            return RpcResultBuilder.success(new RemoveKeystoreEntryOutputBuilder().build()).buildFuture();
+        }
+
+        final var keys = keyIds.stream().map(KeyCredentialKey::new).collect(Collectors.toSet());
+        LOG.debug("Removing keypairs: {}", keys);
+        final var tx = newTransaction();
+        for (var key : keys) {
+            tx.delete(LogicalDatastoreType.CONFIGURATION,
+                InstanceIdentifier.create(Keystore.class).child(KeyCredential.class, key));
+        }
+
+        return tx.commit().transform(commitInfo -> {
+            LOG.debug("Removed keypairs: {}", keys);
+            return RpcResultBuilder.success(new RemoveKeystoreEntryOutputBuilder().build()).build();
+        }, MoreExecutors.directExecutor());
+    }
+}
\ No newline at end of file
diff --git a/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultRemovePrivateKey.java b/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultRemovePrivateKey.java
new file mode 100644 (file)
index 0000000..04a3dbd
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. 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.keystore.legacy;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.stream.Collectors;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.Keystore;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemovePrivateKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemovePrivateKeyInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemovePrivateKeyOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemovePrivateKeyOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017._private.keys.PrivateKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017._private.keys.PrivateKeyKey;
+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;
+
+final class DefaultRemovePrivateKey extends AbstractRpc implements RemovePrivateKey {
+    private static final Logger LOG = LoggerFactory.getLogger(DefaultRemovePrivateKey.class);
+
+    DefaultRemovePrivateKey(final DataBroker dataBroker) {
+        super(dataBroker);
+    }
+
+    @Override
+    public ListenableFuture<RpcResult<RemovePrivateKeyOutput>> invoke(final RemovePrivateKeyInput input) {
+        final var names = input.getName();
+        if (names == null || names.isEmpty()) {
+            return RpcResultBuilder.success(new RemovePrivateKeyOutputBuilder().build()).buildFuture();
+        }
+
+        final var keys = names.stream().map(PrivateKeyKey::new).collect(Collectors.toSet());
+        LOG.debug("Removing private keys: {}", keys);
+
+
+        final var tx = newTransaction();
+        for (var key : keys) {
+            tx.delete(LogicalDatastoreType.CONFIGURATION,
+                InstanceIdentifier.create(Keystore.class).child(PrivateKey.class, key));
+        }
+
+        return tx.commit().transform(commitInfo -> {
+            LOG.debug("Removed trusted keys: {}", keys);
+            return RpcResultBuilder.success(new RemovePrivateKeyOutputBuilder().build()).build();
+        }, MoreExecutors.directExecutor());
+    }
+}
\ No newline at end of file
diff --git a/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultRemoveTrustedCertificate.java b/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultRemoveTrustedCertificate.java
new file mode 100644 (file)
index 0000000..a95d9de
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. 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.keystore.legacy;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.stream.Collectors;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.Keystore;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveTrustedCertificate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveTrustedCertificateInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveTrustedCertificateOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveTrustedCertificateOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.trusted.certificates.TrustedCertificate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.trusted.certificates.TrustedCertificateKey;
+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;
+
+final class DefaultRemoveTrustedCertificate extends AbstractRpc implements RemoveTrustedCertificate {
+    private static final Logger LOG = LoggerFactory.getLogger(DefaultRemoveTrustedCertificate.class);
+
+    DefaultRemoveTrustedCertificate(final DataBroker dataBroker) {
+        super(dataBroker);
+    }
+
+    @Override
+    public ListenableFuture<RpcResult<RemoveTrustedCertificateOutput>> invoke(
+            final RemoveTrustedCertificateInput input) {
+        final var names = input.getName();
+        if (names == null || names.isEmpty()) {
+            return RpcResultBuilder.success(new RemoveTrustedCertificateOutputBuilder().build()).buildFuture();
+        }
+
+        final var keys = names.stream().map(TrustedCertificateKey::new).collect(Collectors.toSet());
+        LOG.debug("Removing trusted certificates: {}", keys);
+        final var tx = newTransaction();
+        for (var key : keys) {
+            tx.delete(LogicalDatastoreType.CONFIGURATION,
+                InstanceIdentifier.create(Keystore.class).child(TrustedCertificate.class, key));
+        }
+
+        return tx.commit().transform(commitInfo -> {
+            LOG.debug("Removed trusted certificates: {}", keys);
+            return RpcResultBuilder.success(new RemoveTrustedCertificateOutputBuilder().build()).build();
+        }, MoreExecutors.directExecutor());
+    }
+}
\ No newline at end of file
diff --git a/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/RpcSingleton.java b/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/RpcSingleton.java
new file mode 100644 (file)
index 0000000..e49c714
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. 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.keystore.legacy;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableClassToInstanceMap;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.aaa.encrypt.AAAEncryptionService;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.api.RpcProviderService;
+import org.opendaylight.mdsal.singleton.api.ClusterSingletonService;
+import org.opendaylight.mdsal.singleton.api.ServiceGroupIdentifier;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddKeystoreEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveKeystoreEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemovePrivateKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveTrustedCertificate;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.binding.Rpc;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class RpcSingleton implements ClusterSingletonService {
+    private static final Logger LOG = LoggerFactory.getLogger(RpcSingleton.class);
+    private static final @NonNull ServiceGroupIdentifier SGI = new ServiceGroupIdentifier("netconf-keystore-rpc");
+
+    private final AAAEncryptionService encryptionService;
+    private final RpcProviderService rpcProvider;
+    private final DataBroker dataBroker;
+
+    private Registration reg;
+
+    RpcSingleton(final DataBroker dataBroker, final RpcProviderService rpcProvider,
+            final AAAEncryptionService encryptionService) {
+        this.dataBroker = requireNonNull(dataBroker);
+        this.rpcProvider = requireNonNull(rpcProvider);
+        this.encryptionService = requireNonNull(encryptionService);
+    }
+
+    @Override
+    public ServiceGroupIdentifier getIdentifier() {
+        return SGI;
+    }
+
+    @Override
+    public void instantiateServiceInstance() {
+        if (reg != null) {
+            LOG.warn("Instiatiating while already running, very weird", new Throwable());
+            return;
+        }
+
+        LOG.debug("Registering RPC implementations");
+        reg = rpcProvider.registerRpcImplementations(ImmutableClassToInstanceMap.<Rpc<?, ?>>builder()
+            .put(AddKeystoreEntry.class, new DefaultAddKeystoreEntry(dataBroker, encryptionService))
+            .put(RemoveKeystoreEntry.class, new DefaultRemoveKeystoreEntry(dataBroker))
+            .put(AddPrivateKey.class, new DefaultAddPrivateKey(dataBroker))
+            .put(RemovePrivateKey.class, new DefaultRemovePrivateKey(dataBroker))
+            .put(AddTrustedCertificate.class, new DefaultAddTrustedCertificate(dataBroker))
+            .put(RemoveTrustedCertificate.class, new DefaultRemoveTrustedCertificate(dataBroker))
+            .build());
+        LOG.info("This node is now owning NETCONF keystore configuration");
+    }
+
+    @Override
+    public ListenableFuture<?> closeServiceInstance() {
+        if (reg != null) {
+            LOG.debug("Unregistering RPC implementations");
+            reg.close();
+            reg = null;
+            LOG.info("This node is no longer owning NETCONF keystore configuration");
+        }
+        return Futures.immediateVoidFuture();
+    }
+}
diff --git a/keystore/keystore-legacy/src/test/java/org/opendaylight/netconf/keystore/legacy/NetconfKeystoreRpcsTest.java b/keystore/keystore-legacy/src/test/java/org/opendaylight/netconf/keystore/legacy/NetconfKeystoreRpcsTest.java
new file mode 100644 (file)
index 0000000..5396420
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2018 ZTE Corporation. 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.keystore.legacy;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.opendaylight.mdsal.common.api.CommitInfo.emptyFluentFuture;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.opendaylight.aaa.encrypt.AAAEncryptionService;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.api.RpcProviderService;
+import org.opendaylight.mdsal.binding.api.WriteTransaction;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.netconf.api.xml.XmlUtil;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKeyInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKeyInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificateInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificateInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017._private.keys.PrivateKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017._private.keys.PrivateKeyBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017._private.keys.PrivateKeyKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.trusted.certificates.TrustedCertificate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.trusted.certificates.TrustedCertificateBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.trusted.certificates.TrustedCertificateKey;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+@ExtendWith(MockitoExtension.class)
+class NetconfKeystoreRpcsTest {
+    private static final String XML_ELEMENT_PRIVATE_KEY = "private-key";
+    private static final String XML_ELEMENT_NAME = "name";
+    private static final String XML_ELEMENT_DATA = "data";
+    private static final String XML_ELEMENT_CERT_CHAIN = "certificate-chain";
+    private static final String XML_ELEMENT_TRUSTED_CERT = "trusted-certificate";
+    private static final String XML_ELEMENT_CERT = "certificate";
+
+    private static Document KEYSTORE;
+
+    @Mock
+    private WriteTransaction writeTx;
+    @Mock
+    private DataBroker dataBroker;
+    @Mock
+    private AAAEncryptionService encryptionService;
+    @Mock
+    private RpcProviderService rpcProvider;
+    @Mock
+    private ObjectRegistration<?> rpcReg;
+
+    @BeforeAll
+    static void beforeAll() throws Exception {
+        KEYSTORE = XmlUtil.readXmlToDocument(
+            NetconfKeystoreRpcsTest.class.getResourceAsStream("/netconf-keystore.xml"));
+    }
+
+    @BeforeEach
+    void beforeEach() {
+        doReturn(writeTx).when(dataBroker).newWriteOnlyTransaction();
+    }
+
+    @Test
+    void testAddPrivateKey() throws Exception {
+        doReturn(emptyFluentFuture()).when(writeTx).commit();
+
+        final var rpc = new DefaultAddPrivateKey(dataBroker);
+        final var input = getPrivateKeyInput();
+        rpc.invoke(input).get();
+
+        verify(writeTx, times(input.nonnullPrivateKey().size()))
+            .put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(DataObject.class));
+    }
+
+    private static AddPrivateKeyInput getPrivateKeyInput() throws Exception {
+        final var privateKeys = new HashMap<PrivateKeyKey, PrivateKey>();
+        final var nodeList = KEYSTORE.getElementsByTagName(XML_ELEMENT_PRIVATE_KEY);
+        for (int i = 0; i < nodeList.getLength(); i++) {
+            if (nodeList.item(i) instanceof Element element) {
+                final var keyName = element.getElementsByTagName(XML_ELEMENT_NAME).item(0).getTextContent();
+                final var keyData = element.getElementsByTagName(XML_ELEMENT_DATA).item(0).getTextContent();
+                final var certNodes = element.getElementsByTagName(XML_ELEMENT_CERT_CHAIN);
+                final var certChain = new ArrayList<String>();
+                for (int j = 0; j < certNodes.getLength(); j++) {
+                    if (certNodes.item(j) instanceof Element certElement) {
+                        certChain.add(certElement.getTextContent());
+                    }
+                }
+
+                final var key = new PrivateKeyKey(keyName);
+                privateKeys.put(key, new PrivateKeyBuilder()
+                    .withKey(key)
+                    .setData(keyData)
+                    .setCertificateChain(certChain)
+                    .build());
+            }
+        }
+
+        return new AddPrivateKeyInputBuilder().setPrivateKey(privateKeys).build();
+    }
+
+    @Test
+    void testAddTrustedCertificate() throws Exception {
+        doReturn(emptyFluentFuture()).when(writeTx).commit();
+
+        final var rpc = new DefaultAddTrustedCertificate(dataBroker);
+        final var input = getTrustedCertificateInput();
+        rpc.invoke(input).get();
+
+        verify(writeTx, times(input.nonnullTrustedCertificate().size()))
+            .put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(DataObject.class));
+    }
+
+    private static AddTrustedCertificateInput getTrustedCertificateInput() throws Exception {
+        final var trustedCertificates = new HashMap<TrustedCertificateKey, TrustedCertificate>();
+        final var nodeList = KEYSTORE.getElementsByTagName(XML_ELEMENT_TRUSTED_CERT);
+        for (int i = 0; i < nodeList.getLength(); i++) {
+            if (nodeList.item(i) instanceof Element element) {
+                final var certName = element.getElementsByTagName(XML_ELEMENT_NAME).item(0).getTextContent();
+                final var certData = element.getElementsByTagName(XML_ELEMENT_CERT).item(0).getTextContent();
+
+                final var key = new TrustedCertificateKey(certName);
+                trustedCertificates.put(key, new TrustedCertificateBuilder()
+                    .withKey(key)
+                    .setName(certName)
+                    .setCertificate(certData)
+                    .build());
+            }
+        }
+
+        return new AddTrustedCertificateInputBuilder().setTrustedCertificate(trustedCertificates).build();
+    }
+}
diff --git a/keystore/keystore-legacy/src/test/resources/netconf-keystore.xml b/keystore/keystore-legacy/src/test/resources/netconf-keystore.xml
new file mode 100644 (file)
index 0000000..250d9f8
--- /dev/null
@@ -0,0 +1,75 @@
+<keystore xmlns="urn:opendaylight:netconf:keystore">\r
+    <private-key>\r
+        <name>test_private_key</name>\r
+        <data>MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCxrZcfyb8hPfiA\r
+Sa1FNqboboPfgOv6E6KJpqpsgbowbEGnw6fOJnx9otfrVmAP86YUT6i+nuPwNG/b\r
+I5YHqxd+OMllmvt0DX1tbtHMirx0wFcVXfk4hruDEMWzsUTohrpiQFTrq/kb9Dr4\r
+FxO+/Ddt+YMmUhu1WGPAhOun2VErwGF6HuYfBMji3QvSzS0Hn9Fez1/nY88CzQrz\r
+lPUH7FMVYIP/rzovKJT/5Bnj+YiiNMKYuPoyvBmyPylFeCvAhF5uyGDjLGYDkscQ\r
+MzFDu44ebtcnVK9BZUxgWUDVwm5FjDSDjdcWKla7N46ykITvyBelm96oY38KkNaV\r
+6x/e/4KZAgMBAAECggEBAJdNO2PWaOvl2bdlBifqYjeL5MBvCIPsNH0DcBz2W5bg\r
+mQhDlKH4JArYoQXGiAcNdF/Xddrdcz0ZaicyJpBhIaBauyXK1FX/JtAJjp6fhdvl\r
+7kJDw/ZexU7W+YQLcKKSGCWSor4NtBQZ5h1diXMZVBpSX1xCj1Xd7xQCHKrSZEzc\r
+Geqx7ew1jkkHL+K6MunyQrY2fb6snWkyGEJf3VyjRjzzNubmF7dR7p+fo6NpeTD4\r
+ywEAenkTtw55gwtz2q5pTV3I11ZJHcFjfbLY3SONz86x5FwNODVzaTrLN0JAvkXz\r
+1LqeZbGRnKQ2/emlX1Gt9z8AzlissqJS8JgvQNm+AAECgYEA5bIEJfFDDvBWFPbh\r
+gp5QmX9v9n2jRRX6mEdRPMIbfLfh9NAg3dc5l+yxQaAzSJHrOxe2eciwUGNhE5yb\r
+Spub9o66ZSjAB58GzMI3LIrMUWufp5Ua7ztl3SuMhoTPlAMuqe69q4RMC0KUW9aE\r
+LF4fs2fAm62dv15Z0pOsnR0CdSkCgYEAxgaVD055iAutKXSugI63hvP0T6DAHcnQ\r
+rgdPUT4uCQGibnqUPRbcnNkBNxORRmap12K39B0QQ0eZTpdKNYmWOSnvNSBwiJ6r\r
+ZRCF7UCqArub3nrONSYXXfd+szVjlYgB0paiOUtzEtt7s+JYV2+KswIAx0QwjynX\r
+OPLVAF4tX/ECgYEAtPirwgUzU2rSN9RH2vTHBhlc6nUUlVL6zM2r2NYKeBoc4hi1\r
+PHPdQbDP+6evoCavkjBdqdgP6lZSXvRNedveZsUPYLJZkeeeoOcIN4Tn8+J6uLuG\r
+rCQ9XqN4JWgwcCqNsn+SWrdyfpCneTArlRVXnq9JFp8UoXlCBeIp5uO7UvkCgYEA\r
+kE7Vq4zlldXkf/RvAnJ+nhMDtE+SEWMz9s6O58anZ5rQUzBy/L2/UXH2p7tTv/kq\r
+xjJDmdrgMhdoSlSIGNHGLqw3jQCx4W23u3O6FXZtLoanhQ77XNIAb1Lf+xrqEltF\r
+8MAjQhuQpWpbEHDfLgC0E9Ve2dgAhyPXmsGjpZv79xECgYAjX+cUFoexgkzhilSc\r
+xyI2GI46IZ1NIFBCMuTMSTkEpBotO1ooUT4ZrR2mdIsoc3O5X5y63ZI1CWMwfd4u\r
+buB0qy91s1i4AMW3JlB/jOX++y7YVwUX2aHHHl9fljFJBYqSxUatPDSuo0r42SXq\r
+sD2PXwX43X/g2QxQhP2l9nfSmg==</data>\r
+        <certificate-chain>MIIDgTCCAmmgAwIBAgIEb3Rj1DANBgkqhkiG9w0BAQsFADBxMQswCQYDVQQGEwJD\r
+TjEQMA4GA1UECBMHSklBTkdTVTEQMA4GA1UEBxMHTkFOSklORzEVMBMGA1UEChMM\r
+b3BlbmRheWxpZ2h0MRAwDgYDVQQLEwduZXRjb25mMRUwEwYDVQQDEwxvcGVuZGF5\r
+bGlnaHQwHhcNMTcxMTIwMDkyNDM3WhcNMjcxMTE4MDkyNDM3WjBxMQswCQYDVQQG\r
+EwJDTjEQMA4GA1UECBMHSklBTkdTVTEQMA4GA1UEBxMHTkFOSklORzEVMBMGA1UE\r
+ChMMb3BlbmRheWxpZ2h0MRAwDgYDVQQLEwduZXRjb25mMRUwEwYDVQQDEwxvcGVu\r
+ZGF5bGlnaHQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxrZcfyb8h\r
+PfiASa1FNqboboPfgOv6E6KJpqpsgbowbEGnw6fOJnx9otfrVmAP86YUT6i+nuPw\r
+NG/bI5YHqxd+OMllmvt0DX1tbtHMirx0wFcVXfk4hruDEMWzsUTohrpiQFTrq/kb\r
+9Dr4FxO+/Ddt+YMmUhu1WGPAhOun2VErwGF6HuYfBMji3QvSzS0Hn9Fez1/nY88C\r
+zQrzlPUH7FMVYIP/rzovKJT/5Bnj+YiiNMKYuPoyvBmyPylFeCvAhF5uyGDjLGYD\r
+kscQMzFDu44ebtcnVK9BZUxgWUDVwm5FjDSDjdcWKla7N46ykITvyBelm96oY38K\r
+kNaV6x/e/4KZAgMBAAGjITAfMB0GA1UdDgQWBBTjjrH57CtARfDWvpmkmv7lGL/2\r
+rDANBgkqhkiG9w0BAQsFAAOCAQEApzyNLC/jh5UhWh9VvQW19Fbn9zTcSiCRtgwf\r
+eUXpQPukLfL0eDOK0kBLIvN8lQtu7nH/3aYTaZwHMOpahDfSe2Q0eZPxr9hX7Hlo\r
+kVOtQ88iW3c/KUF7TVjC16eBeNqvMsBCZ45j0al3QxP40iFPvO576HxRLuYKaB3k\r
++CNI6bn1m5SKoToQgbQbqrALFE3zud7iaUSiLAPSCLBqvEg50pLbbxc6nHXmHjqp\r
+alTeRo89Ph0k0jnHzsW+GlefH4MGS3E2MZG0i1jh/+JoTC6DV2g/vNI4Yry7YFa0\r
+WCCnuFcJwlvd12rkxiNI78buwMzEGcOU16wXeDRJxOM+UbF9xA==</certificate-chain>\r
+    </private-key>\r
+    <trusted-certificate>\r
+        <name>test_trusted_cert</name>\r
+        <certificate>MIIECTCCAvGgAwIBAgIBCDANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMCQ1ox\r
+FjAUBgNVBAgMDVNvdXRoIE1vcmF2aWExDTALBgNVBAcMBEJybm8xDzANBgNVBAoM\r
+BkNFU05FVDEMMAoGA1UECwwDVE1DMRMwEQYDVQQDDApleGFtcGxlIENBMSIwIAYJ\r
+KoZIhvcNAQkBFhNleGFtcGxlY2FAbG9jYWxob3N0MB4XDTE1MDczMDA3MjU1MFoX\r
+DTM1MDcyNTA3MjU1MFowgYUxCzAJBgNVBAYTAkNaMRYwFAYDVQQIDA1Tb3V0aCBN\r
+b3JhdmlhMQ8wDQYDVQQKDAZDRVNORVQxDDAKBgNVBAsMA1RNQzEXMBUGA1UEAwwO\r
+ZXhhbXBsZSBzZXJ2ZXIxJjAkBgkqhkiG9w0BCQEWF2V4YW1wbGVzZXJ2ZXJAbG9j\r
+YWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsdI1TBjzX1Pg\r
+QXFuPCw5/kQwU7qkrhirMcFAXhI8EoXepPa9fKAVuMjHW32P6nNzDpnhFe0YGdNl\r
+oIEN3hJJ87cVOqj4o7zZMbq3zVG2L8As7MTA8tYXm2fSC/0rIxxRRemcGUXM0q+4\r
+LEACjZj2pOKonaivF5VbhgNjPCO1Jj/TamUc0aViE577C9L9EiObGM+bGbabWk/K\r
+WKLsvxUc+sKZXaJ7psTVgpggJAkUszlmwOQgFiMSR53E9/CAkQYhzGVCmH44Vs6H\r
+zs3RZjOTbce4wr4ongiA5LbPeSNSCFjy9loKpaE1rtOjkNBVdiNPCQTmLuODXUTK\r
+gkeL+9v/OwIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVu\r
+U1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU83qEtQDFzDvLoaII\r
+vqiU6k7j1uswHwYDVR0jBBgwFoAUc1YQIqjZsHVwlea0AB4N+ilNI2gwDQYJKoZI\r
+hvcNAQELBQADggEBAJ+QOLi4gPWGofMkLTqSsbv5xRvTw0xa/sJnEeiejtygAu3o\r
+McAsyevSH9EYVPCANxzISPzd9SFaO56HxWgcxLn9vi8ZNvo2wIp9zucNu285ced1\r
+K/2nDZfBmvBxXnj/n7spwqOyuoIc8sR7P7YyI806Qsfhk3ybNZE5UHJFZKDRQKvR\r
+J1t4nk9saeo87kIuNEDfYNdwYZzRfXoGJ5qIJQK+uJJv9noaIhfFowDW/G14Ji5p\r
+Vh/YtvnOPh7aBjOj8jmzk8MqzK+TZgT7GWu48Nd/NaV8g/DNg9hlN047LaNsJly3\r
+NX3+VBlpMnA4rKwl1OnmYSirIVh9RJqNwqe6k/k=</certificate>\r
+    </trusted-certificate>\r
+</keystore>\r
index f26655eb2e93261f617503b58d198d7fb75d6700..106b8c8b0cd454d5b47598de4ff563f91a38fae3 100644 (file)
@@ -19,7 +19,10 @@ import javax.annotation.PreDestroy;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.aaa.encrypt.AAAEncryptionService;
 import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.api.RpcProviderService;
+import org.opendaylight.mdsal.singleton.api.ClusterSingletonServiceProvider;
 import org.opendaylight.netconf.client.SslHandlerFactory;
 import org.opendaylight.netconf.client.mdsal.api.SslHandlerFactoryProvider;
 import org.opendaylight.netconf.keystore.legacy.AbstractNetconfKeystore;
@@ -43,8 +46,11 @@ public final class DefaultSslHandlerFactoryProvider extends AbstractNetconfKeyst
 
     @Inject
     @Activate
-    public DefaultSslHandlerFactoryProvider(@Reference final DataBroker dataBroker) {
-        start(dataBroker);
+    public DefaultSslHandlerFactoryProvider(@Reference final DataBroker dataBroker,
+            @Reference final RpcProviderService rpcProvider,
+            @Reference final ClusterSingletonServiceProvider cssProvider,
+            @Reference final AAAEncryptionService encryptionService) {
+        start(dataBroker, rpcProvider, cssProvider, encryptionService);
     }
 
     @Deactivate
diff --git a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/impl/NetconfSalKeystoreRpcs.java b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/impl/NetconfSalKeystoreRpcs.java
deleted file mode 100644 (file)
index 691a319..0000000
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * 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.client.mdsal.impl;
-
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableClassToInstanceMap;
-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.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.util.ArrayList;
-import java.util.Base64;
-import javax.annotation.PreDestroy;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-import org.opendaylight.aaa.encrypt.AAAEncryptionService;
-import org.opendaylight.mdsal.binding.api.DataBroker;
-import org.opendaylight.mdsal.binding.api.RpcProviderService;
-import org.opendaylight.mdsal.binding.api.WriteTransaction;
-import org.opendaylight.mdsal.common.api.CommitInfo;
-import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.mdsal.dom.api.DefaultDOMRpcException;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddKeystoreEntry;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddKeystoreEntryInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddKeystoreEntryOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddKeystoreEntryOutputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKeyInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKeyOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKeyOutputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificate;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificateInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificateOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificateOutputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.Keystore;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveKeystoreEntry;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveKeystoreEntryInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveKeystoreEntryOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveKeystoreEntryOutputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemovePrivateKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemovePrivateKeyInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemovePrivateKeyOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemovePrivateKeyOutputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveTrustedCertificate;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveTrustedCertificateInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveTrustedCertificateOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveTrustedCertificateOutputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017._private.keys.PrivateKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017._private.keys.PrivateKeyKey;
-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.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.trusted.certificates.TrustedCertificate;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.trusted.certificates.TrustedCertificateKey;
-import org.opendaylight.yangtools.concepts.Registration;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.binding.Rpc;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Deactivate;
-import org.osgi.service.component.annotations.Reference;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@Singleton
-@Component(service = { })
-public final class NetconfSalKeystoreRpcs implements AutoCloseable {
-    private static final Logger LOG = LoggerFactory.getLogger(NetconfSalKeystoreRpcs.class);
-    private static final InstanceIdentifier<Keystore> KEYSTORE_IID = InstanceIdentifier.create(Keystore.class);
-
-    // FIXME: we are populating config datastore, but there may be risks with concurrent access. We really should be
-    //        using cluster singleton service here.
-    private final DataBroker dataBroker;
-    private final AAAEncryptionService encryptionService;
-    private final Registration reg;
-
-    @Inject
-    @Activate
-    public NetconfSalKeystoreRpcs(@Reference final DataBroker dataBroker,
-            @Reference final AAAEncryptionService encryptionService, @Reference final RpcProviderService rpcProvider) {
-        this.dataBroker = requireNonNull(dataBroker);
-        this.encryptionService = requireNonNull(encryptionService);
-
-        reg = rpcProvider.registerRpcImplementations(ImmutableClassToInstanceMap.<Rpc<?, ?>>builder()
-            .put(RemoveKeystoreEntry.class, this::removeKeystoreEntry)
-            .put(AddKeystoreEntry.class, this::addKeystoreEntry)
-            .put(AddTrustedCertificate.class, this::addTrustedCertificate)
-            .put(RemoveTrustedCertificate.class, this::removeTrustedCertificate)
-            .put(AddPrivateKey.class, this::addPrivateKey)
-            .put(RemovePrivateKey.class, this::removePrivateKey)
-            .build());
-        LOG.info("NETCONF keystore service started");
-    }
-
-    @PreDestroy
-    @Deactivate
-    @Override
-    public void close() {
-        reg.close();
-        LOG.info("NETCONF keystore service stopped");
-    }
-
-    private ListenableFuture<RpcResult<RemoveKeystoreEntryOutput>> removeKeystoreEntry(
-            final RemoveKeystoreEntryInput input) {
-        LOG.debug("Removing keypairs: {}", input);
-
-        final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
-
-        for (final String id : input.getKeyId()) {
-            writeTransaction.delete(LogicalDatastoreType.CONFIGURATION,
-                KEYSTORE_IID.child(KeyCredential.class, new KeyCredentialKey(id)));
-        }
-
-        final SettableFuture<RpcResult<RemoveKeystoreEntryOutput>> rpcResult = SettableFuture.create();
-
-        writeTransaction.commit().addCallback(new FutureCallback<CommitInfo>() {
-            @Override
-            public void onSuccess(final CommitInfo result) {
-                LOG.debug("remove-key-pair success. Input: {}", input);
-                rpcResult.set(RpcResultBuilder.success(new RemoveKeystoreEntryOutputBuilder().build()).build());
-            }
-
-            @Override
-            public void onFailure(final Throwable throwable) {
-                LOG.warn("remove-key-pair failed. Input: {}", input, throwable);
-                rpcResult.setException(throwable);
-            }
-        }, MoreExecutors.directExecutor());
-
-        return rpcResult;
-    }
-
-    private ListenableFuture<RpcResult<AddKeystoreEntryOutput>> addKeystoreEntry(final AddKeystoreEntryInput input) {
-        LOG.debug("Adding keypairs: {}", input);
-
-        final var plain = input.nonnullKeyCredential();
-        final var encrypted = new ArrayList<KeyCredential>(plain.size());
-        for (var credential : plain.values()) {
-            try {
-                encrypted.add(new KeyCredentialBuilder(credential)
-                    .setPrivateKey(encryptString(credential.getPrivateKey()))
-                    .setPassphrase(encryptString(credential.getPassphrase()))
-                    .build());
-            } catch (GeneralSecurityException e) {
-                return Futures.immediateFailedFuture(new DefaultDOMRpcException("Failed to decrypt " + credential, e));
-            }
-        }
-
-        final var writeTransaction = dataBroker.newWriteOnlyTransaction();
-        for (var keypair : encrypted) {
-            writeTransaction.merge(LogicalDatastoreType.CONFIGURATION,
-                KEYSTORE_IID.child(KeyCredential.class, keypair.key()), keypair);
-        }
-
-        final var rpcResult = SettableFuture.<RpcResult<AddKeystoreEntryOutput>>create();
-        writeTransaction.commit().addCallback(new FutureCallback<CommitInfo>() {
-            @Override
-            public void onSuccess(final CommitInfo result) {
-                LOG.debug("add-key-pair success. Input: {}", input);
-                rpcResult.set(RpcResultBuilder.success(new AddKeystoreEntryOutputBuilder().build()).build());
-            }
-
-            @Override
-            public void onFailure(final Throwable throwable) {
-                LOG.warn("add-key-pair failed. Input: {}", input, throwable);
-                rpcResult.setException(throwable);
-            }
-        }, MoreExecutors.directExecutor());
-        return rpcResult;
-    }
-
-    private String encryptString(final String plain) throws GeneralSecurityException {
-        return Base64.getEncoder().encodeToString(encryptionService.encrypt(plain.getBytes(StandardCharsets.UTF_8)));
-    }
-
-    @VisibleForTesting
-    ListenableFuture<RpcResult<AddTrustedCertificateOutput>> addTrustedCertificate(
-            final AddTrustedCertificateInput input) {
-        final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
-
-        for (TrustedCertificate certificate : input.nonnullTrustedCertificate().values()) {
-            writeTransaction.merge(LogicalDatastoreType.CONFIGURATION,
-                KEYSTORE_IID.child(TrustedCertificate.class, certificate.key()), certificate);
-        }
-
-        final SettableFuture<RpcResult<AddTrustedCertificateOutput>> rpcResult = SettableFuture.create();
-
-        writeTransaction.commit().addCallback(new FutureCallback<CommitInfo>() {
-            @Override
-            public void onSuccess(final CommitInfo result) {
-                LOG.debug("add-trusted-certificate success. Input: {}", input);
-                rpcResult.set(RpcResultBuilder.success(new AddTrustedCertificateOutputBuilder().build()).build());
-            }
-
-            @Override
-            public void onFailure(final Throwable throwable) {
-                LOG.warn("add-trusted-certificate failed. Input: {}", input, throwable);
-                rpcResult.setException(throwable);
-            }
-        }, MoreExecutors.directExecutor());
-
-        return rpcResult;
-    }
-
-    private ListenableFuture<RpcResult<RemoveTrustedCertificateOutput>> removeTrustedCertificate(
-            final RemoveTrustedCertificateInput input) {
-        final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
-
-        for (final String name : input.getName()) {
-            writeTransaction.delete(LogicalDatastoreType.CONFIGURATION,
-                KEYSTORE_IID.child(TrustedCertificate.class, new TrustedCertificateKey(name)));
-        }
-
-        final SettableFuture<RpcResult<RemoveTrustedCertificateOutput>> rpcResult = SettableFuture.create();
-
-        writeTransaction.commit().addCallback(new FutureCallback<CommitInfo>() {
-            @Override
-            public void onSuccess(final CommitInfo result) {
-                LOG.debug("remove-trusted-certificate success. Input: {}", input);
-                rpcResult.set(RpcResultBuilder.success(new RemoveTrustedCertificateOutputBuilder().build()).build());
-            }
-
-            @Override
-            public void onFailure(final Throwable throwable) {
-                LOG.warn("remove-trusted-certificate failed. Input: {}", input, throwable);
-                rpcResult.setException(throwable);
-            }
-        }, MoreExecutors.directExecutor());
-
-        return rpcResult;
-    }
-
-    @VisibleForTesting
-    ListenableFuture<RpcResult<AddPrivateKeyOutput>> addPrivateKey(final AddPrivateKeyInput input) {
-        final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
-
-        for (PrivateKey key: input.nonnullPrivateKey().values()) {
-            writeTransaction.merge(LogicalDatastoreType.CONFIGURATION,
-                KEYSTORE_IID.child(PrivateKey.class, key.key()), key);
-        }
-
-        final SettableFuture<RpcResult<AddPrivateKeyOutput>> rpcResult = SettableFuture.create();
-
-        writeTransaction.commit().addCallback(new FutureCallback<CommitInfo>() {
-            @Override
-            public void onSuccess(final CommitInfo result) {
-                LOG.debug("add-private-key success. Input: {}", input);
-                rpcResult.set(RpcResultBuilder.success(new AddPrivateKeyOutputBuilder().build()).build());
-            }
-
-            @Override
-            public void onFailure(final Throwable throwable) {
-                LOG.warn("add-private-key failed. Input: {}", input, throwable);
-                rpcResult.setException(throwable);
-            }
-        }, MoreExecutors.directExecutor());
-
-        return rpcResult;
-    }
-
-    private ListenableFuture<RpcResult<RemovePrivateKeyOutput>> removePrivateKey(final RemovePrivateKeyInput input) {
-        final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
-
-        for (final String name : input.getName()) {
-            writeTransaction.delete(LogicalDatastoreType.CONFIGURATION,
-                KEYSTORE_IID.child(PrivateKey.class, new PrivateKeyKey(name)));
-        }
-
-        final SettableFuture<RpcResult<RemovePrivateKeyOutput>> rpcResult = SettableFuture.create();
-
-        writeTransaction.commit().addCallback(new FutureCallback<CommitInfo>() {
-            @Override
-            public void onSuccess(final CommitInfo result) {
-                LOG.debug("remove-private-key success. Input: {}", input);
-                rpcResult.set(RpcResultBuilder.success(new RemovePrivateKeyOutputBuilder().build()).build());
-            }
-
-            @Override
-            public void onFailure(final Throwable throwable) {
-                LOG.warn("remove-private-key failed. Input: {}", input, throwable);
-                rpcResult.setException(throwable);
-            }
-        }, MoreExecutors.directExecutor());
-
-        return rpcResult;
-    }
-}
index 08f41b46dbcba57dba058a45118943042a54e076..c20414ec3b7813720160727fab49cb9c09d69a26 100644 (file)
@@ -24,10 +24,13 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
+import org.opendaylight.aaa.encrypt.AAAEncryptionService;
 import org.opendaylight.mdsal.binding.api.DataBroker;
 import org.opendaylight.mdsal.binding.api.DataObjectModification;
 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
 import org.opendaylight.mdsal.binding.api.DataTreeModification;
+import org.opendaylight.mdsal.binding.api.RpcProviderService;
+import org.opendaylight.mdsal.singleton.api.ClusterSingletonServiceProvider;
 import org.opendaylight.netconf.api.xml.XmlUtil;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.Keystore;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017._private.keys.PrivateKey;
@@ -52,6 +55,12 @@ class DefaultSslHandlerFactoryProviderTest {
     @Mock
     private DataBroker dataBroker;
     @Mock
+    private RpcProviderService rpcProvider;
+    @Mock
+    private ClusterSingletonServiceProvider cssProvider;
+    @Mock
+    private AAAEncryptionService encryptionService;
+    @Mock
     private Registration listenerRegistration;
     @Mock
     private DataTreeModification<Keystore> dataTreeModification1;
@@ -76,9 +85,13 @@ class DefaultSslHandlerFactoryProviderTest {
         }).when(dataBroker).registerTreeChangeListener(any(), any());
     }
 
+    private DefaultSslHandlerFactoryProvider newProvider() {
+        return new DefaultSslHandlerFactoryProvider(dataBroker, rpcProvider, cssProvider, encryptionService);
+    }
+
     @Test
     void testKeystoreAdapterInit() throws Exception {
-        try (var keystoreAdapter = new DefaultSslHandlerFactoryProvider(dataBroker)) {
+        try (var keystoreAdapter = newProvider()) {
             final var ex = assertThrows(KeyStoreException.class, () -> keystoreAdapter.getJavaKeyStore(Set.of()));
             assertThat(ex.getMessage(), startsWith("No keystore private key found"));
         }
@@ -94,7 +107,7 @@ class DefaultSslHandlerFactoryProviderTest {
         final var privateKey = getPrivateKey();
         doReturn(privateKey).when(privateKeyModification).dataAfter();
 
-        try (var keystoreAdapter = new DefaultSslHandlerFactoryProvider(dataBroker)) {
+        try (var keystoreAdapter = newProvider()) {
             listener.onDataTreeChanged(List.of(dataTreeModification1));
 
             final var keyStore = keystoreAdapter.getJavaKeyStore(Set.of());
@@ -125,7 +138,7 @@ class DefaultSslHandlerFactoryProviderTest {
         final var trustedCertificate = getTrustedCertificate();
         doReturn(trustedCertificate).when(trustedCertificateModification).dataAfter();
 
-        try (var keystoreAdapter = new DefaultSslHandlerFactoryProvider(dataBroker)) {
+        try (var keystoreAdapter = newProvider()) {
             // Apply configurations
             listener.onDataTreeChanged(List.of(dataTreeModification1, dataTreeModification2));
 
diff --git a/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/impl/NetconfSalKeystoreRpcsTest.java b/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/impl/NetconfSalKeystoreRpcsTest.java
deleted file mode 100644 (file)
index 18e357e..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (c) 2018 ZTE Corporation. 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.client.mdsal.impl;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.opendaylight.mdsal.common.api.CommitInfo.emptyFluentFuture;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-import org.opendaylight.aaa.encrypt.AAAEncryptionService;
-import org.opendaylight.mdsal.binding.api.DataBroker;
-import org.opendaylight.mdsal.binding.api.RpcProviderService;
-import org.opendaylight.mdsal.binding.api.WriteTransaction;
-import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.netconf.api.xml.XmlUtil;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKeyInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKeyInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificateInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificateInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017._private.keys.PrivateKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017._private.keys.PrivateKeyBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017._private.keys.PrivateKeyKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.trusted.certificates.TrustedCertificate;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.trusted.certificates.TrustedCertificateBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.trusted.certificates.TrustedCertificateKey;
-import org.opendaylight.yangtools.concepts.ObjectRegistration;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-@RunWith(MockitoJUnitRunner.StrictStubs.class)
-public class NetconfSalKeystoreRpcsTest {
-    private static final String XML_ELEMENT_PRIVATE_KEY = "private-key";
-    private static final String XML_ELEMENT_NAME = "name";
-    private static final String XML_ELEMENT_DATA = "data";
-    private static final String XML_ELEMENT_CERT_CHAIN = "certificate-chain";
-    private static final String XML_ELEMENT_TRUSTED_CERT = "trusted-certificate";
-    private static final String XML_ELEMENT_CERT = "certificate";
-
-    @Mock
-    private WriteTransaction writeTx;
-    @Mock
-    private DataBroker dataBroker;
-    @Mock
-    private AAAEncryptionService encryptionService;
-    @Mock
-    private RpcProviderService rpcProvider;
-    @Mock
-    private ObjectRegistration<?> rpcReg;
-
-    @Before
-    public void setUp() {
-        doReturn(writeTx).when(dataBroker).newWriteOnlyTransaction();
-        doNothing().when(writeTx)
-            .merge(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(DataObject.class));
-        doReturn(rpcReg).when(rpcProvider).registerRpcImplementations(any());
-        doNothing().when(rpcReg).close();
-    }
-
-    @Test
-    public void testAddPrivateKey() throws Exception {
-        doReturn(emptyFluentFuture()).when(writeTx).commit();
-        try (var keystoreService = new NetconfSalKeystoreRpcs(dataBroker, encryptionService, rpcProvider)) {
-            final AddPrivateKeyInput input = getPrivateKeyInput();
-            keystoreService.addPrivateKey(input).get();
-
-            verify(writeTx, times(input.nonnullPrivateKey().size()))
-                .merge(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(DataObject.class));
-        }
-    }
-
-    @Test
-    public void testAddTrustedCertificate() throws Exception {
-        doReturn(emptyFluentFuture()).when(writeTx).commit();
-        try (var keystoreService = new NetconfSalKeystoreRpcs(dataBroker, encryptionService, rpcProvider)) {
-            final var input = getTrustedCertificateInput();
-            keystoreService.addTrustedCertificate(input).get();
-
-            verify(writeTx, times(input.nonnullTrustedCertificate().size()))
-                .merge(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(DataObject.class));
-        }
-    }
-
-    private AddPrivateKeyInput getPrivateKeyInput() throws Exception {
-        final Map<PrivateKeyKey, PrivateKey> privateKeys = new HashMap<>();
-        final Document document = readKeystoreXML();
-        final NodeList nodeList = document.getElementsByTagName(XML_ELEMENT_PRIVATE_KEY);
-        for (int i = 0; i < nodeList.getLength(); i++) {
-            final Node node = nodeList.item(i);
-            if (node.getNodeType() != Node.ELEMENT_NODE) {
-                continue;
-            }
-            final Element element = (Element)node;
-            final String keyName = element.getElementsByTagName(XML_ELEMENT_NAME).item(0).getTextContent();
-            final String keyData = element.getElementsByTagName(XML_ELEMENT_DATA).item(0).getTextContent();
-            final NodeList certNodes = element.getElementsByTagName(XML_ELEMENT_CERT_CHAIN);
-            final List<String> certChain = new ArrayList<>();
-            for (int j = 0; j < certNodes.getLength(); j++) {
-                final Node certNode = certNodes.item(j);
-                if (certNode.getNodeType() != Node.ELEMENT_NODE) {
-                    continue;
-                }
-                certChain.add(certNode.getTextContent());
-            }
-
-            final PrivateKeyKey key = new PrivateKeyKey(keyName);
-            privateKeys.put(key, new PrivateKeyBuilder()
-                .withKey(key)
-                .setData(keyData)
-                .setCertificateChain(certChain)
-                .build());
-        }
-
-        return new AddPrivateKeyInputBuilder().setPrivateKey(privateKeys).build();
-    }
-
-    private AddTrustedCertificateInput getTrustedCertificateInput() throws Exception {
-        final Map<TrustedCertificateKey, TrustedCertificate> trustedCertificates = new HashMap<>();
-        final Document document = readKeystoreXML();
-        final NodeList nodeList = document.getElementsByTagName(XML_ELEMENT_TRUSTED_CERT);
-        for (int i = 0; i < nodeList.getLength(); i++) {
-            final Node node = nodeList.item(i);
-            if (node.getNodeType() != Node.ELEMENT_NODE) {
-                continue;
-            }
-            final Element element = (Element)node;
-            final String certName = element.getElementsByTagName(XML_ELEMENT_NAME).item(0).getTextContent();
-            final String certData = element.getElementsByTagName(XML_ELEMENT_CERT).item(0).getTextContent();
-
-            final TrustedCertificateKey key = new TrustedCertificateKey(certName);
-            trustedCertificates.put(key, new TrustedCertificateBuilder()
-                .withKey(key)
-                .setName(certName)
-                .setCertificate(certData)
-                .build());
-        }
-
-        return new AddTrustedCertificateInputBuilder().setTrustedCertificate(trustedCertificates).build();
-    }
-
-    private Document readKeystoreXML() throws Exception {
-        return XmlUtil.readXmlToDocument(getClass().getResourceAsStream("/netconf-keystore.xml"));
-    }
-}