Add Call-Home implementation for the TLS secure-transport
[netconf.git] / netconf / callhome-provider / src / main / java / org / opendaylight / netconf / callhome / mount / tls / SslHandlerFactoryAdapter.java
diff --git a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/tls/SslHandlerFactoryAdapter.java b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/tls/SslHandlerFactoryAdapter.java
new file mode 100644 (file)
index 0000000..3dfe21b
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2020 Pantheon Technologies, 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.callhome.mount.tls;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.collect.ImmutableSet;
+import io.netty.handler.ssl.SslHandler;
+import java.util.Collection;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+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.DataTreeIdentifier;
+import org.opendaylight.mdsal.binding.api.DataTreeModification;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.netconf.client.SslHandlerFactory;
+import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfKeystoreAdapter;
+import org.opendaylight.netconf.sal.connect.util.SslHandlerFactoryImpl;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.NetconfCallhomeServer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.AllowedDevices;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.allowed.devices.Device;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.allowed.devices.device.transport.Tls;
+import org.opendaylight.yangtools.concepts.AbstractRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SslHandlerFactoryAdapter extends AbstractRegistration implements SslHandlerFactory {
+    private static final DataTreeIdentifier<Device> ALLOWED_DEVICES =
+        DataTreeIdentifier.create(LogicalDatastoreType.CONFIGURATION,
+            InstanceIdentifier.builder(NetconfCallhomeServer.class).child(AllowedDevices.class).child(Device.class)
+                .build());
+
+    private static final Logger LOG = LoggerFactory.getLogger(SslHandlerFactoryAdapter.class);
+
+    private final DeviceListener deviceListener = new DeviceListener();
+    private final NetconfKeystoreAdapter keystoreAdapter;
+    private final SslHandlerFactory sslHandlerFactory;
+    private final Registration deviceListenerReg;
+
+    public SslHandlerFactoryAdapter(final DataBroker dataBroker) {
+        this.keystoreAdapter = new NetconfKeystoreAdapter(dataBroker);
+        this.sslHandlerFactory = new SslHandlerFactoryImpl(keystoreAdapter);
+        this.deviceListenerReg = dataBroker.registerDataTreeChangeListener(ALLOWED_DEVICES, deviceListener);
+    }
+
+    @Override
+    public SslHandler createSslHandler() {
+        return createSslHandlerFilteredByKeys();
+    }
+
+    @Override
+    public SslHandler createSslHandler(final Set<String> allowedKeys) {
+        return createSslHandlerFilteredByKeys();
+    }
+
+    @Override
+    protected void removeRegistration() {
+        deviceListenerReg.close();
+    }
+
+    private SslHandler createSslHandlerFilteredByKeys() {
+        return sslHandlerFactory.createSslHandler(deviceListener.getAllowedKeys());
+    }
+
+    private static final class DeviceListener implements DataTreeChangeListener<Device> {
+        private final ConcurrentMap<String, String> allowedKeys = new ConcurrentHashMap<>();
+
+        @Override
+        public void onDataTreeChanged(final Collection<DataTreeModification<Device>> mods) {
+            for (final DataTreeModification<Device> dataTreeModification : mods) {
+                final DataObjectModification<Device> deviceMod = dataTreeModification.getRootNode();
+                final DataObjectModification.ModificationType modType = deviceMod.getModificationType();
+                switch (modType) {
+                    case DELETE:
+                        deleteDevice(deviceMod.getDataBefore());
+                        break;
+                    case SUBTREE_MODIFIED:
+                    case WRITE:
+                        deleteDevice(deviceMod.getDataBefore());
+                        writeDevice(deviceMod.getDataAfter());
+                        break;
+                    default:
+                        throw new IllegalStateException("Unhandled modification type " + modType);
+                }
+            }
+        }
+
+        Set<String> getAllowedKeys() {
+            final Set<String> ret = ImmutableSet.copyOf(allowedKeys.values());
+            checkState(!ret.isEmpty(), "No associated keys for TLS authentication were found");
+            return ret;
+        }
+
+        private void deleteDevice(final Device dataBefore) {
+            if (dataBefore != null && dataBefore.getTransport() instanceof Tls) {
+                LOG.debug("Removing device {}", dataBefore.getUniqueId());
+                allowedKeys.remove(dataBefore.getUniqueId());
+            }
+        }
+
+        private void writeDevice(final Device dataAfter) {
+            if (dataAfter != null && dataAfter.getTransport() instanceof Tls) {
+                LOG.debug("Adding device {}", dataAfter.getUniqueId());
+                final String tlsKeyId = ((Tls) dataAfter.getTransport()).getTlsClientParams().getKeyId();
+                allowedKeys.putIfAbsent(dataAfter.getUniqueId(), tlsKeyId);
+            }
+        }
+    }
+}
\ No newline at end of file