Adjust to yangtools-2.0.0/odlparent-3.0.0 changes
[netconf.git] / netconf / callhome-provider / src / main / java / org / opendaylight / netconf / callhome / mount / CallHomeAuthProviderImpl.java
index 485df12d8adf9360986b3fc476b6cbf01708be36..29632c247f1f4caf420b94009db530aab6b7d864 100644 (file)
@@ -9,12 +9,15 @@ package org.opendaylight.netconf.callhome.mount;
 
 import com.google.common.base.Objects;
 import com.google.common.net.InetAddresses;
+import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
+import java.security.GeneralSecurityException;
 import java.security.PublicKey;
 import java.util.Collection;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import javax.annotation.Nonnull;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
@@ -47,37 +50,60 @@ public class CallHomeAuthProviderImpl implements CallHomeAuthorizationProvider,
             InstanceIdentifier.create(NetconfCallhomeServer.class).child(AllowedDevices.class).child(Device.class);
     private static final DataTreeIdentifier<Device> ALLOWED_DEVICES =
             new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, ALLOWED_DEVICES_PATH);
+    private static final DataTreeIdentifier<Device> ALLOWED_OP_DEVICES =
+            new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, ALLOWED_DEVICES_PATH);
 
     private final GlobalConfig globalConfig = new GlobalConfig();
     private final DeviceConfig deviceConfig = new DeviceConfig();
+    private final DeviceOp deviceOp = new DeviceOp();
     private final ListenerRegistration<GlobalConfig> configReg;
     private final ListenerRegistration<DeviceConfig> deviceReg;
+    private final ListenerRegistration<DeviceOp> deviceOpReg;
 
-    public CallHomeAuthProviderImpl(DataBroker broker) {
+    private final CallhomeStatusReporter statusReporter;
+
+    CallHomeAuthProviderImpl(final DataBroker broker) {
         configReg = broker.registerDataTreeChangeListener(GLOBAL, globalConfig);
         deviceReg = broker.registerDataTreeChangeListener(ALLOWED_DEVICES, deviceConfig);
+        deviceOpReg = broker.registerDataTreeChangeListener(ALLOWED_OP_DEVICES, deviceOp);
+        statusReporter = new CallhomeStatusReporter(broker);
     }
 
+    @Nonnull
     @Override
-    public CallHomeAuthorization provideAuth(SocketAddress remoteAddress, PublicKey serverKey) {
+    public CallHomeAuthorization provideAuth(@Nonnull final SocketAddress remoteAddress,
+            @Nonnull final PublicKey serverKey) {
         Device deviceSpecific = deviceConfig.get(serverKey);
         String sessionName;
         Credentials deviceCred;
+
         if (deviceSpecific != null) {
             sessionName = deviceSpecific.getUniqueId();
             deviceCred = deviceSpecific.getCredentials();
-        } else if (globalConfig.allowedUnknownKeys()) {
-            sessionName = fromRemoteAddress(remoteAddress);
-            deviceCred = null;
         } else {
-            return CallHomeAuthorization.rejected();
+            String syntheticId = fromRemoteAddress(remoteAddress);
+            if (globalConfig.allowedUnknownKeys()) {
+                sessionName = syntheticId;
+                deviceCred = null;
+                statusReporter.asForceListedDevice(syntheticId, serverKey);
+            } else {
+                Device opDevice = deviceOp.get(serverKey);
+                if (opDevice == null) {
+                    statusReporter.asUnlistedDevice(syntheticId, serverKey);
+                } else {
+                    LOG.info("Repeating rejection of unlisted device with id of {}", opDevice.getUniqueId());
+                }
+                return CallHomeAuthorization.rejected();
+            }
         }
+
         final Credentials credentials = deviceCred != null ? deviceCred : globalConfig.getCredentials();
 
         if (credentials == null) {
             LOG.info("No credentials found for {}, rejecting.", remoteAddress);
             return CallHomeAuthorization.rejected();
         }
+
         Builder authBuilder = CallHomeAuthorization.serverAccepted(sessionName, credentials.getUsername());
         for (String password : credentials.getPasswords()) {
             authBuilder.addPassword(password);
@@ -89,9 +115,10 @@ public class CallHomeAuthProviderImpl implements CallHomeAuthorizationProvider,
     public void close() throws Exception {
         configReg.close();
         deviceReg.close();
+        deviceOpReg.close();
     }
 
-    private String fromRemoteAddress(SocketAddress remoteAddress) {
+    private static String fromRemoteAddress(final SocketAddress remoteAddress) {
         if (remoteAddress instanceof InetSocketAddress) {
             InetSocketAddress socketAddress = (InetSocketAddress) remoteAddress;
             return InetAddresses.toAddrString(socketAddress.getAddress()) + ":" + socketAddress.getPort();
@@ -99,20 +126,21 @@ public class CallHomeAuthProviderImpl implements CallHomeAuthorizationProvider,
         return remoteAddress.toString();
     }
 
-
     private class DeviceConfig implements DataTreeChangeListener<Device> {
 
-        private ConcurrentMap<PublicKey, Device> byPublicKey = new ConcurrentHashMap<PublicKey, Device>();
+        private final AuthorizedKeysDecoder keyDecoder = new AuthorizedKeysDecoder();
+
+        private final ConcurrentMap<PublicKey, Device> byPublicKey = new ConcurrentHashMap<>();
 
         @Override
-        public void onDataTreeChanged(Collection<DataTreeModification<Device>> arg0) {
-            for (DataTreeModification<Device> dataTreeModification : arg0) {
+        public void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<Device>> mods) {
+            for (DataTreeModification<Device> dataTreeModification : mods) {
                 DataObjectModification<Device> rootNode = dataTreeModification.getRootNode();
                 process(rootNode);
             }
         }
 
-        private void process(DataObjectModification<Device> deviceMod) {
+        private void process(final DataObjectModification<Device> deviceMod) {
             Device before = deviceMod.getDataBefore();
             Device after = deviceMod.getDataAfter();
 
@@ -130,7 +158,7 @@ public class CallHomeAuthProviderImpl implements CallHomeAuthorizationProvider,
             }
         }
 
-        private void putDevice(Device device) {
+        private void putDevice(final Device device) {
             PublicKey key = publicKey(device);
             if (key == null) {
                 return;
@@ -138,7 +166,7 @@ public class CallHomeAuthProviderImpl implements CallHomeAuthorizationProvider,
             byPublicKey.put(key, device);
         }
 
-        private void removeDevice(Device device) {
+        private void removeDevice(final Device device) {
             PublicKey key = publicKey(device);
             if (key == null) {
                 return;
@@ -146,29 +174,81 @@ public class CallHomeAuthProviderImpl implements CallHomeAuthorizationProvider,
             byPublicKey.remove(key);
         }
 
-        private PublicKey publicKey(Device device) {
+        private PublicKey publicKey(final Device device) {
             String hostKey = device.getSshHostKey();
             try {
-                return new AuthorizedKeysDecoder().decodePublicKey(hostKey);
-            } catch (Exception e) {
-                LOG.error("Unable to decode SSH key for {}. Ignoring update for this device",device.getUniqueId(),e);
+                return keyDecoder.decodePublicKey(hostKey);
+            } catch (GeneralSecurityException e) {
+                LOG.error("Unable to decode SSH key for {}. Ignoring update for this device", device.getUniqueId(), e);
                 return null;
             }
         }
 
-        private Device get(PublicKey key) {
+        private Device get(final PublicKey key) {
             return byPublicKey.get(key);
         }
     }
 
-    private class GlobalConfig implements DataTreeChangeListener<Global> {
+    private class DeviceOp implements DataTreeChangeListener<Device> {
+
+        private final ConcurrentMap<String, Device> byPublicKey = new ConcurrentHashMap<>();
+
+        @Override
+        public void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<Device>> mods) {
+            for (DataTreeModification<Device> dataTreeModification : mods) {
+                DataObjectModification<Device> rootNode = dataTreeModification.getRootNode();
+                process(rootNode);
+            }
+        }
 
+        private void process(final DataObjectModification<Device> deviceMod) {
+            Device before = deviceMod.getDataBefore();
+            Device after = deviceMod.getDataAfter();
+
+            if (before == null) {
+                putDevice(after);
+            } else if (after == null) {
+                // Delete
+                removeDevice(before);
+            } else {
+                if (!Objects.equal(before.getSshHostKey(), after.getSshHostKey())) {
+                    // key changed // we should remove previous key.
+                    removeDevice(before);
+                }
+                putDevice(after);
+            }
+        }
+
+        private void putDevice(final Device device) {
+            String key = device.getSshHostKey();
+            byPublicKey.put(key, device);
+        }
+
+        private void removeDevice(final Device device) {
+            String key = device.getSshHostKey();
+            byPublicKey.remove(key);
+        }
+
+        Device get(final PublicKey serverKey) {
+            String skey = "";
+
+            try {
+                skey = AuthorizedKeysDecoder.encodePublicKey(serverKey);
+                return byPublicKey.get(skey);
+            } catch (IOException | IllegalArgumentException e) {
+                LOG.error("Unable to encode server key: {}", skey, e);
+                return null;
+            }
+        }
+    }
+
+    private class GlobalConfig implements DataTreeChangeListener<Global> {
 
         private volatile Global current = null;
 
         @Override
-        public void onDataTreeChanged(Collection<DataTreeModification<Global>> arg0) {
-            for (DataTreeModification<Global> dataTreeModification : arg0) {
+        public void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<Global>> mods) {
+            for (DataTreeModification<Global> dataTreeModification : mods) {
                 current = dataTreeModification.getRootNode().getDataAfter();
             }
         }
@@ -184,7 +264,5 @@ public class CallHomeAuthProviderImpl implements CallHomeAuthorizationProvider,
         Credentials getCredentials() {
             return current != null ? current.getCredentials() : null;
         }
-
     }
-
 }