Expose NetconfKeystoreService 10/110110/3
authorRobert Varga <robert.varga@pantheon.tech>
Wed, 31 Jan 2024 21:56:33 +0000 (22:56 +0100)
committerRobert Varga <nite@hq.sk>
Thu, 1 Feb 2024 09:22:02 +0000 (09:22 +0000)
We are performing translation of datastore configuration state into Java
equivalent. Let's expose that as a service and run a full component,
which supports asynchronous updates.

This also defers start by not reporting initial state until we have
observed the datastore.

JIRA: NETCONF-1237
Change-Id: Ib6b34d707eb664a94cb4a30049e50444f47fc5ae
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
20 files changed:
keystore/keystore-legacy/pom.xml
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/CertifiedPrivateKey.java [new file with mode: 0644]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/NetconfKeystore.java [new file with mode: 0644]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/NetconfKeystoreService.java [new file with mode: 0644]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/AbstractRpc.java [moved from keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/AbstractRpc.java with 93% similarity]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/ConfigListener.java [moved from keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/ConfigListener.java with 92% similarity]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/DefaultAddKeystoreEntry.java [moved from keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultAddKeystoreEntry.java with 98% similarity]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/DefaultAddPrivateKey.java [moved from keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultAddPrivateKey.java with 97% similarity]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/DefaultAddTrustedCertificate.java [moved from keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultAddTrustedCertificate.java with 97% similarity]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/DefaultNetconfKeystoreService.java [moved from keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/AbstractNetconfKeystore.java with 69% similarity]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/DefaultRemoveKeystoreEntry.java [moved from keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultRemoveKeystoreEntry.java with 98% similarity]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/DefaultRemovePrivateKey.java [moved from keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultRemovePrivateKey.java with 98% similarity]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/DefaultRemoveTrustedCertificate.java [moved from keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultRemoveTrustedCertificate.java with 98% similarity]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/RpcSingleton.java [moved from keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/RpcSingleton.java with 98% similarity]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/SecurityHelper.java [moved from keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/SecurityHelper.java with 97% similarity]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/package-info.java [new file with mode: 0644]
keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/package-info.java [new file with mode: 0644]
keystore/keystore-legacy/src/test/java/org/opendaylight/netconf/keystore/legacy/impl/NetconfKeystoreRpcsTest.java [moved from keystore/keystore-legacy/src/test/java/org/opendaylight/netconf/keystore/legacy/NetconfKeystoreRpcsTest.java with 99% similarity]
plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/impl/DefaultSslHandlerFactoryProvider.java
plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/impl/DefaultSslHandlerFactoryProviderTest.java

index 94f2b5a5dfebb262da623123cce5fb0a6832c9d1..209a25ea72e00f61dc349915af983d4e4dd46be9 100644 (file)
     <description>Legacy NETCONF keystore</description>
 
     <dependencies>
+        <dependency>
+            <groupId>com.guicedee.services</groupId>
+            <artifactId>javax.inject</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.annotation</groupId>
+            <artifactId>jakarta.annotation-api</artifactId>
+            <scope>provided</scope>
+            <optional>true</optional>
+        </dependency>
         <dependency>
             <groupId>org.opendaylight.aaa</groupId>
             <artifactId>aaa-encrypt-service</artifactId>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>concepts</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.component.annotations</artifactId>
+        </dependency>
 
         <dependency>
             <groupId>org.opendaylight.netconf</groupId>
             <scope>test</scope>
         </dependency>
     </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.netconf.keystore.legacy</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
 </project>
diff --git a/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/CertifiedPrivateKey.java b/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/CertifiedPrivateKey.java
new file mode 100644 (file)
index 0000000..e350153
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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 java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+@NonNullByDefault
+public record CertifiedPrivateKey(
+        PrivateKey key,
+        List<X509Certificate> certificateChain) implements Immutable {
+    public CertifiedPrivateKey {
+        requireNonNull(key);
+        certificateChain = List.copyOf(certificateChain);
+        if (certificateChain.isEmpty()) {
+            throw new IllegalArgumentException("Certificate chain must not be empty");
+        }
+    }
+}
\ No newline at end of file
diff --git a/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/NetconfKeystore.java b/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/NetconfKeystore.java
new file mode 100644 (file)
index 0000000..0f04c0b
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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 java.security.cert.X509Certificate;
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+@NonNullByDefault
+public record NetconfKeystore(
+        Map<String, CertifiedPrivateKey> privateKeys,
+        Map<String, X509Certificate> trustedCertificates) implements Immutable {
+    public static final NetconfKeystore EMPTY = new NetconfKeystore(Map.of(), Map.of());
+
+    public NetconfKeystore {
+        privateKeys = Map.copyOf(privateKeys);
+        trustedCertificates = Map.copyOf(trustedCertificates);
+    }
+}
diff --git a/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/NetconfKeystoreService.java b/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/NetconfKeystoreService.java
new file mode 100644 (file)
index 0000000..11c1b0c
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * 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 java.util.function.Consumer;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.Registration;
+
+/**
+ * Asynchronous access to {@link NetconfKeystore}.
+ */
+@NonNullByDefault
+public interface NetconfKeystoreService {
+
+    Registration registerKeystoreConsumer(Consumer<NetconfKeystore> consumer);
+}
similarity index 93%
rename from keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/AbstractRpc.java
rename to keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/AbstractRpc.java
index b2db630f36be76f8a49b72aa1cd48d8669c226f6..920ca6c6dc5b7f2789b222a185c9705758118653 100644 (file)
@@ -5,7 +5,7 @@
  * 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;
+package org.opendaylight.netconf.keystore.legacy.impl;
 
 import static java.util.Objects.requireNonNull;
 
similarity index 92%
rename from keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/ConfigListener.java
rename to keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/ConfigListener.java
index 35d3c4593942465a1b94ee1720e8d8f2e1c4e778..cc1f9e1c5b59ce03d08473d94552d37bb34179f1 100644 (file)
@@ -5,7 +5,7 @@
  * 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;
+package org.opendaylight.netconf.keystore.legacy.impl;
 
 import static java.util.Objects.requireNonNull;
 
@@ -14,7 +14,7 @@ import java.util.List;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
 import org.opendaylight.mdsal.binding.api.DataTreeModification;
-import org.opendaylight.netconf.keystore.legacy.AbstractNetconfKeystore.ConfigStateBuilder;
+import org.opendaylight.netconf.keystore.legacy.impl.DefaultNetconfKeystoreService.ConfigStateBuilder;
 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;
@@ -22,7 +22,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @NonNullByDefault
-record ConfigListener(AbstractNetconfKeystore keystore) implements DataTreeChangeListener<Keystore> {
+record ConfigListener(DefaultNetconfKeystoreService keystore) implements DataTreeChangeListener<Keystore> {
     private static final Logger LOG = LoggerFactory.getLogger(ConfigListener.class);
 
     ConfigListener {
similarity index 98%
rename from keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultAddKeystoreEntry.java
rename to keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/DefaultAddKeystoreEntry.java
index 828ebaf246fbc5161ad439ef1aa0a7aa98d08da7..d40e02a258d7f190067dcb8b87c96c93d340cc6d 100644 (file)
@@ -5,7 +5,7 @@
  * 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;
+package org.opendaylight.netconf.keystore.legacy.impl;
 
 import static java.util.Objects.requireNonNull;
 
similarity index 97%
rename from keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultAddPrivateKey.java
rename to keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/DefaultAddPrivateKey.java
index a8e4251f8ca98695b9ece56639719b47746a26e3..f3dfc18611bbaa52bfe151be7f57985da59a1aa2 100644 (file)
@@ -5,7 +5,7 @@
  * 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;
+package org.opendaylight.netconf.keystore.legacy.impl;
 
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
@@ -5,7 +5,7 @@
  * 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;
+package org.opendaylight.netconf.keystore.legacy.impl;
 
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
@@ -5,7 +5,7 @@
  * 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;
+package org.opendaylight.netconf.keystore.legacy.impl;
 
 import static java.util.Objects.requireNonNull;
 
@@ -16,10 +16,14 @@ import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Base64;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Consumer;
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+import javax.inject.Singleton;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
@@ -29,45 +33,31 @@ 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.netconf.keystore.legacy.CertifiedPrivateKey;
+import org.opendaylight.netconf.keystore.legacy.NetconfKeystore;
+import org.opendaylight.netconf.keystore.legacy.NetconfKeystoreService;
 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;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.concepts.Mutable;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
 import org.opendaylight.yangtools.concepts.Registration;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+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;
 
 /**
  * Abstract substrate for implementing security services based on the contents of {@link Keystore}.
  */
-public abstract class AbstractNetconfKeystore {
-    @NonNullByDefault
-    protected record CertifiedPrivateKey(
-            java.security.PrivateKey key,
-            List<X509Certificate> certificateChain) implements Immutable {
-        public CertifiedPrivateKey {
-            requireNonNull(key);
-            certificateChain = List.copyOf(certificateChain);
-            if (certificateChain.isEmpty()) {
-                throw new IllegalArgumentException("Certificate chain must not be empty");
-            }
-        }
-    }
-
-    @NonNullByDefault
-    protected record State(
-            Map<String, CertifiedPrivateKey> privateKeys,
-            Map<String, X509Certificate> trustedCertificates) implements Immutable {
-        public static final State EMPTY = new State(Map.of(), Map.of());
-
-        public State {
-            privateKeys = Map.copyOf(privateKeys);
-            trustedCertificates = Map.copyOf(trustedCertificates);
-        }
-    }
-
+@Singleton
+@Component(service = NetconfKeystoreService.class)
+public final class DefaultNetconfKeystoreService implements NetconfKeystoreService, AutoCloseable {
     @NonNullByDefault
     private record ConfigState(
             Map<String, PrivateKey> privateKeys,
@@ -90,48 +80,60 @@ public abstract class AbstractNetconfKeystore {
         }
     }
 
-    private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfKeystore.class);
+    private static final Logger LOG = LoggerFactory.getLogger(DefaultNetconfKeystoreService.class);
 
-    private final AtomicReference<@NonNull ConfigState> state = new AtomicReference<>(ConfigState.EMPTY);
+    private final Set<ObjectRegistration<Consumer<NetconfKeystore>>> consumers = ConcurrentHashMap.newKeySet();
+    private final AtomicReference<NetconfKeystore> keystore = new AtomicReference<>(null);
+    private final AtomicReference<ConfigState> config = new AtomicReference<>(ConfigState.EMPTY);
     private final SecurityHelper securityHelper = new SecurityHelper();
+    private final Registration configListener;
+    private final Registration rpcSingleton;
+
+    @Inject
+    @Activate
+    public DefaultNetconfKeystoreService(@Reference final DataBroker dataBroker,
+            @Reference final RpcProviderService rpcProvider,
+            @Reference final ClusterSingletonServiceProvider cssProvider,
+            @Reference final AAAEncryptionService encryptionService) {
+        configListener = dataBroker.registerTreeChangeListener(
+            DataTreeIdentifier.of(LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(Keystore.class)),
+            new ConfigListener(this));
+        rpcSingleton = cssProvider.registerClusterSingletonService(
+            new RpcSingleton(dataBroker, rpcProvider, encryptionService));
+
+        // FIXME: create an operation datastore updater and register it as a consumer
 
-    private @Nullable Registration configListener;
-    private @Nullable Registration rpcSingleton;
-
-    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;
-            listener.close();
-            state.set(ConfigState.EMPTY);
-        }
+    @PreDestroy
+    @Deactivate
+    @Override
+    public void close() {
+        rpcSingleton.close();
+        configListener.close();
+        LOG.info("NETCONF keystore service stopped");
     }
 
-    protected abstract void onStateUpdated(@NonNull State newState);
+    @Override
+    public Registration registerKeystoreConsumer(final Consumer<NetconfKeystore> consumer) {
+        final var reg = new AbstractObjectRegistration<>(consumer) {
+            @Override
+            protected void removeRegistration() {
+                consumers.remove(this);
+            }
+        };
 
-    final void runUpdate(final Consumer<@NonNull ConfigStateBuilder> task) {
-        final var prevState = state.getAcquire();
+        consumers.add(reg);
+        final var ks = keystore.getAcquire();
+        if (ks != null) {
+            consumer.accept(ks);
+        }
+        return reg;
+    }
+
+    void runUpdate(final Consumer<@NonNull ConfigStateBuilder> task) {
+        final var prevState = config.getAcquire();
 
         final var builder = new ConfigStateBuilder(new HashMap<>(prevState.privateKeys),
             new HashMap<>(prevState.trustedCertificates));
@@ -139,7 +141,7 @@ public abstract class AbstractNetconfKeystore {
         final var newState = new ConfigState(builder.privateKeys, builder.trustedCertificates);
 
         // Careful application -- check if listener is still up and whether the state was not updated.
-        if (configListener == null || state.compareAndExchangeRelease(prevState, newState) != prevState) {
+        if (configListener == null || config.compareAndExchangeRelease(prevState, newState) != prevState) {
             return;
         }
 
@@ -231,9 +233,9 @@ public abstract class AbstractNetconfKeystore {
             return;
         }
 
-        onStateUpdated(new State(keys, certs));
-
-        // FIXME: tickle operational updater (which does not exist yet)
+        final var newKeystore = new NetconfKeystore(keys, certs);
+        keystore.setRelease(newKeystore);
+        consumers.forEach(consumer -> consumer.getInstance().accept(newKeystore));
     }
 
     private static byte[] base64Decode(final String base64) {
@@ -5,7 +5,7 @@
  * 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;
+package org.opendaylight.netconf.keystore.legacy.impl;
 
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
similarity index 98%
rename from keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/DefaultRemovePrivateKey.java
rename to keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/DefaultRemovePrivateKey.java
index 04a3dbd9f0d0ba2c2299e0ea4cc71d182e5eba41..7d3e7d6557744b242197e57c9dcdf2f1eb7311cf 100644 (file)
@@ -5,7 +5,7 @@
  * 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;
+package org.opendaylight.netconf.keystore.legacy.impl;
 
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
@@ -5,7 +5,7 @@
  * 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;
+package org.opendaylight.netconf.keystore.legacy.impl;
 
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
similarity index 98%
rename from keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/RpcSingleton.java
rename to keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/RpcSingleton.java
index e49c7142453a329201618005fa4d715633df5c6b..08b1079d3d43aa2c2f800372befcab70ae625a0f 100644 (file)
@@ -5,7 +5,7 @@
  * 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;
+package org.opendaylight.netconf.keystore.legacy.impl;
 
 import static java.util.Objects.requireNonNull;
 
similarity index 97%
rename from keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/SecurityHelper.java
rename to keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/SecurityHelper.java
index 18b3b547da51def17bf232ead9fadbea8e9b0eb7..679970e99dbbde6ed19ef04e511cc6a5e4bfefbc 100644 (file)
@@ -5,7 +5,7 @@
  * 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;
+package org.opendaylight.netconf.keystore.legacy.impl;
 
 import java.io.ByteArrayInputStream;
 import java.security.GeneralSecurityException;
diff --git a/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/package-info.java b/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/impl/package-info.java
new file mode 100644 (file)
index 0000000..3524f77
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * 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
+ */
+/**
+ * Implementation details.
+ */
+package org.opendaylight.netconf.keystore.legacy.impl;
\ No newline at end of file
diff --git a/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/package-info.java b/keystore/keystore-legacy/src/main/java/org/opendaylight/netconf/keystore/legacy/package-info.java
new file mode 100644 (file)
index 0000000..8ed81de
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * 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
+ */
+/**
+ * Access to key and trust material stored in
+ * {@link org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.NetconfKeystoreData}.
+ * The primary access interface is {@link NetconfKeystoreService}, which allows subscription to receive updates about
+ * {@link NetconfKeystore}.
+ */
+package org.opendaylight.netconf.keystore.legacy;
\ No newline at end of file
similarity index 99%
rename from keystore/keystore-legacy/src/test/java/org/opendaylight/netconf/keystore/legacy/NetconfKeystoreRpcsTest.java
rename to keystore/keystore-legacy/src/test/java/org/opendaylight/netconf/keystore/legacy/impl/NetconfKeystoreRpcsTest.java
index 5396420339579749b02318549d16322d40afb77e..1b1e845da86bbf8cea99fa967995bab2c60c5826 100644 (file)
@@ -5,7 +5,7 @@
  * 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;
+package org.opendaylight.netconf.keystore.legacy.impl;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
index 106b8c8b0cd454d5b47598de4ff563f91a38fae3..0d98fa0bb043c5b957be8b79a83c868fd0ad6019 100644 (file)
@@ -19,15 +19,13 @@ 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;
+import org.opendaylight.netconf.keystore.legacy.NetconfKeystore;
+import org.opendaylight.netconf.keystore.legacy.NetconfKeystoreService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.parameters.protocol.Specification;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.parameters.protocol.specification.TlsCase;
+import org.opendaylight.yangtools.concepts.Registration;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Deactivate;
@@ -35,34 +33,30 @@ import org.osgi.service.component.annotations.Reference;
 
 @Singleton
 @Component(service = SslHandlerFactoryProvider.class)
-public final class DefaultSslHandlerFactoryProvider extends AbstractNetconfKeystore
-        implements SslHandlerFactoryProvider, AutoCloseable {
+public final class DefaultSslHandlerFactoryProvider implements SslHandlerFactoryProvider, AutoCloseable {
     private static final X509Certificate[] EMPTY_CERTS = { };
     private static final char[] EMPTY_CHARS = { };
 
     private final @NonNull SslHandlerFactory nospecFactory = new SslHandlerFactoryImpl(this, Set.of());
+    private final Registration reg;
 
-    private volatile @NonNull State state = State.EMPTY;
+    private volatile @NonNull NetconfKeystore keystore = NetconfKeystore.EMPTY;
 
     @Inject
     @Activate
-    public DefaultSslHandlerFactoryProvider(@Reference final DataBroker dataBroker,
-            @Reference final RpcProviderService rpcProvider,
-            @Reference final ClusterSingletonServiceProvider cssProvider,
-            @Reference final AAAEncryptionService encryptionService) {
-        start(dataBroker, rpcProvider, cssProvider, encryptionService);
+    public DefaultSslHandlerFactoryProvider(@Reference final NetconfKeystoreService keystoreService) {
+        reg = keystoreService.registerKeystoreConsumer(this::onKeystoreUpdated);
     }
 
-    @Deactivate
-    @PreDestroy
     @Override
+    @PreDestroy
+    @Deactivate
     public void close() {
-        stop();
+        reg.close();
     }
 
-    @Override
-    protected void onStateUpdated(final State newState) {
-        state = newState;
+    private void onKeystoreUpdated(final @NonNull NetconfKeystore newKeystore) {
+        keystore = newKeystore;
     }
 
     @Override
@@ -91,7 +85,7 @@ public final class DefaultSslHandlerFactoryProvider extends AbstractNetconfKeyst
      */
     KeyStore getJavaKeyStore(final Set<String> allowedKeys) throws GeneralSecurityException, IOException {
         requireNonNull(allowedKeys);
-        final var current = state;
+        final var current = keystore;
         if (current.privateKeys().isEmpty()) {
             throw new KeyStoreException("No keystore private key found");
         }
index c20414ec3b7813720160727fab49cb9c09d69a26..5f37ba205e83bb335daa99189c265245645132c0 100644 (file)
@@ -19,6 +19,7 @@ import java.security.KeyStoreException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -32,6 +33,7 @@ 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.netconf.keystore.legacy.impl.DefaultNetconfKeystoreService;
 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._private.keys.PrivateKeyBuilder;
@@ -61,7 +63,7 @@ class DefaultSslHandlerFactoryProviderTest {
     @Mock
     private AAAEncryptionService encryptionService;
     @Mock
-    private Registration listenerRegistration;
+    private Registration registration;
     @Mock
     private DataTreeModification<Keystore> dataTreeModification1;
     @Mock
@@ -76,17 +78,25 @@ class DefaultSslHandlerFactoryProviderTest {
     private DataObjectModification<TrustedCertificate> trustedCertificateModification;
 
     private DataTreeChangeListener<Keystore> listener;
+    private DefaultNetconfKeystoreService keystore;
 
     @BeforeEach
     void beforeEach() {
         doAnswer(inv -> {
             listener = inv.getArgument(1);
-            return listenerRegistration;
+            return registration;
         }).when(dataBroker).registerTreeChangeListener(any(), any());
+        doReturn(registration).when(cssProvider).registerClusterSingletonService(any());
+        keystore = new DefaultNetconfKeystoreService(dataBroker, rpcProvider, cssProvider, encryptionService);
+    }
+
+    @AfterEach
+    void afterEach() {
+        keystore.close();
     }
 
     private DefaultSslHandlerFactoryProvider newProvider() {
-        return new DefaultSslHandlerFactoryProvider(dataBroker, rpcProvider, cssProvider, encryptionService);
+        return new DefaultSslHandlerFactoryProvider(keystore);
     }
 
     @Test