X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=netconf%2Fcallhome-provider%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fnetconf%2Fcallhome%2Fmount%2FCallHomeAuthProviderImpl.java;h=840dca1ffdee9baea59490135af263efb92a3541;hb=268bde77fa1196a742565202783d5071afa833bf;hp=485df12d8adf9360986b3fc476b6cbf01708be36;hpb=108a906fb42eac5ea506bc5fd0f1077b948e8c84;p=netconf.git diff --git a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeAuthProviderImpl.java b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeAuthProviderImpl.java index 485df12d8a..840dca1ffd 100644 --- a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeAuthProviderImpl.java +++ b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeAuthProviderImpl.java @@ -7,20 +7,23 @@ */ 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 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; -import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; -import org.opendaylight.controller.md.sal.binding.api.DataTreeModification; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import javax.annotation.Nonnull; +import org.opendaylight.mdsal.binding.api.DataBroker; +import org.opendaylight.mdsal.binding.api.DataObjectModification; +import org.opendaylight.mdsal.binding.api.DataObjectModification.ModificationType; +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.callhome.protocol.AuthorizedKeysDecoder; import org.opendaylight.netconf.callhome.protocol.CallHomeAuthorization; import org.opendaylight.netconf.callhome.protocol.CallHomeAuthorization.Builder; @@ -41,43 +44,66 @@ public class CallHomeAuthProviderImpl implements CallHomeAuthorizationProvider, private static final InstanceIdentifier GLOBAL_PATH = InstanceIdentifier.create(NetconfCallhomeServer.class).child(Global.class); private static final DataTreeIdentifier GLOBAL = - new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, GLOBAL_PATH); + DataTreeIdentifier.create(LogicalDatastoreType.CONFIGURATION, GLOBAL_PATH); private static final InstanceIdentifier ALLOWED_DEVICES_PATH = InstanceIdentifier.create(NetconfCallhomeServer.class).child(AllowedDevices.class).child(Device.class); private static final DataTreeIdentifier ALLOWED_DEVICES = - new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, ALLOWED_DEVICES_PATH); + DataTreeIdentifier.create(LogicalDatastoreType.CONFIGURATION, ALLOWED_DEVICES_PATH); + private static final DataTreeIdentifier ALLOWED_OP_DEVICES = + DataTreeIdentifier.create(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 configReg; private final ListenerRegistration deviceReg; + private final ListenerRegistration 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); @@ -86,12 +112,13 @@ public class CallHomeAuthProviderImpl implements CallHomeAuthorizationProvider, } @Override - public void close() throws Exception { + public void close() { 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,92 +126,134 @@ public class CallHomeAuthProviderImpl implements CallHomeAuthorizationProvider, return remoteAddress.toString(); } - - private class DeviceConfig implements DataTreeChangeListener { - - private ConcurrentMap byPublicKey = new ConcurrentHashMap(); + private abstract static class AbstractDeviceListener implements DataTreeChangeListener { @Override - public void onDataTreeChanged(Collection> arg0) { - for (DataTreeModification dataTreeModification : arg0) { - DataObjectModification rootNode = dataTreeModification.getRootNode(); - process(rootNode); + public final void onDataTreeChanged(final Collection> mods) { + for (DataTreeModification dataTreeModification : mods) { + final DataObjectModification deviceMod = dataTreeModification.getRootNode(); + final 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); + } } } - private void process(DataObjectModification deviceMod) { - Device before = deviceMod.getDataBefore(); - Device after = deviceMod.getDataAfter(); + private void deleteDevice(final Device dataBefore) { + if (dataBefore != null) { + final String publicKey = dataBefore.getSshHostKey(); + if (publicKey != null) { + LOG.debug("Removing device {}", dataBefore.getUniqueId()); + removeDevice(publicKey, dataBefore); + } else { + LOG.debug("Ignoring removal of device {}, no host key present", dataBefore.getUniqueId()); + } + } + } - if (before == null) { - putDevice(after); - } else if (after == null) { - // Delete - removeDevice(before); + private void writeDevice(final Device dataAfter) { + final String publicKey = dataAfter.getSshHostKey(); + if (publicKey != null) { + LOG.debug("Adding device {}", dataAfter.getUniqueId()); + addDevice(publicKey, dataAfter); } else { - if (!Objects.equal(before.getSshHostKey(), after.getSshHostKey())) { - // key changed // we should remove previous key. - removeDevice(before); - } - putDevice(after); + LOG.debug("Ignoring addition of device {}, no host key present", dataAfter.getUniqueId()); } } - private void putDevice(Device device) { - PublicKey key = publicKey(device); - if (key == null) { - return; + abstract void addDevice(String publicKey, Device device); + + abstract void removeDevice(String publicKey, Device device); + } + + private static class DeviceConfig extends AbstractDeviceListener { + private final ConcurrentMap byPublicKey = new ConcurrentHashMap<>(); + private final AuthorizedKeysDecoder keyDecoder = new AuthorizedKeysDecoder(); + + Device get(final PublicKey key) { + return byPublicKey.get(key); + } + + @Override + void addDevice(final String publicKey, final Device device) { + final PublicKey key = publicKey(publicKey, device); + if (key != null) { + byPublicKey.put(key, device); } - byPublicKey.put(key, device); } - private void removeDevice(Device device) { - PublicKey key = publicKey(device); - if (key == null) { - return; + @Override + void removeDevice(final String publicKey, final Device device) { + final PublicKey key = publicKey(publicKey, device); + if (key != null) { + byPublicKey.remove(key); } - byPublicKey.remove(key); } - private PublicKey publicKey(Device device) { - String hostKey = device.getSshHostKey(); + private PublicKey publicKey(final String hostKey, final Device device) { 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) { - return byPublicKey.get(key); + private static class DeviceOp extends AbstractDeviceListener { + private final ConcurrentMap byPublicKey = new ConcurrentHashMap<>(); + + Device get(final PublicKey serverKey) { + final String skey; + try { + skey = AuthorizedKeysDecoder.encodePublicKey(serverKey); + } catch (IOException | IllegalArgumentException e) { + LOG.error("Unable to encode server key: {}", serverKey, e); + return null; + } + + return byPublicKey.get(skey); } - } - private class GlobalConfig implements DataTreeChangeListener { + @Override + void removeDevice(final String publicKey, final Device device) { + byPublicKey.remove(publicKey); + } + @Override + void addDevice(final String publicKey, final Device device) { + byPublicKey.put(publicKey, device); + } + } + private static class GlobalConfig implements DataTreeChangeListener { private volatile Global current = null; @Override - public void onDataTreeChanged(Collection> arg0) { - for (DataTreeModification dataTreeModification : arg0) { + public void onDataTreeChanged(@Nonnull final Collection> mods) { + for (DataTreeModification dataTreeModification : mods) { current = dataTreeModification.getRootNode().getDataAfter(); } } boolean allowedUnknownKeys() { - if (current == null) { - return false; - } + final Global local = current; // Deal with null values. - return Boolean.TRUE.equals(current.isAcceptAllSshKeys()); + return local != null && Boolean.TRUE.equals(local.isAcceptAllSshKeys()); } Credentials getCredentials() { - return current != null ? current.getCredentials() : null; + final Global local = current; + return local != null ? local.getCredentials() : null; } - } - }