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>
<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>
<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>
/*
* 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,
*/
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
}
@Override
- public void set(Authentication authentication) {
+ public void set(final Authentication authentication) {
auth.set(authentication);
}
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());
}
}
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}" />
<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">
+++ /dev/null
-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.
+++ /dev/null
-<?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
-authorizedClients=dlux:secrete
-authEnabled=true
\ No newline at end of file
+authEnabled=true
* 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;
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
}
}
- @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> {