Repair AuthenticationManager 28/93228/6
authorRobert Varga <robert.varga@pantheon.tech>
Wed, 21 Oct 2020 10:39:59 +0000 (12:39 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 21 Oct 2020 11:40:46 +0000 (13:40 +0200)
AuthenticationManager was utterly wrecked in commit
193729d84ebb567d94e75311cd3bb871e7731b0b, which removed its
binding to CM, effectively meaning it was always disabled.

This is best evidenced by the unused bits in OSGI-INF/metatype,
the unpackaged authn.cfg file and the fact AuthenticationManager
is referencing them without having the BluePrint wiring to do so.

The primary problem is addressed by switching to OSGi DS, which
eliminates all run-time dependencies on OSGi, as it is purely
compile-time.

This flushes out the rest of the problems, as in order to drive
the OSGi CM integration we really need a configurationPID --
which investigation has shown to be long-lost
'org.opendaylight.aaa.authn'.

Furthermore OSGi CM integration really discourages use of properties,
preferring those be bound to a configuration object -- which points
to src/main/resources/OSGI-INF/metatype/ -- hence we ditch those XMLs
and replace them with Metatype Annotations.

Doing all this shifts property interpretation solely down to
ConfigAdmin -- hence we do not need to deal with at all, cleaning up
AuthenticationManagerTest.

Finally, to take advantage of all this work, we re-ingrate the new
component into blueprint as a simple service. We throw in proper
@Singleton annotation as a bonus.

JIRA: AAA-201
Change-Id: I51e23dc02836208774e8cdb8fb1a999537a691a6
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
aaa-shiro/impl/pom.xml
aaa-shiro/impl/src/main/java/org/opendaylight/aaa/shiro/tokenauthrealm/auth/AuthenticationManager.java
aaa-shiro/impl/src/main/resources/OSGI-INF/blueprint/impl-blueprint.xml
aaa-shiro/impl/src/main/resources/OSGI-INF/metatype/metatype.properties [deleted file]
aaa-shiro/impl/src/main/resources/OSGI-INF/metatype/metatype.xml [deleted file]
aaa-shiro/impl/src/main/resources/authn.cfg
aaa-shiro/impl/src/test/java/org/opendaylight/aaa/shiro/tokenauthrealm/auth/AuthenticationManagerTest.java

index 65f2d638947bd239cf0ce997f9a08901668cc0ac..4db517aa630dc7765118ff7dd85ce9ecb9ddb04a 100644 (file)
@@ -55,12 +55,6 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
             <artifactId>gson</artifactId>
         </dependency>
 
-        <dependency>
-            <groupId>org.apache.felix</groupId>
-            <artifactId>org.apache.felix.dependencymanager</artifactId>
-            <scope>provided</scope>
-        </dependency>
-
         <!--Yang Binding -->
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
@@ -78,6 +72,12 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
             <groupId>org.osgi</groupId>
             <artifactId>osgi.cmpn</artifactId>
         </dependency>
+        <dependency>
+            <groupId>javax.inject</groupId>
+            <artifactId>javax.inject</artifactId>
+            <optional>true</optional>
+            <scope>provided</scope>
+        </dependency>
 
         <!-- JSON JAXB Stuff -->
         <dependency>
index 5ba9573c04b2bb2a6a87f4fa7463ae904fc05757..d46dafc4e0ab6217260245fecb3d9961e1e28893 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2014, 2017 Hewlett-Packard Development Company, L.P. and others.  All rights reserved.
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
@@ -7,28 +8,56 @@
  */
 package org.opendaylight.aaa.shiro.tokenauthrealm.auth;
 
-import java.util.Dictionary;
+import javax.inject.Singleton;
 import org.opendaylight.aaa.api.Authentication;
 import org.opendaylight.aaa.api.AuthenticationService;
-import org.osgi.service.cm.ConfigurationException;
-import org.osgi.service.cm.ManagedService;
+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.Modified;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * An {@link InheritableThreadLocal}-based {@link AuthenticationService}.
- *
- * @author liemmn
  */
-public class AuthenticationManager implements AuthenticationService, ManagedService {
-    private static final String AUTH_ENABLED_ERR = "Error setting authEnabled";
+@Singleton
+@Component(configurationPid = "org.opendaylight.aaa.authn")
+@Designate(ocd = AuthenticationManager.Configuration.class)
+public final class AuthenticationManager implements AuthenticationService {
+    @ObjectClassDefinition(name = "OpenDaylight AAA Authentication Configuration")
+    public @interface Configuration {
+        @AttributeDefinition(
+            name = "Enable authentication",
+            description =
+                "Enable authentication by setting it to the value 'true', or 'false' if bypassing authentication.\n"
+                    + "Note that bypassing authentication may result in your controller being more vulnerable to "
+                    + "unauthorized accesses.\n"
+                    + "Authorization, if enabled, will not work if authentication is disabled.")
+        boolean authEnabled() default true;
+    }
 
-    protected static final String AUTH_ENABLED = "authEnabled";
+    private static final Logger LOG = LoggerFactory.getLogger(AuthenticationManager.class);
 
-    // In non-Karaf environments, authEnabled is set to false by default
-    private volatile boolean authEnabled = false;
+    private volatile boolean authEnabled;
 
     private final ThreadLocal<Authentication> auth = new InheritableThreadLocal<>();
 
     public AuthenticationManager() {
+        // In non-Karaf environments, authEnabled is set to false by default
+        this(false);
+    }
+
+    public AuthenticationManager(final boolean authEnabled) {
+        this.authEnabled = authEnabled;
+    }
+
+    public void setAuthEnabled(final boolean authEnabled) {
+        this.authEnabled = authEnabled;
+        LOG.info("Authentication is now {}", authEnabled ? "enabled" : "disabled");
     }
 
     @Override
@@ -37,7 +66,7 @@ public class AuthenticationManager implements AuthenticationService, ManagedServ
     }
 
     @Override
-    public void set(Authentication authentication) {
+    public void set(final Authentication authentication) {
         auth.set(authentication);
     }
 
@@ -51,17 +80,20 @@ public class AuthenticationManager implements AuthenticationService, ManagedServ
         return authEnabled;
     }
 
-    @Override
-    public void updated(Dictionary<String, ?> properties) throws ConfigurationException {
-        if (properties == null) {
-            return;
-        }
+    @Activate
+    void activate(final Configuration configuration) {
+        setAuthEnabled(configuration.authEnabled());
+        LOG.info("Authentication Manager activated");
+    }
+
+    @Deactivate
+    @SuppressWarnings("static-method")
+    void deactivate() {
+        LOG.info("Authentication Manager deactivated");
+    }
 
-        String propertyValue = (String) properties.get(AUTH_ENABLED);
-        boolean isTrueString = Boolean.parseBoolean(propertyValue);
-        if (!isTrueString && !"false".equalsIgnoreCase(propertyValue)) {
-            throw new ConfigurationException(AUTH_ENABLED, AUTH_ENABLED_ERR);
-        }
-        authEnabled = isTrueString;
+    @Modified
+    void modified(final Configuration configuration) {
+        setAuthEnabled(configuration.authEnabled());
     }
 }
index 591619e93d6763df67252b060eb23db502b83732..ab045e202abe98dbe6249f8b10c81e088d8df997 100644 (file)
@@ -20,8 +20,9 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
         default-config-file-name="aaa-datastore-config.xml"
         binding-class="org.opendaylight.yang.gen.v1.urn.opendaylight.aaa.app.config.rev170619.DatastoreConfig" />
 
+  <reference id="authService" interface="org.opendaylight.aaa.api.AuthenticationService"/>
   <reference id="passwordService" interface="org.opendaylight.aaa.api.password.service.PasswordHashService"
-        odl:type="default"/>
+      odl:type="default"/>
 
   <bean id="idmStore" class="org.opendaylight.aaa.datastore.h2.H2Store">
     <argument value="${dbUsername}" />
@@ -34,8 +35,6 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
     <argument ref="passwordService" />
   </bean>
 
-  <bean id="authService" class="org.opendaylight.aaa.shiro.tokenauthrealm.auth.AuthenticationManager"/>
-
   <service ref="idmStore" interface="org.opendaylight.aaa.api.IIDMStore" odl:type="default"/>
 
   <service ref="idmLightProxy" odl:type="default">
diff --git a/aaa-shiro/impl/src/main/resources/OSGI-INF/metatype/metatype.properties b/aaa-shiro/impl/src/main/resources/OSGI-INF/metatype/metatype.properties
deleted file mode 100644 (file)
index 9f7b7d3..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-org.opendaylight.aaa.authn.name = Opendaylight AAA Authentication Configuration
-org.opendaylight.aaa.authn.description = Configuration for AAA authorized clients
-org.opendaylight.aaa.authn.authorizedClients.name = Authorized Clients
-org.opendaylight.aaa.authn.authorizedClients.description = Space-delimited list of authorized \
- clients, with client id and client password separated by a ':'. \
- Example:  dlux:secret LESS-THAN client_id:client_secret BIGGER-THAN
-org.opendaylight.aaa.authn.authEnabled.name = Enable authentication
-org.opendaylight.aaa.authn.authEnabled.description = Enable authentication by setting it \
-to the value 'true', or 'false' if bypassing authentication. \
-Note that bypassing authentication may result in your controller being more \
-vulnerable to unauthorized accesses.  Authorization, if enabled, will not work if \
-authentication is disabled.
diff --git a/aaa-shiro/impl/src/main/resources/OSGI-INF/metatype/metatype.xml b/aaa-shiro/impl/src/main/resources/OSGI-INF/metatype/metatype.xml
deleted file mode 100644 (file)
index 1015058..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<metatype:MetaData xmlns:metatype="http://www.osgi.org/xmlns/metatype/v1.0.0"
-    localization="OSGI-INF/metatype/metatype">
-    <OCD id="org.opendaylight.aaa.authn" name="%org.opendaylight.aaa.authn.name"
-        description="%org.opendaylight.aaa.authn.description">
-        <AD id="authorizedClients" type="String" default="dlux:secrete"
-            name="%org.opendaylight.aaa.authn.authorizedClients.name"
-            description="%org.opendaylight.aaa.authn.authorizedClients.description" />
-        <AD id="authEnabled" type="String" default="true"
-            name="%org.opendaylight.aaa.authn.authEnabled.name"
-            description="%org.opendaylight.aaa.authn.authEnabled.description" />
-    </OCD>
-    <Designate pid="org.opendaylight.aaa.authn">
-        <Object ocdref="org.opendaylight.aaa.authn" />
-    </Designate>
-</metatype:MetaData>
\ No newline at end of file
index e7326f86e9c36333735a727b83fb7fb734eb66ab..ebe4563122f8f456b6aa70fac98c11a2a128b522 100644 (file)
@@ -1,2 +1 @@
-authorizedClients=dlux:secrete
-authEnabled=true
\ No newline at end of file
+authEnabled=true
index 7203b2b53bd8157536bf1ff9ee6f9d7f9a14907e..109f2ec3feea32713bf777fcc8ff8e2fe0d199d7 100644 (file)
@@ -5,7 +5,6 @@
  * 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.shiro.tokenauthrealm.auth;
 
 import static org.junit.Assert.assertEquals;
@@ -13,21 +12,25 @@ import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
 
 import java.util.Arrays;
-import java.util.Dictionary;
-import java.util.Hashtable;
 import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
-import org.gaul.modernizer_maven_annotations.SuppressModernizer;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
 import org.opendaylight.aaa.api.Authentication;
-import org.osgi.service.cm.ConfigurationException;
 
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
 public class AuthenticationManagerTest {
+    @Mock
+    private AuthenticationManager.Configuration configuration;
+
     private final AuthenticationManager authManager = new AuthenticationManager();
 
     @Test
@@ -79,46 +82,17 @@ public class AuthenticationManagerTest {
         }
     }
 
-    @SuppressModernizer
     @Test
-    public void testUpdatedValid() throws ConfigurationException {
-        Dictionary<String, String> props = new Hashtable<>();
-
+    public void testUpdatedValid() {
         assertFalse(authManager.isAuthEnabled());
 
-        props.put(AuthenticationManager.AUTH_ENABLED, "TrUe");
-        authManager.updated(props);
+        doReturn(true).when(configuration).authEnabled();
+        authManager.modified(configuration);
         assertTrue(authManager.isAuthEnabled());
 
-        props.put(AuthenticationManager.AUTH_ENABLED, "FaLsE");
-        authManager.updated(props);
-        assertFalse(authManager.isAuthEnabled());
-    }
-
-    @Test
-    public void testUpdatedNullProperty() throws ConfigurationException {
-
+        doReturn(false).when(configuration).authEnabled();
+        authManager.modified(configuration);
         assertFalse(authManager.isAuthEnabled());
-        authManager.updated(null);
-        assertFalse(authManager.isAuthEnabled());
-    }
-
-    @SuppressModernizer
-    @Test(expected = ConfigurationException.class)
-    public void testUpdatedInvalidValue() throws ConfigurationException {
-        Dictionary<String, String> props = new Hashtable<>();
-
-        props.put(AuthenticationManager.AUTH_ENABLED, "yes");
-        authManager.updated(props);
-    }
-
-    @SuppressModernizer
-    @Test(expected = ConfigurationException.class)
-    public void testUpdatedInvalidKey() throws ConfigurationException {
-        Dictionary<String, String> props = new Hashtable<>();
-
-        props.put("Invalid Key", "true");
-        authManager.updated(props);
     }
 
     private class Worker implements Callable<Authentication> {