Migrate aaa-encrypt-service to OSGi DS 90/94290/20
authorRobert Varga <robert.varga@pantheon.tech>
Tue, 14 Feb 2023 11:28:24 +0000 (12:28 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 14 Feb 2023 18:29:10 +0000 (19:29 +0100)
This service is using clustered-app-config, hence the migration
is a bit more involved, but this gets rid of another blueprint.

The way this operates is it instantiates listens for datastore changes,
updates the configuration if needed, and then uses a ComponentFactory
to instantiate the service.

One notable change here is that we no longer provide an .xml which the
user can edit -- hence the datastore is only populated if it is empty
and we generate a new encryption key for every new deployment.

JIRA: AAA-204
Change-Id: I83a8f8fea8e272dc4b9c801be6799a15aa71f5ec
Signed-off-by: Tomas Cere <tomas.cere@pantheon.tech>
Signed-off-by: Peter Suna <peter.suna@pantheon.tech>
Signed-off-by: OleksandrZharov <Oleksandr.Zharov@pantheon.tech>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
16 files changed:
aaa-encrypt-service/impl/pom.xml
aaa-encrypt-service/impl/src/main/java/org/opendaylight/aaa/encrypt/impl/AAAEncryptionServiceConfigurator.java [deleted file]
aaa-encrypt-service/impl/src/main/java/org/opendaylight/aaa/encrypt/impl/AAAEncryptionServiceImpl.java
aaa-encrypt-service/impl/src/main/java/org/opendaylight/aaa/encrypt/impl/EncryptServiceConfigImpl.java [new file with mode: 0644]
aaa-encrypt-service/impl/src/main/java/org/opendaylight/aaa/encrypt/impl/EncryptServiceConfigSupplier.java [deleted file]
aaa-encrypt-service/impl/src/main/java/org/opendaylight/aaa/encrypt/impl/OSGiEncryptionServiceConfigurator.java [new file with mode: 0644]
aaa-encrypt-service/impl/src/main/resources/OSGI-INF/blueprint/encryptservice.xml [deleted file]
aaa-encrypt-service/impl/src/main/resources/initial/aaa-encrypt-service-config.xml [deleted file]
aaa-encrypt-service/impl/src/main/yang/aaa-encrypt-service-config.yang
aaa-encrypt-service/impl/src/test/java/org/opendaylight/aaa/encrypt/impl/AAAEncryptServiceImplTest.java
aaa-encrypt-service/impl/src/test/java/org/opendaylight/aaa/encrypt/impl/OSGiEncryptionServiceConfiguratorTest.java [new file with mode: 0644]
artifacts/pom.xml
dependency-check/pom.xml
features/odl-aaa-cert/src/main/feature/feature.xml
features/odl-aaa-encryption-service/pom.xml
features/odl-aaa-encryption-service/src/main/feature/feature.xml

index cba8ce2656cf7399c98de15e7016f572b1d1146c..3607cdd829308308e97c30dd57709e572a821e15 100644 (file)
@@ -29,6 +29,10 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-lang3</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.odlparent</groupId>
+      <artifactId>logging-markers</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.opendaylight.yangtools</groupId>
       <artifactId>yang-common</artifactId>
@@ -45,6 +49,14 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
       <groupId>org.opendaylight.aaa</groupId>
       <artifactId>aaa-encrypt-service</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.framework</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.service.component</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.osgi</groupId>
       <artifactId>org.osgi.service.component.annotations</artifactId>
@@ -56,46 +68,4 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
       <scope>test</scope>
     </dependency>
   </dependencies>
-
-  <!-- FIXME: AAA-204: remove all this once we do not have Blueprint -->
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.codehaus.mojo</groupId>
-        <artifactId>build-helper-maven-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>attach-artifacts</id>
-            <goals>
-              <goal>attach-artifact</goal>
-            </goals>
-            <phase>package</phase>
-            <configuration>
-              <artifacts>
-                <artifact>
-                  <file>${project.build.directory}/classes/initial/aaa-encrypt-service-config.xml</file>
-                  <type>xml</type>
-                  <classifier>config</classifier>
-                </artifact>
-              </artifacts>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <configuration>
-          <instructions>
-            <!-- Mixed OSGi DS/Blueprint services, we have to override to get Blueprint bits here -->
-            <Provide-Capability>
-                osgi.service;objectClass:List&lt;String&gt;="org.opendaylight.aaa.encrypt.AAAEncryptionService";uses:="org.opendaylight.aaa.encrypt",
-                osgi.service;objectClass:List&lt;String&gt;="org.opendaylight.aaa.encrypt.impl.EncryptServiceConfigSupplier";uses:="org.opendaylight.aaa.encrypt.impl"
-            </Provide-Capability>
-          </instructions>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
 </project>
diff --git a/aaa-encrypt-service/impl/src/main/java/org/opendaylight/aaa/encrypt/impl/AAAEncryptionServiceConfigurator.java b/aaa-encrypt-service/impl/src/main/java/org/opendaylight/aaa/encrypt/impl/AAAEncryptionServiceConfigurator.java
deleted file mode 100644 (file)
index 1f6458e..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (c) 2023 PANTHEON.tech, 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.aaa.encrypt.impl;
-
-import com.google.common.base.Strings;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.MoreExecutors;
-import java.io.File;
-import java.io.IOException;
-import java.security.SecureRandom;
-import java.util.Base64;
-import java.util.concurrent.ExecutionException;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-import org.apache.commons.lang3.RandomStringUtils;
-import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.binding.api.DataBroker;
-import org.opendaylight.mdsal.common.api.CommitInfo;
-import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.yang.gen.v1.config.aaa.authn.encrypt.service.config.rev160915.AaaEncryptServiceConfig;
-import org.opendaylight.yang.gen.v1.config.aaa.authn.encrypt.service.config.rev160915.AaaEncryptServiceConfigBuilder;
-import org.opendaylight.yang.gen.v1.config.aaa.authn.encrypt.service.config.rev160915.EncryptServiceConfig;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-import org.xml.sax.SAXException;
-
-/**
- * Intermediate component dealing with establishing initial configuration for {@link AAAEncryptionServiceImpl}. In
- * particular it deals with generating and persisting of encryption salt and encryption password.
- */
-public final class AAAEncryptionServiceConfigurator implements EncryptServiceConfigSupplier, EncryptServiceConfig {
-    private static final Logger LOG = LoggerFactory.getLogger(AAAEncryptionServiceConfigurator.class);
-
-    // Note: this is a strong binding to Blueprint, which is loading etc/opendaylight/datastore/initial/config
-    private static final String DEFAULT_CONFIG_FILE_PATH = "etc" + File.separator + "opendaylight" + File.separator
-        + "datastore" + File.separator + "initial" + File.separator + "config" + File.separator
-        + "aaa-encrypt-service-config.xml";
-    private static final SecureRandom RANDOM = new SecureRandom();
-
-    private final AaaEncryptServiceConfig delegate;
-
-    public AAAEncryptionServiceConfigurator(final DataBroker dataBroker,
-            final AaaEncryptServiceConfig blueprintConfig) {
-        if (Strings.isNullOrEmpty(blueprintConfig.getEncryptSalt())
-            || Strings.isNullOrEmpty(blueprintConfig.getEncryptKey())) {
-            final var generatedConfig = generateConfig(blueprintConfig);
-
-            // Update initial configuration and config datastore
-            updateEncrySrvConfig(generatedConfig.requireEncryptKey(), generatedConfig.requireEncryptSalt());
-            updateDatastore(dataBroker, generatedConfig);
-
-            delegate = generatedConfig;
-        } else {
-            delegate = blueprintConfig;
-        }
-    }
-
-    private static @NonNull AaaEncryptServiceConfig generateConfig(final AaaEncryptServiceConfig blueprintConfig) {
-        LOG.debug("Set the Encryption service password and encrypt salt");
-        final var salt = new byte[16];
-        RANDOM.nextBytes(salt);
-
-        return new AaaEncryptServiceConfigBuilder(blueprintConfig)
-            .setEncryptKey(RandomStringUtils.random(blueprintConfig.requirePasswordLength(), true, true))
-            .setEncryptSalt(Base64.getEncoder().encodeToString(salt))
-            .build();
-    }
-
-    // FIXME: Update configuration datastore, but only if not present?! this looks weird lifecycle-wise
-    private static void updateDatastore(final DataBroker dataBroker, final @NonNull AaaEncryptServiceConfig config) {
-        final var iid = InstanceIdentifier.create(AaaEncryptServiceConfig.class);
-
-        final var tx = dataBroker.newReadWriteTransaction();
-        final var existsFuture = tx.exists(LogicalDatastoreType.CONFIGURATION, iid);
-
-        final boolean exists;
-        try {
-            exists = existsFuture.get();
-        } catch (InterruptedException | ExecutionException e) {
-            tx.cancel();
-            LOG.error("Failed to read configuration", e);
-            return;
-        }
-        if (exists) {
-            tx.cancel();
-            LOG.info("Configuration already present, skipping update");
-            return;
-        }
-
-        LOG.debug("Populating configuration: {}, {}", iid, config);
-        tx.put(LogicalDatastoreType.CONFIGURATION, iid, config);
-        // Perform the transaction.submit asynchronously
-        Futures.addCallback(tx.commit(), new FutureCallback<CommitInfo>() {
-            @Override
-            public void onFailure(final Throwable throwable) {
-                LOG.error("initDatastore: configuration data populated: {}", iid);
-            }
-
-            @Override
-            public void onSuccess(final CommitInfo result) {
-                LOG.info("initDatastore: transaction succeeded");
-            }
-        }, MoreExecutors.directExecutor());
-    }
-
-    private static void updateEncrySrvConfig(final String newPwd, final String newSalt) {
-        LOG.debug("Update encryption service config file");
-        try {
-            final File configFile = new File(DEFAULT_CONFIG_FILE_PATH);
-            if (configFile.exists()) {
-                final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(configFile);
-                final Node keyNode = doc.getElementsByTagName("encrypt-key").item(0);
-                keyNode.setTextContent(newPwd);
-                final Node salt = doc.getElementsByTagName("encrypt-salt").item(0);
-                salt.setTextContent(newSalt);
-                TransformerFactory.newInstance().newTransformer().transform(new DOMSource(doc),
-                    new StreamResult(new File(DEFAULT_CONFIG_FILE_PATH)));
-            } else {
-                LOG.warn("The encryption service config file does not exist {}", DEFAULT_CONFIG_FILE_PATH);
-            }
-        } catch (ParserConfigurationException | TransformerException | SAXException | IOException e) {
-            LOG.error("Error while updating the encryption service config file", e);
-        }
-    }
-
-    @Override
-    public EncryptServiceConfig get() {
-        return this;
-    }
-
-    @Override
-    public Class<? extends EncryptServiceConfig> implementedInterface() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public String getEncryptKey() {
-        return delegate.requireEncryptKey();
-    }
-
-    @Override
-    public byte[] getEncryptSalt() {
-        return Base64.getDecoder().decode(delegate.requireEncryptSalt());
-    }
-
-    @Override
-    public String getEncryptMethod() {
-        return delegate.getEncryptMethod();
-    }
-
-    @Override
-    public String getEncryptType() {
-        return delegate.getEncryptType();
-    }
-
-    @Override
-    public Integer getEncryptIterationCount() {
-        return delegate.getEncryptIterationCount();
-    }
-
-    @Override
-    public Integer getEncryptKeyLength() {
-        return delegate.getEncryptKeyLength();
-    }
-
-    @Override
-    public String getCipherTransforms() {
-        return delegate.getCipherTransforms();
-    }
-}
index 41178bde1d7375ce9a0ffa3f1a9fdded26d0dfd5..5789dccbad93c9fd0432df21ec9b575d1203076f 100644 (file)
@@ -7,6 +7,9 @@
  */
 package org.opendaylight.aaa.encrypt.impl;
 
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
 import java.nio.charset.Charset;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
@@ -14,6 +17,7 @@ import java.security.NoSuchAlgorithmException;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.KeySpec;
 import java.util.Base64;
+import java.util.Map;
 import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
 import javax.crypto.IllegalBlockSizeException;
@@ -24,10 +28,10 @@ import javax.crypto.spec.IvParameterSpec;
 import javax.crypto.spec.PBEKeySpec;
 import javax.crypto.spec.SecretKeySpec;
 import org.opendaylight.aaa.encrypt.AAAEncryptionService;
+import org.opendaylight.yang.gen.v1.config.aaa.authn.encrypt.service.config.rev160915.EncryptServiceConfig;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Deactivate;
-import org.osgi.service.component.annotations.Reference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -37,26 +41,26 @@ import org.slf4j.LoggerFactory;
  * @author - Sharon Aicler (saichler@gmail.com)
  */
 @Deprecated
-@Component(immediate = true)
+@Component(factory = AAAEncryptionServiceImpl.FACTORY_NAME)
 public final class AAAEncryptionServiceImpl implements AAAEncryptionService {
+    static final String FACTORY_NAME = "org.opendaylight.aaa.encrypt.impl.AAAEncryptionServiceImpl";
+
     private static final Logger LOG = LoggerFactory.getLogger(AAAEncryptionServiceImpl.class);
+    private static final String CONFIG_PROP = ".config";
 
     private final SecretKey key;
     private final Cipher encryptCipher;
     private final Cipher decryptCipher;
 
-    @Activate
-    public AAAEncryptionServiceImpl(@Reference final EncryptServiceConfigSupplier configProvider) {
-        final var encrySrvConfig = configProvider.get();
-
-        final byte[] encryptionKeySalt = encrySrvConfig.requireEncryptSalt();
+    public AAAEncryptionServiceImpl(final EncryptServiceConfig configuration) {
+        final byte[] encryptionKeySalt = configuration.requireEncryptSalt();
         IvParameterSpec tempIvSpec = null;
         SecretKey tempKey = null;
         try {
-            final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encrySrvConfig.getEncryptMethod());
-            final KeySpec spec = new PBEKeySpec(encrySrvConfig.requireEncryptKey().toCharArray(), encryptionKeySalt,
-                    encrySrvConfig.getEncryptIterationCount(), encrySrvConfig.getEncryptKeyLength());
-            tempKey = new SecretKeySpec(keyFactory.generateSecret(spec).getEncoded(), encrySrvConfig.getEncryptType());
+            final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(configuration.getEncryptMethod());
+            final KeySpec spec = new PBEKeySpec(configuration.requireEncryptKey().toCharArray(), encryptionKeySalt,
+                    configuration.getEncryptIterationCount(), configuration.getEncryptKeyLength());
+            tempKey = new SecretKeySpec(keyFactory.generateSecret(spec).getEncoded(), configuration.getEncryptType());
             tempIvSpec = new IvParameterSpec(encryptionKeySalt);
         } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
             LOG.error("Failed to initialize secret key", e);
@@ -65,7 +69,7 @@ public final class AAAEncryptionServiceImpl implements AAAEncryptionService {
         final var ivSpec = tempIvSpec;
         Cipher cipher = null;
         try {
-            cipher = Cipher.getInstance(encrySrvConfig.getCipherTransforms());
+            cipher = Cipher.getInstance(configuration.getCipherTransforms());
             cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
         } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException
                 | InvalidKeyException e) {
@@ -74,7 +78,7 @@ public final class AAAEncryptionServiceImpl implements AAAEncryptionService {
         encryptCipher = cipher;
         cipher = null;
         try {
-            cipher = Cipher.getInstance(encrySrvConfig.getCipherTransforms());
+            cipher = Cipher.getInstance(configuration.getCipherTransforms());
             cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
         } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException
                 | InvalidKeyException e) {
@@ -84,6 +88,15 @@ public final class AAAEncryptionServiceImpl implements AAAEncryptionService {
         LOG.info("AAAEncryptionService activated");
     }
 
+    @Activate
+    public AAAEncryptionServiceImpl(final Map<String, ?> properties) {
+        this((EncryptServiceConfig) verifyNotNull(properties.get(CONFIG_PROP)));
+    }
+
+    static Map<String, ?> props(final EncryptServiceConfig configuration) {
+        return Map.of(CONFIG_PROP, requireNonNull(configuration));
+    }
+
     @Deactivate
     void deactivate() {
         LOG.info("AAAEncryptionService deactivated");
diff --git a/aaa-encrypt-service/impl/src/main/java/org/opendaylight/aaa/encrypt/impl/EncryptServiceConfigImpl.java b/aaa-encrypt-service/impl/src/main/java/org/opendaylight/aaa/encrypt/impl/EncryptServiceConfigImpl.java
new file mode 100644 (file)
index 0000000..03b1eae
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2023 PANTHEON.tech, 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.aaa.encrypt.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Base64;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yang.gen.v1.config.aaa.authn.encrypt.service.config.rev160915.AaaEncryptServiceConfig;
+import org.opendaylight.yang.gen.v1.config.aaa.authn.encrypt.service.config.rev160915.EncryptServiceConfig;
+
+record EncryptServiceConfigImpl(@NonNull AaaEncryptServiceConfig delegate) implements EncryptServiceConfig {
+    EncryptServiceConfigImpl {
+        requireNonNull(delegate);
+    }
+
+    @Override
+    public Class<? extends EncryptServiceConfig> implementedInterface() {
+        // This implementation is not generated and used only internally, hence this method is never called
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getEncryptKey() {
+        return delegate.requireEncryptKey();
+    }
+
+    @Override
+    public byte[] getEncryptSalt() {
+        return Base64.getDecoder().decode(delegate.requireEncryptSalt());
+    }
+
+    @Override
+    public String getEncryptMethod() {
+        return delegate.getEncryptMethod();
+    }
+
+    @Override
+    public String getEncryptType() {
+        return delegate.getEncryptType();
+    }
+
+    @Override
+    public Integer getEncryptIterationCount() {
+        return delegate.getEncryptIterationCount();
+    }
+
+    @Override
+    public Integer getEncryptKeyLength() {
+        return delegate.getEncryptKeyLength();
+    }
+
+    @Override
+    public String getCipherTransforms() {
+        return delegate.getCipherTransforms();
+    }
+}
diff --git a/aaa-encrypt-service/impl/src/main/java/org/opendaylight/aaa/encrypt/impl/EncryptServiceConfigSupplier.java b/aaa-encrypt-service/impl/src/main/java/org/opendaylight/aaa/encrypt/impl/EncryptServiceConfigSupplier.java
deleted file mode 100644 (file)
index 7a7eddf..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (c) 2023 PANTHEON.tech, 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.aaa.encrypt.impl;
-
-import java.util.function.Supplier;
-import org.opendaylight.yang.gen.v1.config.aaa.authn.encrypt.service.config.rev160915.EncryptServiceConfig;
-
-/**
- * Blueprint compatibility hack. Direct injection would result in
- * <pre>
- *   Caused by: java.lang.IncompatibleClassChangeError: class Proxy601f54db_5975_4863_a33b_758ee248518f cannot implement
- *              sealed interface org.opendaylight.yangtools.yang.binding.DataContainer
- * </pre>
- * and hence we wrap it with a DataObject, which is not sealed.
- */
-@Deprecated
-public interface EncryptServiceConfigSupplier extends Supplier<EncryptServiceConfig> {
-
-}
diff --git a/aaa-encrypt-service/impl/src/main/java/org/opendaylight/aaa/encrypt/impl/OSGiEncryptionServiceConfigurator.java b/aaa-encrypt-service/impl/src/main/java/org/opendaylight/aaa/encrypt/impl/OSGiEncryptionServiceConfigurator.java
new file mode 100644 (file)
index 0000000..89d37d0
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2023 PANTHEON.tech, 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.aaa.encrypt.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.security.SecureRandom;
+import java.util.Base64;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.checkerframework.checker.lock.qual.GuardedBy;
+import org.checkerframework.checker.lock.qual.Holding;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+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.CommitInfo;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.odlparent.logging.markers.Markers;
+import org.opendaylight.yang.gen.v1.config.aaa.authn.encrypt.service.config.rev160915.AaaEncryptServiceConfig;
+import org.opendaylight.yang.gen.v1.config.aaa.authn.encrypt.service.config.rev160915.AaaEncryptServiceConfigBuilder;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.service.component.ComponentFactory;
+import org.osgi.service.component.ComponentInstance;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Intermediate component dealing with establishing initial configuration for {@link AAAEncryptionServiceImpl}. In
+ * particular it deals with generating and persisting of encryption salt and encryption password.
+ *
+ * <p>
+ * We primarily listen to the configuration being present. Whenever the salt is missing or the password does not match
+ * the required length, we generate them and persist them. This mode of operation means we potentially have a loop, i.e.
+ * our touching the datastore will trigger again {@link #onDataTreeChanged(Collection)}, which will re-evaluate the
+ * conditions and we try again.
+ */
+@Component(service = { })
+public final class OSGiEncryptionServiceConfigurator implements DataTreeChangeListener<AaaEncryptServiceConfig> {
+    private static final Logger LOG = LoggerFactory.getLogger(OSGiEncryptionServiceConfigurator.class);
+    private static final SecureRandom RANDOM = new SecureRandom();
+    private static final @NonNull AaaEncryptServiceConfig DEFAULT_CONFIG = new AaaEncryptServiceConfigBuilder()
+        // Note: mirrors defaults from YANG file
+        .setEncryptMethod("PBKDF2WithHmacSHA1")
+        .setEncryptType("AES")
+        .setEncryptIterationCount(32768)
+        .setEncryptKeyLength(128)
+        .setCipherTransforms("AES/CBC/PKCS5Padding")
+        .setPasswordLength(12)
+        .build();
+
+    private final ComponentFactory<AAAEncryptionServiceImpl> factory;
+    private final DataBroker dataBroker;
+
+    @GuardedBy("this")
+    private Registration reg;
+    @GuardedBy("this")
+    private ComponentInstance<AAAEncryptionServiceImpl> instance;
+    @GuardedBy("this")
+    private AaaEncryptServiceConfig current;
+
+    @Activate
+    public OSGiEncryptionServiceConfigurator(@Reference final DataBroker dataBroker,
+            @Reference(target = "(component.factory=" + AAAEncryptionServiceImpl.FACTORY_NAME + ")")
+            final ComponentFactory<AAAEncryptionServiceImpl> factory) {
+        this.dataBroker = requireNonNull(dataBroker);
+        this.factory = requireNonNull(factory);
+        reg = dataBroker.registerDataTreeChangeListener(
+            DataTreeIdentifier.create(LogicalDatastoreType.CONFIGURATION,
+                InstanceIdentifier.create(AaaEncryptServiceConfig.class)),
+            this);
+        LOG.debug("AAA Encryption Service configurator started");
+    }
+
+    @Deactivate
+    public synchronized void deactivate() {
+        reg.close();
+        reg = null;
+        disableInstance();
+        LOG.debug("AAA Encryption Service configurator stopped");
+    }
+
+    @Override
+    public void onDataTreeChanged(final Collection<DataTreeModification<AaaEncryptServiceConfig>> changes) {
+        // Acquire the last reported configuration and check if it needs to have salt/password generated.
+        final var dsConfig = Iterables.getLast(changes).getRootNode().getDataAfter();
+        if (dsConfig == null || needKey(dsConfig) || needSalt(dsConfig)) {
+            // Generate salt/key as needed and persist it -- causing us to be re-invoked later.
+            updateDatastore(dsConfig);
+        } else {
+            // Configuration is self-consistent, proceed to activate an instance based on it
+            updateInstance(dsConfig);
+        }
+    }
+
+    @Override
+    public void onInitialData() {
+        updateDatastore(null);
+    }
+
+    @VisibleForTesting
+    static @NonNull AaaEncryptServiceConfig generateConfig(final @Nullable AaaEncryptServiceConfig datastoreConfig) {
+        // Select template and decide which parts need to be updated
+        final var template = datastoreConfig != null ? datastoreConfig : DEFAULT_CONFIG;
+        final var builder = new AaaEncryptServiceConfigBuilder(template);
+        if (needKey(template)) {
+            LOG.debug("Set the Encryption Service salt");
+            builder.setEncryptKey(RandomStringUtils.random(template.requirePasswordLength(), true, true));
+        }
+        if (needSalt(template)) {
+            LOG.debug("Set the Encryption Service salt");
+            final var salt = new byte[16];
+            RANDOM.nextBytes(salt);
+            builder.setEncryptSalt(Base64.getEncoder().encodeToString(salt));
+        }
+        return builder.build();
+    }
+
+    private void updateDatastore(final @Nullable AaaEncryptServiceConfig expected) {
+        final var target = generateConfig(expected);
+
+        // Careful update of the datastore: we are coming from DTCL thread, so inherently 'expected' may already be out
+        // of date, either by user action, or our update from another node (in a cluster). We rely on transaction's
+        // read&put atomicity to do the right thing here.
+        final var iid = InstanceIdentifier.create(AaaEncryptServiceConfig.class);
+        final var tx = dataBroker.newReadWriteTransaction();
+        final var readFuture = tx.read(LogicalDatastoreType.CONFIGURATION, iid);
+
+        final AaaEncryptServiceConfig actual;
+        try {
+            actual = readFuture.get().orElse(null);
+        } catch (InterruptedException | ExecutionException e) {
+            // Read failed: all we can do now is to disable the service and hope for an external recovery action -- like
+            // a restart of this component or a write to the datastore (which will trigger a retry).
+            tx.cancel();
+            LOG.error("Failed to read configuration, disabling service", e);
+            synchronized (this) {
+                disableInstance();
+            }
+            return;
+        }
+
+        if (!Objects.equals(actual, expected)) {
+            // Yup, there has been a race -- log that fact and bail out
+            tx.cancel();
+            LOG.debug(Markers.confidential(), "Skipping update on datastore mismatch: expected {} actual {}",
+                expected, actual);
+            return;
+        }
+
+        LOG.debug(Markers.confidential(), "Updating configuration to {}", target);
+        tx.put(LogicalDatastoreType.CONFIGURATION, iid, target);
+        Futures.addCallback(tx.commit(), new FutureCallback<CommitInfo>() {
+            @Override
+            public void onFailure(final Throwable throwable) {
+                // Async update: we should get a new onDataTreeChanged() callback
+                LOG.warn("Configuration update failed, attempting to continue", throwable);
+            }
+
+            @Override
+            public void onSuccess(final CommitInfo result) {
+                LOG.info("Configuration update succeeded");
+            }
+        }, MoreExecutors.directExecutor());
+    }
+
+    @Holding("this")
+    private void disableInstance() {
+        if (instance != null) {
+            instance.dispose();
+            instance = null;
+            current = null;
+            LOG.info("Encryption Service disabled");
+        }
+    }
+
+    private synchronized void updateInstance(final AaaEncryptServiceConfig newConfig) {
+        if (reg == null) {
+            LOG.debug("Skipping instance update due to shutdown");
+            return;
+        }
+        if (newConfig.equals(current)) {
+            LOG.debug("Skipping instance update due to equal configuration");
+            return;
+        }
+
+        disableInstance();
+        instance = factory.newInstance(FrameworkUtil.asDictionary(
+            AAAEncryptionServiceImpl.props(new EncryptServiceConfigImpl(newConfig))));
+        current = newConfig;
+        LOG.info("Encryption Service enabled");
+    }
+
+    private static boolean needKey(final AaaEncryptServiceConfig config) {
+        final var key = config.getEncryptKey();
+        return key == null || key.length() != config.requirePasswordLength();
+    }
+
+    private static boolean needSalt(final AaaEncryptServiceConfig config) {
+        final var salt = config.getEncryptSalt();
+        return salt == null || salt.isEmpty();
+    }
+}
diff --git a/aaa-encrypt-service/impl/src/main/resources/OSGI-INF/blueprint/encryptservice.xml b/aaa-encrypt-service/impl/src/main/resources/OSGI-INF/blueprint/encryptservice.xml
deleted file mode 100644 (file)
index 5009bce..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
-           xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0">
-  <reference id="dataBroker" interface="org.opendaylight.mdsal.binding.api.DataBroker"/>
-
-  <odl:clustered-app-config id="encryptConfig" default-config-file-name="aaa-encrypt-service-config.xml"
-        binding-class="org.opendaylight.yang.gen.v1.config.aaa.authn.encrypt.service.config.rev160915.AaaEncryptServiceConfig">
-  </odl:clustered-app-config>
-
-  <bean id="encryptServiceConfigurator" class="org.opendaylight.aaa.encrypt.impl.AAAEncryptionServiceConfigurator">
-    <argument ref="dataBroker"/>
-    <argument ref="encryptConfig"/>
-  </bean>
-  <service ref="encryptServiceConfigurator" interface="org.opendaylight.aaa.encrypt.impl.EncryptServiceConfigSupplier"/>
-
-</blueprint>
diff --git a/aaa-encrypt-service/impl/src/main/resources/initial/aaa-encrypt-service-config.xml b/aaa-encrypt-service/impl/src/main/resources/initial/aaa-encrypt-service-config.xml
deleted file mode 100644 (file)
index 17072a8..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<aaa-encrypt-service-config xmlns="config:aaa:authn:encrypt:service:config">
-    <encrypt-key>V1S1ED4OMeEh</encrypt-key>
-    <password-length>12</password-length>
-    <encrypt-salt>TdtWeHbch/7xP52/rp3Usw==</encrypt-salt>
-    <encrypt-method>PBKDF2WithHmacSHA1</encrypt-method>
-    <encrypt-type>AES</encrypt-type>
-    <encrypt-iteration-count>32768</encrypt-iteration-count>
-    <encrypt-key-length>128</encrypt-key-length>
-    <cipher-transforms>AES/CBC/PKCS5Padding</cipher-transforms>
-</aaa-encrypt-service-config>
index be3a78dd785db37ac6e635dc47b26559570dbd1d..67eac6c0a4e92d400d6a673a1579e8fddcadf582 100644 (file)
@@ -14,22 +14,29 @@ module aaa-encrypt-service-config {
     leaf encrypt-method {
       description "The encryption method to use";
       type string;
+      default PBKDF2WithHmacSHA1;
     }
     leaf encrypt-type {
       description "The encryption type";
       type string;
+      default AES;
     }
     leaf encrypt-iteration-count {
       description "Number of iterations that will be used by the key";
+      // FIXME: uint32
       type int32;
+      default 32768;
     }
     leaf encrypt-key-length {
       description "Key length";
+      // FIXME: uint32
       type int32;
+      default 128;
     }
     leaf cipher-transforms {
       description "cipher transformation type ex: AES/CBC/PKCS5Padding (128)";
       type string;
+      default "AES/CBC/PKCS5Padding";
     }
   }
 
index f04fb96cc61b2a3354c3f1d19ca2fd7e121521d7..32ba3eba89d1c64983b0512480cf0037fb1ce2c7 100644 (file)
@@ -9,46 +9,31 @@ package org.opendaylight.aaa.encrypt.impl;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
-import static org.mockito.Mockito.doReturn;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-import org.opendaylight.mdsal.binding.api.DataBroker;
-import org.opendaylight.mdsal.binding.api.ReadWriteTransaction;
-import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.yang.gen.v1.config.aaa.authn.encrypt.service.config.rev160915.AaaEncryptServiceConfig;
 import org.opendaylight.yang.gen.v1.config.aaa.authn.encrypt.service.config.rev160915.AaaEncryptServiceConfigBuilder;
-import org.opendaylight.yangtools.util.concurrent.FluentFutures;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
 /*
  *  @author - Sharon Aicler (saichler@gmail.com)
  */
 @Deprecated
-@RunWith(MockitoJUnitRunner.StrictStubs.class)
 public class AAAEncryptServiceImplTest {
-    @Mock
-    private DataBroker dataBroker;
-    @Mock
-    private ReadWriteTransaction tx;
-
     private AAAEncryptionServiceImpl impl;
 
     @Before
     public void setup() {
-        final var module = new AaaEncryptServiceConfigBuilder()
-                .setCipherTransforms("AES/CBC/PKCS5Padding").setEncryptIterationCount(32768).setEncryptKey("")
-                .setEncryptKeyLength(128).setEncryptMethod("PBKDF2WithHmacSHA1").setEncryptSalt("")
-                .setEncryptType("AES").setPasswordLength(12).build();
-
-        doReturn(tx).when(dataBroker).newReadWriteTransaction();
-        doReturn(FluentFutures.immediateTrueFluentFuture()).when(tx)
-            .exists(LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(AaaEncryptServiceConfig.class));
-
-        impl = new AAAEncryptionServiceImpl(new AAAEncryptionServiceConfigurator(dataBroker, module));
+        impl = new AAAEncryptionServiceImpl(new EncryptServiceConfigImpl(
+            OSGiEncryptionServiceConfigurator.generateConfig(new AaaEncryptServiceConfigBuilder()
+                .setCipherTransforms("AES/CBC/PKCS5Padding")
+                .setEncryptIterationCount(32768)
+                .setEncryptKey("")
+                .setEncryptKeyLength(128)
+                .setEncryptMethod("PBKDF2WithHmacSHA1")
+                .setEncryptSalt("")
+                .setEncryptType("AES")
+                .setPasswordLength(12)
+                .build())));
     }
 
     @Test
diff --git a/aaa-encrypt-service/impl/src/test/java/org/opendaylight/aaa/encrypt/impl/OSGiEncryptionServiceConfiguratorTest.java b/aaa-encrypt-service/impl/src/test/java/org/opendaylight/aaa/encrypt/impl/OSGiEncryptionServiceConfiguratorTest.java
new file mode 100644 (file)
index 0000000..e7a375e
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2023 PANTHEON.tech, 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.aaa.encrypt.impl;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+
+import java.util.Base64;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+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.binding.api.ReadWriteTransaction;
+import org.opendaylight.mdsal.common.api.CommitInfo;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.config.aaa.authn.encrypt.service.config.rev160915.AaaEncryptServiceConfig;
+import org.opendaylight.yang.gen.v1.config.aaa.authn.encrypt.service.config.rev160915.EncryptServiceConfig;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.util.concurrent.FluentFutures;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.osgi.service.component.ComponentFactory;
+import org.osgi.service.component.ComponentInstance;
+
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
+public class OSGiEncryptionServiceConfiguratorTest {
+    private static final @NonNull InstanceIdentifier<AaaEncryptServiceConfig> IID =
+        InstanceIdentifier.create(AaaEncryptServiceConfig.class);
+
+    @Mock
+    private DataBroker dataBroker;
+    @Mock
+    private ComponentFactory<AAAEncryptionServiceImpl> factory;
+    @Mock
+    private ComponentInstance<AAAEncryptionServiceImpl> instance;
+    @Mock
+    private ListenerRegistration<?> registration;
+    @Mock
+    private ReadWriteTransaction transaction;
+    @Mock
+    private DataTreeModification<AaaEncryptServiceConfig> treeModification;
+    @Mock
+    private DataObjectModification<AaaEncryptServiceConfig> objectModification;
+    @Captor
+    private ArgumentCaptor<DataTreeIdentifier<AaaEncryptServiceConfig>> treeIdCaptor;
+    @Captor
+    private ArgumentCaptor<DataTreeChangeListener<AaaEncryptServiceConfig>> listenerCaptor;
+    @Captor
+    private ArgumentCaptor<AaaEncryptServiceConfig> configCaptor;
+    @Captor
+    private ArgumentCaptor<Dictionary<String, ?>> propertiesCaptor;
+
+    private OSGiEncryptionServiceConfigurator configurator;
+
+    @Before
+    public void before() {
+        doReturn(registration).when(dataBroker).registerDataTreeChangeListener(treeIdCaptor.capture(),
+            listenerCaptor.capture());
+
+        configurator = new OSGiEncryptionServiceConfigurator(dataBroker, factory);
+
+        assertEquals(DataTreeIdentifier.create(LogicalDatastoreType.CONFIGURATION, IID), treeIdCaptor.getValue());
+        assertSame(configurator, listenerCaptor.getValue());
+    }
+
+    @Test
+    public void testImmediateDeactivate() {
+        doNothing().when(registration).close();
+        configurator.deactivate();
+    }
+
+    @Test
+    public void testEmptyDatastore() {
+        // Config datastore write is expected: capture what gets written
+        doReturn(transaction).when(dataBroker).newReadWriteTransaction();
+        doReturn(FluentFutures.immediateFluentFuture(Optional.empty())).when(transaction)
+            .read(LogicalDatastoreType.CONFIGURATION, IID);
+        doNothing().when(transaction).put(eq(LogicalDatastoreType.CONFIGURATION), eq(IID), configCaptor.capture());
+        doReturn(CommitInfo.emptyFluentFuture()).when(transaction).commit();
+
+        configurator.onInitialData();
+
+        final var config = configCaptor.getValue();
+        assertEquals("AES/CBC/PKCS5Padding", config.getCipherTransforms());
+        assertEquals(Integer.valueOf(32768), config.getEncryptIterationCount());
+        assertEquals(Integer.valueOf(128), config.getEncryptKeyLength());
+        assertEquals("PBKDF2WithHmacSHA1", config.getEncryptMethod());
+        assertEquals("AES", config.getEncryptType());
+        assertEquals(Integer.valueOf(12), config.getPasswordLength());
+
+        final var salt = Base64.getDecoder().decode(config.getEncryptSalt());
+        assertEquals(16, salt.length);
+
+        final var key = config.getEncryptKey();
+        assertNotNull(key);
+        assertEquals(12, key.length());
+
+        // Now we circle around are report that config. We expect the factory to be called
+        doReturn(config).when(objectModification).getDataAfter();
+        doReturn(objectModification).when(treeModification).getRootNode();
+        doReturn(instance).when(factory).newInstance(propertiesCaptor.capture());
+
+        configurator.onDataTreeChanged(List.of(treeModification));
+
+        final var props = propertiesCaptor.getValue();
+        assertNotNull(props);
+        assertEquals(1, props.size());
+        final var configObj = props.elements().nextElement();
+        assertNotNull(configObj);
+        assertThat(configObj, instanceOf(EncryptServiceConfig.class));
+        final var serviceConfig = (EncryptServiceConfig) configObj;
+        assertArrayEquals(salt, serviceConfig.getEncryptSalt());
+        assertEquals(key, serviceConfig.getEncryptKey());
+
+        // Now shut down configurator
+        doNothing().when(registration).close();
+        doNothing().when(instance).dispose();
+        configurator.deactivate();
+    }
+}
index e82f87a9c9b6d1b415311120c644ded90527c593..26b628b77fd16e52aa460c7c3bebbfe1c0906e95 100644 (file)
                 <artifactId>aaa-encrypt-service-impl</artifactId>
                 <version>${project.version}</version>
             </dependency>
-            <dependency>
-                <groupId>${project.groupId}</groupId>
-                <artifactId>aaa-encrypt-service-impl</artifactId>
-                <version>${project.version}</version>
-                <type>xml</type>
-                <classifier>config</classifier>
-            </dependency>
 
             <dependency>
                 <groupId>${project.groupId}</groupId>
index 42e01a37445c61b7c3cb106f6d0409ba23f05353..b8d05b4d008e9983c2509e43d8a38d23744af8f2 100644 (file)
             <groupId>${project.groupId}</groupId>
             <artifactId>aaa-encrypt-service</artifactId>
         </dependency>
-        <dependency>
-            <groupId>${project.groupId}</groupId>
-            <artifactId>aaa-encrypt-service-impl</artifactId>
-            <classifier>config</classifier>
-            <type>xml</type>
-        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>aaa-filterchain</artifactId>
index 9ca3d458b6eb4a1a8eac47a43de593e5326e0222..636a5b4363863bd812dd99cbe7bce57945e45643 100644 (file)
@@ -8,6 +8,7 @@
  -->
 <features name="odl-aaa-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.2.0">
     <feature name="odl-aaa-cert" version="${project.version}">
+        <feature version="[7,8)">odl-controller-blueprint</feature>
         <feature version="[7,8)">odl-mdsal-broker</feature>
         <configfile finalname="etc/opendaylight/datastore/initial/config/aaa-cert-config.xml">
             mvn:org.opendaylight.aaa/aaa-cert/${project.version}/xml/config
index 99981e46da842bb76a4cb777eebbc1fc2e24f73f..3c80192a21a286283d94ff4f63b3897b2f64cf43 100644 (file)
             <groupId>org.opendaylight.aaa</groupId>
             <artifactId>aaa-encrypt-service-impl</artifactId>
         </dependency>
-        <dependency>
-            <!-- finalname="etc/opendaylight/datastore/initial/config/aaa-encrypt-service-config.xml" -->
-            <groupId>org.opendaylight.aaa</groupId>
-            <artifactId>aaa-encrypt-service-impl</artifactId>
-            <type>xml</type>
-            <classifier>config</classifier>
-        </dependency>
     </dependencies>
 </project>
index 288f5f026bb7fdeb79da8c860c596a6b18acd4ad..e20ae5874b85fdfcdf4b63f21282d547400b44cb 100644 (file)
@@ -8,10 +8,6 @@
  -->
 <features name="odl-aaa-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.2.0">
     <feature name="odl-aaa-encryption-service" version="${project.version}">
-        <feature version="[7,8)">odl-controller-blueprint</feature>
         <feature version="[7,8)">odl-mdsal-broker</feature>
-        <configfile finalname="etc/opendaylight/datastore/initial/config/aaa-encrypt-service-config.xml">
-            mvn:org.opendaylight.aaa/aaa-encrypt-service-impl/${project.version}/xml/config
-        </configfile>
     </feature>
 </features>