3dfe21b849fc493a974751c5d95f2b9889531f5d
[netconf.git] / netconf / callhome-provider / src / main / java / org / opendaylight / netconf / callhome / mount / tls / SslHandlerFactoryAdapter.java
1 /*
2  * Copyright (c) 2020 Pantheon Technologies, 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.callhome.mount.tls;
9
10 import static com.google.common.base.Preconditions.checkState;
11
12 import com.google.common.collect.ImmutableSet;
13 import io.netty.handler.ssl.SslHandler;
14 import java.util.Collection;
15 import java.util.Set;
16 import java.util.concurrent.ConcurrentHashMap;
17 import java.util.concurrent.ConcurrentMap;
18 import org.opendaylight.mdsal.binding.api.DataBroker;
19 import org.opendaylight.mdsal.binding.api.DataObjectModification;
20 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
21 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
22 import org.opendaylight.mdsal.binding.api.DataTreeModification;
23 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
24 import org.opendaylight.netconf.client.SslHandlerFactory;
25 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfKeystoreAdapter;
26 import org.opendaylight.netconf.sal.connect.util.SslHandlerFactoryImpl;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.NetconfCallhomeServer;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.AllowedDevices;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.allowed.devices.Device;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.allowed.devices.device.transport.Tls;
31 import org.opendaylight.yangtools.concepts.AbstractRegistration;
32 import org.opendaylight.yangtools.concepts.Registration;
33 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 public class SslHandlerFactoryAdapter extends AbstractRegistration implements SslHandlerFactory {
38     private static final DataTreeIdentifier<Device> ALLOWED_DEVICES =
39         DataTreeIdentifier.create(LogicalDatastoreType.CONFIGURATION,
40             InstanceIdentifier.builder(NetconfCallhomeServer.class).child(AllowedDevices.class).child(Device.class)
41                 .build());
42
43     private static final Logger LOG = LoggerFactory.getLogger(SslHandlerFactoryAdapter.class);
44
45     private final DeviceListener deviceListener = new DeviceListener();
46     private final NetconfKeystoreAdapter keystoreAdapter;
47     private final SslHandlerFactory sslHandlerFactory;
48     private final Registration deviceListenerReg;
49
50     public SslHandlerFactoryAdapter(final DataBroker dataBroker) {
51         this.keystoreAdapter = new NetconfKeystoreAdapter(dataBroker);
52         this.sslHandlerFactory = new SslHandlerFactoryImpl(keystoreAdapter);
53         this.deviceListenerReg = dataBroker.registerDataTreeChangeListener(ALLOWED_DEVICES, deviceListener);
54     }
55
56     @Override
57     public SslHandler createSslHandler() {
58         return createSslHandlerFilteredByKeys();
59     }
60
61     @Override
62     public SslHandler createSslHandler(final Set<String> allowedKeys) {
63         return createSslHandlerFilteredByKeys();
64     }
65
66     @Override
67     protected void removeRegistration() {
68         deviceListenerReg.close();
69     }
70
71     private SslHandler createSslHandlerFilteredByKeys() {
72         return sslHandlerFactory.createSslHandler(deviceListener.getAllowedKeys());
73     }
74
75     private static final class DeviceListener implements DataTreeChangeListener<Device> {
76         private final ConcurrentMap<String, String> allowedKeys = new ConcurrentHashMap<>();
77
78         @Override
79         public void onDataTreeChanged(final Collection<DataTreeModification<Device>> mods) {
80             for (final DataTreeModification<Device> dataTreeModification : mods) {
81                 final DataObjectModification<Device> deviceMod = dataTreeModification.getRootNode();
82                 final DataObjectModification.ModificationType modType = deviceMod.getModificationType();
83                 switch (modType) {
84                     case DELETE:
85                         deleteDevice(deviceMod.getDataBefore());
86                         break;
87                     case SUBTREE_MODIFIED:
88                     case WRITE:
89                         deleteDevice(deviceMod.getDataBefore());
90                         writeDevice(deviceMod.getDataAfter());
91                         break;
92                     default:
93                         throw new IllegalStateException("Unhandled modification type " + modType);
94                 }
95             }
96         }
97
98         Set<String> getAllowedKeys() {
99             final Set<String> ret = ImmutableSet.copyOf(allowedKeys.values());
100             checkState(!ret.isEmpty(), "No associated keys for TLS authentication were found");
101             return ret;
102         }
103
104         private void deleteDevice(final Device dataBefore) {
105             if (dataBefore != null && dataBefore.getTransport() instanceof Tls) {
106                 LOG.debug("Removing device {}", dataBefore.getUniqueId());
107                 allowedKeys.remove(dataBefore.getUniqueId());
108             }
109         }
110
111         private void writeDevice(final Device dataAfter) {
112             if (dataAfter != null && dataAfter.getTransport() instanceof Tls) {
113                 LOG.debug("Adding device {}", dataAfter.getUniqueId());
114                 final String tlsKeyId = ((Tls) dataAfter.getTransport()).getTlsClientParams().getKeyId();
115                 allowedKeys.putIfAbsent(dataAfter.getUniqueId(), tlsKeyId);
116             }
117         }
118     }
119 }