Do not leak ClassToInstanceMap from NetconfSalKeystoreRpcs
[netconf.git] / plugins / netconf-client-mdsal / src / main / java / org / opendaylight / netconf / client / mdsal / impl / NetconfSalKeystoreRpcs.java
1 /*
2  * Copyright (c) 2017 Cisco Systems, Inc. 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.client.mdsal.impl;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.VisibleForTesting;
13 import com.google.common.collect.ImmutableClassToInstanceMap;
14 import com.google.common.util.concurrent.FutureCallback;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import com.google.common.util.concurrent.MoreExecutors;
17 import com.google.common.util.concurrent.SettableFuture;
18 import java.util.List;
19 import java.util.stream.Collectors;
20 import javax.annotation.PreDestroy;
21 import javax.inject.Inject;
22 import javax.inject.Singleton;
23 import org.opendaylight.aaa.encrypt.AAAEncryptionService;
24 import org.opendaylight.mdsal.binding.api.DataBroker;
25 import org.opendaylight.mdsal.binding.api.RpcProviderService;
26 import org.opendaylight.mdsal.binding.api.WriteTransaction;
27 import org.opendaylight.mdsal.common.api.CommitInfo;
28 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddKeystoreEntry;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddKeystoreEntryInput;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddKeystoreEntryOutput;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddKeystoreEntryOutputBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKey;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKeyInput;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKeyOutput;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKeyOutputBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificate;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificateInput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificateOutput;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificateOutputBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.Keystore;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveKeystoreEntry;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveKeystoreEntryInput;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveKeystoreEntryOutput;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveKeystoreEntryOutputBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemovePrivateKey;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemovePrivateKeyInput;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemovePrivateKeyOutput;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemovePrivateKeyOutputBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveTrustedCertificate;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveTrustedCertificateInput;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveTrustedCertificateOutput;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveTrustedCertificateOutputBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017._private.keys.PrivateKey;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017._private.keys.PrivateKeyKey;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.keystore.entry.KeyCredential;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.keystore.entry.KeyCredentialBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.keystore.entry.KeyCredentialKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.trusted.certificates.TrustedCertificate;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.trusted.certificates.TrustedCertificateKey;
61 import org.opendaylight.yangtools.concepts.Registration;
62 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
63 import org.opendaylight.yangtools.yang.binding.Rpc;
64 import org.opendaylight.yangtools.yang.common.RpcResult;
65 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
66 import org.osgi.service.component.annotations.Activate;
67 import org.osgi.service.component.annotations.Component;
68 import org.osgi.service.component.annotations.Deactivate;
69 import org.osgi.service.component.annotations.Reference;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72
73 @Singleton
74 @Component(service = { })
75 public final class NetconfSalKeystoreRpcs implements AutoCloseable {
76     private static final Logger LOG = LoggerFactory.getLogger(NetconfSalKeystoreRpcs.class);
77     private static final InstanceIdentifier<Keystore> KEYSTORE_IID = InstanceIdentifier.create(Keystore.class);
78
79     // FIXME: we are populating config datastore, but there may be risks with concurrent access. We really should be
80     //        using cluster singleton service here.
81     private final DataBroker dataBroker;
82     private final AAAEncryptionService encryptionService;
83     private final Registration reg;
84
85     @Inject
86     @Activate
87     public NetconfSalKeystoreRpcs(@Reference final DataBroker dataBroker,
88             @Reference final AAAEncryptionService encryptionService, @Reference final RpcProviderService rpcProvider) {
89         this.dataBroker = requireNonNull(dataBroker);
90         this.encryptionService = requireNonNull(encryptionService);
91
92         reg = rpcProvider.registerRpcImplementations(ImmutableClassToInstanceMap.<Rpc<?, ?>>builder()
93             .put(RemoveKeystoreEntry.class, this::removeKeystoreEntry)
94             .put(AddKeystoreEntry.class, this::addKeystoreEntry)
95             .put(AddTrustedCertificate.class, this::addTrustedCertificate)
96             .put(RemoveTrustedCertificate.class, this::removeTrustedCertificate)
97             .put(AddPrivateKey.class, this::addPrivateKey)
98             .put(RemovePrivateKey.class, this::removePrivateKey)
99             .build());
100         LOG.info("NETCONF keystore service started");
101     }
102
103     @PreDestroy
104     @Deactivate
105     @Override
106     public void close() {
107         reg.close();
108         LOG.info("NETCONF keystore service stopped");
109     }
110
111     private ListenableFuture<RpcResult<RemoveKeystoreEntryOutput>> removeKeystoreEntry(
112             final RemoveKeystoreEntryInput input) {
113         LOG.debug("Removing keypairs: {}", input);
114
115         final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
116
117         for (final String id : input.getKeyId()) {
118             writeTransaction.delete(LogicalDatastoreType.CONFIGURATION,
119                 KEYSTORE_IID.child(KeyCredential.class, new KeyCredentialKey(id)));
120         }
121
122         final SettableFuture<RpcResult<RemoveKeystoreEntryOutput>> rpcResult = SettableFuture.create();
123
124         writeTransaction.commit().addCallback(new FutureCallback<CommitInfo>() {
125             @Override
126             public void onSuccess(final CommitInfo result) {
127                 LOG.debug("remove-key-pair success. Input: {}", input);
128                 rpcResult.set(RpcResultBuilder.success(new RemoveKeystoreEntryOutputBuilder().build()).build());
129             }
130
131             @Override
132             public void onFailure(final Throwable throwable) {
133                 LOG.warn("remove-key-pair failed. Input: {}", input, throwable);
134                 rpcResult.setException(throwable);
135             }
136         }, MoreExecutors.directExecutor());
137
138         return rpcResult;
139     }
140
141     private ListenableFuture<RpcResult<AddKeystoreEntryOutput>> addKeystoreEntry(final AddKeystoreEntryInput input) {
142         LOG.debug("Adding keypairs: {}", input);
143
144         final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
145         final List<KeyCredential> keypairs = input.nonnullKeyCredential().values().stream()
146             .map(keypair -> new KeyCredentialBuilder(keypair)
147                 .setPrivateKey(encryptionService.encrypt(keypair.getPrivateKey()))
148                 .setPassphrase(encryptionService.encrypt(keypair.getPassphrase()))
149                 .build())
150             .collect(Collectors.toList());
151
152         for (KeyCredential keypair : keypairs) {
153             writeTransaction.merge(LogicalDatastoreType.CONFIGURATION,
154                 KEYSTORE_IID.child(KeyCredential.class, keypair.key()), keypair);
155         }
156
157         final SettableFuture<RpcResult<AddKeystoreEntryOutput>> rpcResult = SettableFuture.create();
158
159         writeTransaction.commit().addCallback(new FutureCallback<CommitInfo>() {
160             @Override
161             public void onSuccess(final CommitInfo result) {
162                 LOG.debug("add-key-pair success. Input: {}", input);
163                 rpcResult.set(RpcResultBuilder.success(new AddKeystoreEntryOutputBuilder().build()).build());
164             }
165
166             @Override
167             public void onFailure(final Throwable throwable) {
168                 LOG.warn("add-key-pair failed. Input: {}", input, throwable);
169                 rpcResult.setException(throwable);
170             }
171         }, MoreExecutors.directExecutor());
172
173         return rpcResult;
174     }
175
176     @VisibleForTesting
177     ListenableFuture<RpcResult<AddTrustedCertificateOutput>> addTrustedCertificate(
178             final AddTrustedCertificateInput input) {
179         final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
180
181         for (TrustedCertificate certificate : input.nonnullTrustedCertificate().values()) {
182             writeTransaction.merge(LogicalDatastoreType.CONFIGURATION,
183                 KEYSTORE_IID.child(TrustedCertificate.class, certificate.key()), certificate);
184         }
185
186         final SettableFuture<RpcResult<AddTrustedCertificateOutput>> rpcResult = SettableFuture.create();
187
188         writeTransaction.commit().addCallback(new FutureCallback<CommitInfo>() {
189             @Override
190             public void onSuccess(final CommitInfo result) {
191                 LOG.debug("add-trusted-certificate success. Input: {}", input);
192                 rpcResult.set(RpcResultBuilder.success(new AddTrustedCertificateOutputBuilder().build()).build());
193             }
194
195             @Override
196             public void onFailure(final Throwable throwable) {
197                 LOG.warn("add-trusted-certificate failed. Input: {}", input, throwable);
198                 rpcResult.setException(throwable);
199             }
200         }, MoreExecutors.directExecutor());
201
202         return rpcResult;
203     }
204
205     private ListenableFuture<RpcResult<RemoveTrustedCertificateOutput>> removeTrustedCertificate(
206             final RemoveTrustedCertificateInput input) {
207         final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
208
209         for (final String name : input.getName()) {
210             writeTransaction.delete(LogicalDatastoreType.CONFIGURATION,
211                 KEYSTORE_IID.child(TrustedCertificate.class, new TrustedCertificateKey(name)));
212         }
213
214         final SettableFuture<RpcResult<RemoveTrustedCertificateOutput>> rpcResult = SettableFuture.create();
215
216         writeTransaction.commit().addCallback(new FutureCallback<CommitInfo>() {
217             @Override
218             public void onSuccess(final CommitInfo result) {
219                 LOG.debug("remove-trusted-certificate success. Input: {}", input);
220                 rpcResult.set(RpcResultBuilder.success(new RemoveTrustedCertificateOutputBuilder().build()).build());
221             }
222
223             @Override
224             public void onFailure(final Throwable throwable) {
225                 LOG.warn("remove-trusted-certificate failed. Input: {}", input, throwable);
226                 rpcResult.setException(throwable);
227             }
228         }, MoreExecutors.directExecutor());
229
230         return rpcResult;
231     }
232
233     @VisibleForTesting
234     ListenableFuture<RpcResult<AddPrivateKeyOutput>> addPrivateKey(final AddPrivateKeyInput input) {
235         final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
236
237         for (PrivateKey key: input.nonnullPrivateKey().values()) {
238             writeTransaction.merge(LogicalDatastoreType.CONFIGURATION,
239                 KEYSTORE_IID.child(PrivateKey.class, key.key()), key);
240         }
241
242         final SettableFuture<RpcResult<AddPrivateKeyOutput>> rpcResult = SettableFuture.create();
243
244         writeTransaction.commit().addCallback(new FutureCallback<CommitInfo>() {
245             @Override
246             public void onSuccess(final CommitInfo result) {
247                 LOG.debug("add-private-key success. Input: {}", input);
248                 rpcResult.set(RpcResultBuilder.success(new AddPrivateKeyOutputBuilder().build()).build());
249             }
250
251             @Override
252             public void onFailure(final Throwable throwable) {
253                 LOG.warn("add-private-key failed. Input: {}", input, throwable);
254                 rpcResult.setException(throwable);
255             }
256         }, MoreExecutors.directExecutor());
257
258         return rpcResult;
259     }
260
261     private ListenableFuture<RpcResult<RemovePrivateKeyOutput>> removePrivateKey(final RemovePrivateKeyInput input) {
262         final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
263
264         for (final String name : input.getName()) {
265             writeTransaction.delete(LogicalDatastoreType.CONFIGURATION,
266                 KEYSTORE_IID.child(PrivateKey.class, new PrivateKeyKey(name)));
267         }
268
269         final SettableFuture<RpcResult<RemovePrivateKeyOutput>> rpcResult = SettableFuture.create();
270
271         writeTransaction.commit().addCallback(new FutureCallback<CommitInfo>() {
272             @Override
273             public void onSuccess(final CommitInfo result) {
274                 LOG.debug("remove-private-key success. Input: {}", input);
275                 rpcResult.set(RpcResultBuilder.success(new RemovePrivateKeyOutputBuilder().build()).build());
276             }
277
278             @Override
279             public void onFailure(final Throwable throwable) {
280                 LOG.warn("remove-private-key failed. Input: {}", input, throwable);
281                 rpcResult.setException(throwable);
282             }
283         }, MoreExecutors.directExecutor());
284
285         return rpcResult;
286     }
287 }