0cae9c5998d1f47d9ccf4afb1e60a7b3630d5d71
[netconf.git] / keystore / keystore-legacy / src / main / java / org / opendaylight / netconf / keystore / legacy / AbstractNetconfKeystore.java
1 /*
2  * Copyright (c) 2024 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netconf.keystore.legacy;
9
10 import static java.util.Objects.requireNonNull;
11
12 import java.util.HashMap;
13 import java.util.Map;
14 import java.util.concurrent.atomic.AtomicReference;
15 import java.util.function.Consumer;
16 import org.eclipse.jdt.annotation.NonNull;
17 import org.eclipse.jdt.annotation.NonNullByDefault;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.opendaylight.mdsal.binding.api.DataBroker;
20 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
21 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.Keystore;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017._private.keys.PrivateKey;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.trusted.certificates.TrustedCertificate;
25 import org.opendaylight.yangtools.concepts.Immutable;
26 import org.opendaylight.yangtools.concepts.Registration;
27 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
28
29 /**
30  * Abstract substrate for implementing security services based on the contents of {@link Keystore}.
31  */
32 public abstract class AbstractNetconfKeystore {
33     @NonNullByDefault
34     protected record State(
35             Map<String, PrivateKey> privateKeys,
36             Map<String, TrustedCertificate> trustedCertificates) implements Immutable {
37         public static final State EMPTY = new State(Map.of(), Map.of());
38
39         public State {
40             privateKeys = Map.copyOf(privateKeys);
41             trustedCertificates = Map.copyOf(trustedCertificates);
42         }
43     }
44
45     @NonNullByDefault
46     private record ConfigState(
47             Map<String, PrivateKey> privateKeys,
48             Map<String, TrustedCertificate> trustedCertificates) implements Immutable {
49         static final ConfigState EMPTY = new ConfigState(Map.of(), Map.of());
50
51         ConfigState {
52             privateKeys = Map.copyOf(privateKeys);
53             trustedCertificates = Map.copyOf(trustedCertificates);
54         }
55     }
56
57     @NonNullByDefault
58     record ConfigStateBuilder(
59             HashMap<String, PrivateKey> privateKeys,
60             HashMap<String, TrustedCertificate> trustedCertificates) {
61         ConfigStateBuilder {
62             requireNonNull(privateKeys);
63             requireNonNull(trustedCertificates);
64         }
65     }
66
67     private final AtomicReference<@NonNull ConfigState> state = new AtomicReference<>(ConfigState.EMPTY);
68
69     private @Nullable Registration configListener;
70
71     protected final void start(final DataBroker dataBroker) {
72         if (configListener == null) {
73             configListener = dataBroker.registerTreeChangeListener(
74                 DataTreeIdentifier.of(LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(Keystore.class)),
75                 new ConfigListener(this));
76         }
77     }
78
79     protected final void stop() {
80         final var listener = configListener;
81         if (listener != null) {
82             configListener = null;
83             listener.close();
84             state.set(ConfigState.EMPTY);
85         }
86     }
87
88     protected abstract void onStateUpdated(@NonNull State newState);
89
90     final void runUpdate(final Consumer<@NonNull ConfigStateBuilder> task) {
91         final var prevState = state.getAcquire();
92
93         final var builder = new ConfigStateBuilder(new HashMap<>(prevState.privateKeys),
94             new HashMap<>(prevState.trustedCertificates));
95         task.accept(builder);
96         final var newState = new ConfigState(builder.privateKeys, builder.trustedCertificates);
97
98         // Careful application -- check if listener is still up and whether the state was not updated.
99         if (configListener == null || state.compareAndExchangeRelease(prevState, newState) != prevState) {
100             return;
101         }
102
103         // FIXME: compile to crypto
104
105         onStateUpdated(new State(newState.privateKeys, newState.trustedCertificates));
106
107         // FIXME: tickle operational updater (which does not exist yet)
108     }
109 }