aboutsummaryrefslogtreecommitdiffstats
path: root/odl-aaa-moon/aaa/aaa-authn/src
diff options
context:
space:
mode:
authorDUVAL Thomas <thomas.duval@orange.com>2016-06-16 13:08:32 +0000
committerGerrit Code Review <gerrit@172.30.200.206>2016-06-16 13:08:32 +0000
commit9bdc327fd67e2189b6088f83f7d7470efc859acb (patch)
tree7ee82b65535696e1f004ab2a2de16ced0ac8b6d5 /odl-aaa-moon/aaa/aaa-authn/src
parent2cadaf72d814b8f8c243855c65ba43d3b9a701cc (diff)
parentadf7e6616c2a8d6f60207059288423f693509928 (diff)
Merge "Add new version of aaa"
Diffstat (limited to 'odl-aaa-moon/aaa/aaa-authn/src')
-rw-r--r--odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/Activator.java51
-rw-r--r--odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/AuthenticationBuilder.java122
-rw-r--r--odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/AuthenticationManager.java77
-rw-r--r--odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/ClaimBuilder.java160
-rw-r--r--odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/ClientManager.java88
-rw-r--r--odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/EqualUtil.java42
-rw-r--r--odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/HashCodeUtil.java104
-rw-r--r--odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/PasswordCredentialBuilder.java87
-rw-r--r--odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/SecureBlockingQueue.java258
-rw-r--r--odl-aaa-moon/aaa/aaa-authn/src/main/resources/OSGI-INF/metatype/metatype.properties12
-rw-r--r--odl-aaa-moon/aaa/aaa-authn/src/main/resources/OSGI-INF/metatype/metatype.xml16
-rw-r--r--odl-aaa-moon/aaa/aaa-authn/src/main/resources/authn.cfg2
-rw-r--r--odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/AuthenticationBuilderTest.java129
-rw-r--r--odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/AuthenticationManagerTest.java133
-rw-r--r--odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/ClaimBuilderTest.java208
-rw-r--r--odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/ClientManagerTest.java70
-rw-r--r--odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/PasswordCredentialTest.java39
-rw-r--r--odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/SecureBlockingQueueTest.java191
18 files changed, 1789 insertions, 0 deletions
diff --git a/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/Activator.java b/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/Activator.java
new file mode 100644
index 00000000..cfe27ef0
--- /dev/null
+++ b/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/Activator.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. 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;
+
+import java.util.Dictionary;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.opendaylight.aaa.api.AuthenticationService;
+import org.opendaylight.aaa.api.ClientService;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ManagedService;
+
+/**
+ * Activator to register {@link AuthenticationService} with OSGi.
+ *
+ * @author liemmn
+ *
+ */
+public class Activator extends DependencyActivatorBase {
+
+ private static final String AUTHN_PID = "org.opendaylight.aaa.authn";
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ manager.add(createComponent().setInterface(
+ new String[] { AuthenticationService.class.getName() }, null).setImplementation(
+ AuthenticationManager.instance()));
+
+ ClientManager cm = new ClientManager();
+ manager.add(createComponent().setInterface(new String[] { ClientService.class.getName() },
+ null).setImplementation(cm));
+ context.registerService(ManagedService.class.getName(), cm, addPid(ClientManager.defaults));
+ context.registerService(ManagedService.class.getName(), AuthenticationManager.instance(),
+ addPid(AuthenticationManager.defaults));
+ }
+
+ @Override
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ }
+
+ private Dictionary<String, ?> addPid(Dictionary<String, String> dict) {
+ dict.put(Constants.SERVICE_PID, AUTHN_PID);
+ return dict;
+ }
+}
diff --git a/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/AuthenticationBuilder.java b/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/AuthenticationBuilder.java
new file mode 100644
index 00000000..948cbac6
--- /dev/null
+++ b/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/AuthenticationBuilder.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. 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;
+
+import static org.opendaylight.aaa.EqualUtil.areEqual;
+import static org.opendaylight.aaa.HashCodeUtil.hash;
+
+import java.io.Serializable;
+import java.util.Set;
+import org.opendaylight.aaa.api.Authentication;
+import org.opendaylight.aaa.api.Claim;
+
+/**
+ * A builder for the authentication context.
+ *
+ * The expiration defaults to 0.
+ *
+ * @author liemmn
+ *
+ */
+public class AuthenticationBuilder {
+
+ private long expiration = 0L;
+ private Claim claim;
+
+ public AuthenticationBuilder(Claim claim) {
+ this.claim = claim;
+ }
+
+ public AuthenticationBuilder setExpiration(long expiration) {
+ this.expiration = expiration;
+ return this;
+ }
+
+ public Authentication build() {
+ return new ImmutableAuthentication(this);
+ }
+
+ private static final class ImmutableAuthentication implements Authentication, Serializable {
+ private static final long serialVersionUID = 4919078164955609987L;
+ private int hashCode = 0;
+ long expiration = 0L;
+ Claim claim;
+
+ private ImmutableAuthentication(AuthenticationBuilder base) {
+ if (base.claim == null) {
+ throw new IllegalStateException("The Claim is null.");
+ }
+ claim = new ClaimBuilder(base.claim).build();
+ expiration = base.expiration;
+
+ if (base.expiration < 0) {
+ throw new IllegalStateException("The expiration is less than 0.");
+ }
+ }
+
+ @Override
+ public long expiration() {
+ return expiration;
+ }
+
+ @Override
+ public String clientId() {
+ return claim.clientId();
+ }
+
+ @Override
+ public String userId() {
+ return claim.userId();
+ }
+
+ @Override
+ public String user() {
+ return claim.user();
+ }
+
+ @Override
+ public String domain() {
+ return claim.domain();
+ }
+
+ @Override
+ public Set<String> roles() {
+ return claim.roles();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Authentication)) {
+ return false;
+ }
+ Authentication a = (Authentication) o;
+ return areEqual(expiration, a.expiration()) && areEqual(claim.roles(), a.roles())
+ && areEqual(claim.domain(), a.domain()) && areEqual(claim.userId(), a.userId())
+ && areEqual(claim.user(), a.user()) && areEqual(claim.clientId(), a.clientId());
+ }
+
+ @Override
+ public int hashCode() {
+ if (hashCode == 0) {
+ int result = HashCodeUtil.SEED;
+ result = hash(result, expiration);
+ result = hash(result, claim.hashCode());
+ hashCode = result;
+ }
+ return hashCode;
+ }
+
+ @Override
+ public String toString() {
+ return "expiration:" + expiration + "," + claim.toString();
+ }
+ }
+} \ No newline at end of file
diff --git a/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/AuthenticationManager.java b/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/AuthenticationManager.java
new file mode 100644
index 00000000..5f6420a3
--- /dev/null
+++ b/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/AuthenticationManager.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. 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;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import org.opendaylight.aaa.api.Authentication;
+import org.opendaylight.aaa.api.AuthenticationService;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+
+/**
+ * An {@link InheritableThreadLocal}-based {@link AuthenticationService}.
+ *
+ * @author liemmn
+ */
+public class AuthenticationManager implements AuthenticationService, ManagedService {
+ private static final String AUTH_ENABLED_ERR = "Error setting authEnabled";
+
+ static final String AUTH_ENABLED = "authEnabled";
+ static final Dictionary<String, String> defaults = new Hashtable<>();
+ static {
+ defaults.put(AUTH_ENABLED, Boolean.FALSE.toString());
+ }
+
+ // In non-Karaf environments, authEnabled is set to false by default
+ private static volatile boolean authEnabled = false;
+
+ private final static AuthenticationManager am = new AuthenticationManager();
+ private final ThreadLocal<Authentication> auth = new InheritableThreadLocal<>();
+
+ private AuthenticationManager() {
+ }
+
+ static AuthenticationManager instance() {
+ return am;
+ }
+
+ @Override
+ public Authentication get() {
+ return auth.get();
+ }
+
+ @Override
+ public void set(Authentication a) {
+ auth.set(a);
+ }
+
+ @Override
+ public void clear() {
+ auth.remove();
+ }
+
+ @Override
+ public boolean isAuthEnabled() {
+ return authEnabled;
+ }
+
+ @Override
+ public void updated(Dictionary<String, ?> properties) throws ConfigurationException {
+ if (properties == null) {
+ return;
+ }
+
+ 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;
+ }
+}
diff --git a/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/ClaimBuilder.java b/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/ClaimBuilder.java
new file mode 100644
index 00000000..4e4a8ef3
--- /dev/null
+++ b/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/ClaimBuilder.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. 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;
+
+import static org.opendaylight.aaa.EqualUtil.areEqual;
+import static org.opendaylight.aaa.HashCodeUtil.hash;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
+import java.io.Serializable;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import org.opendaylight.aaa.api.Claim;
+
+/**
+ * Builder for a {@link Claim}. The userId, user, and roles information is
+ * mandatory.
+ *
+ * @author liemmn
+ *
+ */
+public class ClaimBuilder {
+ private String userId = "";
+ private String user = "";
+ private Set<String> roles = new LinkedHashSet<>();
+ private String clientId = "";
+ private String domain = "";
+
+ public ClaimBuilder() {
+ }
+
+ public ClaimBuilder(Claim claim) {
+ clientId = claim.clientId();
+ userId = claim.userId();
+ user = claim.user();
+ domain = claim.domain();
+ roles.addAll(claim.roles());
+ }
+
+ public ClaimBuilder setClientId(String clientId) {
+ this.clientId = Strings.nullToEmpty(clientId).trim();
+ return this;
+ }
+
+ public ClaimBuilder setUserId(String userId) {
+ this.userId = Strings.nullToEmpty(userId).trim();
+ return this;
+ }
+
+ public ClaimBuilder setUser(String userName) {
+ user = Strings.nullToEmpty(userName).trim();
+ return this;
+ }
+
+ public ClaimBuilder setDomain(String domain) {
+ this.domain = Strings.nullToEmpty(domain).trim();
+ return this;
+ }
+
+ public ClaimBuilder addRoles(Set<String> roles) {
+ for (String role : roles) {
+ addRole(role);
+ }
+ return this;
+ }
+
+ public ClaimBuilder addRole(String role) {
+ roles.add(Strings.nullToEmpty(role).trim());
+ return this;
+ }
+
+ public Claim build() {
+ return new ImmutableClaim(this);
+ }
+
+ protected static class ImmutableClaim implements Claim, Serializable {
+ private static final long serialVersionUID = -8115027645190209129L;
+ private int hashCode = 0;
+ protected String clientId;
+ protected String userId;
+ protected String user;
+ protected String domain;
+ protected ImmutableSet<String> roles;
+
+ protected ImmutableClaim(ClaimBuilder base) {
+ clientId = base.clientId;
+ userId = base.userId;
+ user = base.user;
+ domain = base.domain;
+ roles = ImmutableSet.<String> builder().addAll(base.roles).build();
+
+ if (userId.isEmpty() || user.isEmpty() || roles.isEmpty() || roles.contains("")) {
+ throw new IllegalStateException(
+ "The Claim is missing one or more of the required fields.");
+ }
+ }
+
+ @Override
+ public String clientId() {
+ return clientId;
+ }
+
+ @Override
+ public String userId() {
+ return userId;
+ }
+
+ @Override
+ public String user() {
+ return user;
+ }
+
+ @Override
+ public String domain() {
+ return domain;
+ }
+
+ @Override
+ public Set<String> roles() {
+ return roles;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof Claim))
+ return false;
+ Claim a = (Claim) o;
+ return areEqual(roles, a.roles()) && areEqual(domain, a.domain())
+ && areEqual(userId, a.userId()) && areEqual(user, a.user())
+ && areEqual(clientId, a.clientId());
+ }
+
+ @Override
+ public int hashCode() {
+ if (hashCode == 0) {
+ int result = HashCodeUtil.SEED;
+ result = hash(result, clientId);
+ result = hash(result, userId);
+ result = hash(result, user);
+ result = hash(result, domain);
+ result = hash(result, roles);
+ hashCode = result;
+ }
+ return hashCode;
+ }
+
+ @Override
+ public String toString() {
+ return "clientId:" + clientId + "," + "userId:" + userId + "," + "userName:" + user
+ + "," + "domain:" + domain + "," + "roles:" + roles;
+ }
+ }
+}
diff --git a/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/ClientManager.java b/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/ClientManager.java
new file mode 100644
index 00000000..e7e51424
--- /dev/null
+++ b/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/ClientManager.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. 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;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.apache.felix.dm.Component;
+import org.opendaylight.aaa.api.AuthenticationException;
+import org.opendaylight.aaa.api.ClientService;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+
+/**
+ * A configuration-based client manager.
+ *
+ * @author liemmn
+ *
+ */
+public class ClientManager implements ClientService, ManagedService {
+ static final String CLIENTS = "authorizedClients";
+ private static final String CLIENTS_FORMAT_ERR = "Clients are space-delimited in the form of <client_id>:<client_secret>";
+ private static final String UNAUTHORIZED_CLIENT_ERR = "Unauthorized client";
+
+ // Defaults (needed only for non-Karaf deployments)
+ static final Dictionary<String, String> defaults = new Hashtable<>();
+ static {
+ defaults.put(CLIENTS, "dlux:secrete");
+ }
+
+ private final Map<String, String> clients = new ConcurrentHashMap<>();
+
+ // This should be a singleton
+ ClientManager() {
+ }
+
+ // Called by DM when all required dependencies are satisfied.
+ void init(Component c) throws ConfigurationException {
+ reconfig(defaults);
+ }
+
+ @Override
+ public void validate(String clientId, String clientSecret) throws AuthenticationException {
+ // TODO: Post-Helium, we will support a CRUD API
+ if (!clients.containsKey(clientId)) {
+ throw new AuthenticationException(UNAUTHORIZED_CLIENT_ERR);
+ }
+ if (!clients.get(clientId).equals(clientSecret)) {
+ throw new AuthenticationException(UNAUTHORIZED_CLIENT_ERR);
+ }
+ }
+
+ @Override
+ public void updated(Dictionary<String, ?> props) throws ConfigurationException {
+ if (props == null) {
+ props = defaults;
+ }
+ reconfig(props);
+ }
+
+ // Reconfigure the client map...
+ private void reconfig(@SuppressWarnings("rawtypes") Dictionary props)
+ throws ConfigurationException {
+ try {
+ String authorizedClients = (String) props.get(CLIENTS);
+ Map<String, String> newClients = new HashMap<>();
+ if (authorizedClients != null) {
+ for (String client : authorizedClients.split(" ")) {
+ String[] aClient = client.split(":");
+ newClients.put(aClient[0], aClient[1]);
+ }
+ }
+ clients.clear();
+ clients.putAll(newClients);
+ } catch (Throwable t) {
+ throw new ConfigurationException(null, CLIENTS_FORMAT_ERR);
+ }
+ }
+
+}
diff --git a/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/EqualUtil.java b/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/EqualUtil.java
new file mode 100644
index 00000000..17204d0e
--- /dev/null
+++ b/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/EqualUtil.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. 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;
+
+/**
+ * Simple class to aide in implementing equals.
+ * <p>
+ *
+ * <em>Arrays are not handled by this class</em>. This is because the
+ * <code>Arrays.equals</code> methods should be used for array fields.
+ */
+public final class EqualUtil {
+ static public boolean areEqual(boolean aThis, boolean aThat) {
+ return aThis == aThat;
+ }
+
+ static public boolean areEqual(char aThis, char aThat) {
+ return aThis == aThat;
+ }
+
+ static public boolean areEqual(long aThis, long aThat) {
+ return aThis == aThat;
+ }
+
+ static public boolean areEqual(float aThis, float aThat) {
+ return Float.floatToIntBits(aThis) == Float.floatToIntBits(aThat);
+ }
+
+ static public boolean areEqual(double aThis, double aThat) {
+ return Double.doubleToLongBits(aThis) == Double.doubleToLongBits(aThat);
+ }
+
+ static public boolean areEqual(Object aThis, Object aThat) {
+ return aThis == null ? aThat == null : aThis.equals(aThat);
+ }
+}
diff --git a/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/HashCodeUtil.java b/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/HashCodeUtil.java
new file mode 100644
index 00000000..c295b3ed
--- /dev/null
+++ b/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/HashCodeUtil.java
@@ -0,0 +1,104 @@
+/*****************************************************************************
+ * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. 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;
+
+import java.lang.reflect.Array;
+
+/**
+ * Collected methods which allow easy implementation of <tt>hashCode</tt>.
+ *
+ * Example use case:
+ *
+ * <pre>
+ * public int hashCode() {
+ * int result = HashCodeUtil.SEED;
+ * // collect the contributions of various fields
+ * result = HashCodeUtil.hash(result, fPrimitive);
+ * result = HashCodeUtil.hash(result, fObject);
+ * result = HashCodeUtil.hash(result, fArray);
+ * return result;
+ * }
+ * </pre>
+ */
+public final class HashCodeUtil {
+
+ /**
+ * An initial value for a <tt>hashCode</tt>, to which is added contributions
+ * from fields. Using a non-zero value decreases collisions of
+ * <tt>hashCode</tt> values.
+ */
+ public static final int SEED = 23;
+
+ /** booleans. */
+ public static int hash(int aSeed, boolean aBoolean) {
+ return firstTerm(aSeed) + (aBoolean ? 1 : 0);
+ }
+
+ /*** chars. */
+ public static int hash(int aSeed, char aChar) {
+ return firstTerm(aSeed) + aChar;
+ }
+
+ /** ints. */
+ public static int hash(int aSeed, int aInt) {
+ return firstTerm(aSeed) + aInt;
+ }
+
+ /** longs. */
+ public static int hash(int aSeed, long aLong) {
+ return firstTerm(aSeed) + (int) (aLong ^ (aLong >>> 32));
+ }
+
+ /** floats. */
+ public static int hash(int aSeed, float aFloat) {
+ return hash(aSeed, Float.floatToIntBits(aFloat));
+ }
+
+ /** doubles. */
+ public static int hash(int aSeed, double aDouble) {
+ return hash(aSeed, Double.doubleToLongBits(aDouble));
+ }
+
+ /**
+ * <tt>aObject</tt> is a possibly-null object field, and possibly an array.
+ *
+ * If <tt>aObject</tt> is an array, then each element may be a primitive or
+ * a possibly-null object.
+ */
+ public static int hash(int aSeed, Object aObject) {
+ int result = aSeed;
+ if (aObject == null) {
+ result = hash(result, 0);
+ } else if (!isArray(aObject)) {
+ result = hash(result, aObject.hashCode());
+ } else {
+ int length = Array.getLength(aObject);
+ for (int idx = 0; idx < length; ++idx) {
+ Object item = Array.get(aObject, idx);
+ // if an item in the array references the array itself, prevent
+ // infinite looping
+ if (!(item == aObject)) {
+ result = hash(result, item);
+ }
+ }
+ }
+ return result;
+ }
+
+ // PRIVATE
+ private static final int fODD_PRIME_NUMBER = 37;
+
+ private static int firstTerm(int aSeed) {
+ return fODD_PRIME_NUMBER * aSeed;
+ }
+
+ private static boolean isArray(Object aObject) {
+ return aObject.getClass().isArray();
+ }
+} \ No newline at end of file
diff --git a/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/PasswordCredentialBuilder.java b/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/PasswordCredentialBuilder.java
new file mode 100644
index 00000000..d8a2e87a
--- /dev/null
+++ b/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/PasswordCredentialBuilder.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. 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;
+
+import static org.opendaylight.aaa.EqualUtil.areEqual;
+import static org.opendaylight.aaa.HashCodeUtil.hash;
+
+import org.opendaylight.aaa.api.PasswordCredentials;
+
+/**
+ * {@link PasswordCredentials} builder.
+ *
+ * @author liemmn
+ *
+ */
+public class PasswordCredentialBuilder {
+ private final MutablePasswordCredentials pc = new MutablePasswordCredentials();
+
+ public PasswordCredentialBuilder setUserName(String username) {
+ pc.username = username;
+ return this;
+ }
+
+ public PasswordCredentialBuilder setPassword(String password) {
+ pc.password = password;
+ return this;
+ }
+
+ public PasswordCredentialBuilder setDomain(String domain) {
+ pc.domain = domain;
+ return this;
+ }
+
+ public PasswordCredentials build() {
+ return pc;
+ }
+
+ private static class MutablePasswordCredentials implements PasswordCredentials {
+ private int hashCode = 0;
+ private String username;
+ private String password;
+ private String domain;
+
+ @Override
+ public String username() {
+ return username;
+ }
+
+ @Override
+ public String password() {
+ return password;
+ }
+
+ @Override
+ public String domain() {
+ return domain;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof PasswordCredentials)) {
+ return false;
+ }
+ PasswordCredentials p = (PasswordCredentials) o;
+ return areEqual(username, p.username()) && areEqual(password, p.password());
+ }
+
+ @Override
+ public int hashCode() {
+ if (hashCode == 0) {
+ int result = HashCodeUtil.SEED;
+ result = hash(result, username);
+ result = hash(result, password);
+ hashCode = result;
+ }
+ return hashCode;
+ }
+ }
+}
diff --git a/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/SecureBlockingQueue.java b/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/SecureBlockingQueue.java
new file mode 100644
index 00000000..3ded52da
--- /dev/null
+++ b/odl-aaa-moon/aaa/aaa-authn/src/main/java/org/opendaylight/aaa/SecureBlockingQueue.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. 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;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+import org.opendaylight.aaa.api.Authentication;
+
+/**
+ * A {@link BlockingQueue} decorator with injected security context.
+ *
+ * @author liemmn
+ *
+ * @param <T>
+ * queue element type
+ */
+public class SecureBlockingQueue<T> implements BlockingQueue<T> {
+ private final BlockingQueue<SecureData<T>> queue;
+
+ /**
+ * Constructor.
+ *
+ * @param queue
+ * blocking queue implementation to use
+ */
+ public SecureBlockingQueue(BlockingQueue<SecureData<T>> queue) {
+ this.queue = queue;
+ }
+
+ @Override
+ public T remove() {
+ return setAuth(queue.remove());
+ }
+
+ @Override
+ public T poll() {
+ return setAuth(queue.poll());
+ }
+
+ @Override
+ public T element() {
+ return setAuth(queue.element());
+ }
+
+ @Override
+ public T peek() {
+ return setAuth(queue.peek());
+ }
+
+ @Override
+ public int size() {
+ return queue.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return queue.isEmpty();
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return new Iterator<T>() {
+ Iterator<SecureData<T>> it = queue.iterator();
+
+ @Override
+ public boolean hasNext() {
+ return it.hasNext();
+ }
+
+ @Override
+ public T next() {
+ return it.next().data;
+ }
+
+ @Override
+ public void remove() {
+ it.remove();
+ }
+ };
+ }
+
+ @Override
+ public Object[] toArray() {
+ return toData().toArray();
+ }
+
+ @SuppressWarnings("hiding")
+ @Override
+ public <T> T[] toArray(T[] a) {
+ return toData().toArray(a);
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> c) {
+ return toData().containsAll(c);
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends T> c) {
+ return queue.addAll(fromData(c));
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ return queue.removeAll(fromData(c));
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c) {
+ return queue.retainAll(fromData(c));
+ }
+
+ @Override
+ public void clear() {
+ queue.clear();
+ }
+
+ @Override
+ public boolean add(T e) {
+ return queue.add(new SecureData<>(e));
+ }
+
+ @Override
+ public boolean offer(T e) {
+ return queue.offer(new SecureData<>(e));
+ }
+
+ @Override
+ public void put(T e) throws InterruptedException {
+ queue.put(new SecureData<T>(e));
+ }
+
+ @Override
+ public boolean offer(T e, long timeout, TimeUnit unit) throws InterruptedException {
+ return queue.offer(new SecureData<>(e), timeout, unit);
+ }
+
+ @Override
+ public T take() throws InterruptedException {
+ return setAuth(queue.take());
+ }
+
+ @Override
+ public T poll(long timeout, TimeUnit unit) throws InterruptedException {
+ return setAuth(queue.poll(timeout, unit));
+ }
+
+ @Override
+ public int remainingCapacity() {
+ return queue.remainingCapacity();
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ Iterator<SecureData<T>> it = queue.iterator();
+ while (it.hasNext()) {
+ SecureData<T> sd = it.next();
+ if (sd.data.equals(o)) {
+ return queue.remove(sd);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ Iterator<SecureData<T>> it = queue.iterator();
+ while (it.hasNext()) {
+ SecureData<T> sd = it.next();
+ if (sd.data.equals(o)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int drainTo(Collection<? super T> c) {
+ Collection<SecureData<T>> sd = new ArrayList<>();
+ int n = queue.drainTo(sd);
+ c.addAll(toData(sd));
+ return n;
+ }
+
+ @Override
+ public int drainTo(Collection<? super T> c, int maxElements) {
+ Collection<SecureData<T>> sd = new ArrayList<>();
+ int n = queue.drainTo(sd, maxElements);
+ c.addAll(toData(sd));
+ return n;
+ }
+
+ // Rehydrate security context
+ private T setAuth(SecureData<T> i) {
+ AuthenticationManager.instance().set(i.auth);
+ return i.data;
+ }
+
+ // Construct secure data collection from a plain old data collection
+ @SuppressWarnings("unchecked")
+ private Collection<SecureData<T>> fromData(Collection<?> c) {
+ Collection<SecureData<T>> sd = new ArrayList<>(c.size());
+ for (Object d : c) {
+ sd.add((SecureData<T>) new SecureData<>(d));
+ }
+ return sd;
+ }
+
+ // Extract the data portion out from the secure data
+ @SuppressWarnings("unchecked")
+ private Collection<T> toData() {
+ return toData(Arrays.<SecureData<T>> asList(queue.toArray(new SecureData[0])));
+ }
+
+ // Extract the data portion out from the secure data
+ private Collection<T> toData(Collection<SecureData<T>> secureData) {
+ Collection<T> data = new ArrayList<>(secureData.size());
+ Iterator<SecureData<T>> it = secureData.iterator();
+ while (it.hasNext()) {
+ data.add(it.next().data);
+ }
+ return data;
+ }
+
+ // Inject security context
+ public static final class SecureData<T> {
+ private final T data;
+ private final Authentication auth;
+
+ private SecureData(T data) {
+ this.data = data;
+ this.auth = AuthenticationManager.instance().get();
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public boolean equals(Object o) {
+ if (o == null) {
+ return false;
+ }
+ return (o instanceof SecureData) ? data.equals(((SecureData) o).data) : false;
+ }
+
+ @Override
+ public int hashCode() {
+ return data.hashCode();
+ }
+ }
+}
diff --git a/odl-aaa-moon/aaa/aaa-authn/src/main/resources/OSGI-INF/metatype/metatype.properties b/odl-aaa-moon/aaa/aaa-authn/src/main/resources/OSGI-INF/metatype/metatype.properties
new file mode 100644
index 00000000..75537f6b
--- /dev/null
+++ b/odl-aaa-moon/aaa/aaa-authn/src/main/resources/OSGI-INF/metatype/metatype.properties
@@ -0,0 +1,12 @@
+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:secrete <client_id:client_secret>
+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. \ No newline at end of file
diff --git a/odl-aaa-moon/aaa/aaa-authn/src/main/resources/OSGI-INF/metatype/metatype.xml b/odl-aaa-moon/aaa/aaa-authn/src/main/resources/OSGI-INF/metatype/metatype.xml
new file mode 100644
index 00000000..10150587
--- /dev/null
+++ b/odl-aaa-moon/aaa/aaa-authn/src/main/resources/OSGI-INF/metatype/metatype.xml
@@ -0,0 +1,16 @@
+<?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
diff --git a/odl-aaa-moon/aaa/aaa-authn/src/main/resources/authn.cfg b/odl-aaa-moon/aaa/aaa-authn/src/main/resources/authn.cfg
new file mode 100644
index 00000000..e7326f86
--- /dev/null
+++ b/odl-aaa-moon/aaa/aaa-authn/src/main/resources/authn.cfg
@@ -0,0 +1,2 @@
+authorizedClients=dlux:secrete
+authEnabled=true \ No newline at end of file
diff --git a/odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/AuthenticationBuilderTest.java b/odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/AuthenticationBuilderTest.java
new file mode 100644
index 00000000..2f69fe5b
--- /dev/null
+++ b/odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/AuthenticationBuilderTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2015 Hewlett-Packard Development Company, L.P. 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.aaa.api.Authentication;
+import org.opendaylight.aaa.api.Claim;
+
+public class AuthenticationBuilderTest {
+ private Set<String> roles = new LinkedHashSet<>(Arrays.asList("role1", "role2"));
+ private Claim validClaim = new ClaimBuilder().setDomain("aName").setUserId("1")
+ .setClientId("2222").setUser("bob").addRole("foo").addRoles(roles).build();
+
+ @Test
+ public void testBuildWithExpiration() {
+ Authentication a1 = new AuthenticationBuilder(validClaim).setExpiration(1).build();
+ assertEquals(1, a1.expiration());
+ assertEquals("aName", a1.domain());
+ assertEquals("1", a1.userId());
+ assertEquals("2222", a1.clientId());
+ assertEquals("bob", a1.user());
+ assertTrue(a1.roles().contains("foo"));
+ assertTrue(a1.roles().containsAll(roles));
+ assertEquals(3, a1.roles().size());
+ Authentication a2 = new AuthenticationBuilder(a1).build();
+ assertNotEquals(a1, a2);
+ Authentication a3 = new AuthenticationBuilder(a1).setExpiration(1).build();
+ assertEquals(a1, a3);
+ }
+
+ @Test
+ public void testBuildWithoutExpiration() {
+ Authentication a1 = new AuthenticationBuilder(validClaim).build();
+ assertEquals(0, a1.expiration());
+ assertEquals("aName", a1.domain());
+ assertEquals("1", a1.userId());
+ assertEquals("2222", a1.clientId());
+ assertEquals("bob", a1.user());
+ assertTrue(a1.roles().contains("foo"));
+ assertTrue(a1.roles().containsAll(roles));
+ assertEquals(3, a1.roles().size());
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testBuildWithNegativeExpiration() {
+ AuthenticationBuilder a1 = new AuthenticationBuilder(validClaim).setExpiration(-1);
+ a1.build();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testBuildWithNullClaim() {
+ AuthenticationBuilder a1 = new AuthenticationBuilder(null);
+ a1.build();
+ }
+
+ @Test
+ public void testToString() {
+ Authentication a1 = new AuthenticationBuilder(validClaim).setExpiration(1).build();
+ assertEquals(
+ "expiration:1,clientId:2222,userId:1,userName:bob,domain:aName,roles:[foo, role1, role2]",
+ a1.toString());
+ }
+
+ @Test
+ public void testEquals() {
+ Authentication a1 = new AuthenticationBuilder(validClaim).setExpiration(1).build();
+ assertTrue(a1.equals(a1));
+ Authentication a2 = new AuthenticationBuilder(a1).setExpiration(1).build();
+ assertTrue(a1.equals(a2));
+ assertTrue(a2.equals(a1));
+ Authentication a3 = new AuthenticationBuilder(validClaim).setExpiration(1).build();
+ assertTrue(a1.equals(a3));
+ assertTrue(a3.equals(a2));
+ assertTrue(a1.equals(a2));
+ }
+
+ @Test
+ public void testNotEquals() {
+ Authentication a1 = new AuthenticationBuilder(validClaim).setExpiration(1).build();
+ assertFalse(a1.equals(null));
+ assertFalse(a1.equals("wrong object"));
+ Authentication a2 = new AuthenticationBuilder(a1).build();
+ assertFalse(a1.equals(a2));
+ assertFalse(a2.equals(a1));
+ Authentication a3 = new AuthenticationBuilder(validClaim).setExpiration(1).build();
+ assertFalse(a1.equals(a2));
+ assertTrue(a1.equals(a3));
+ assertFalse(a2.equals(a3));
+ Authentication a4 = new AuthenticationBuilder(validClaim).setExpiration(9).build();
+ assertFalse(a1.equals(a4));
+ assertFalse(a4.equals(a1));
+ Authentication a5 = new AuthenticationBuilder(a1).setExpiration(9).build();
+ assertFalse(a1.equals(a5));
+ assertFalse(a5.equals(a1));
+ }
+
+ @Test
+ public void testHashCode() {
+ Authentication a1 = new AuthenticationBuilder(validClaim).setExpiration(1).build();
+ assertEquals(a1.hashCode(), a1.hashCode());
+ Authentication a2 = new AuthenticationBuilder(a1).setExpiration(1).build();
+ assertTrue(a1.equals(a2));
+ assertEquals(a1.hashCode(), a2.hashCode());
+ Authentication a3 = new AuthenticationBuilder(validClaim).setExpiration(1).build();
+ assertTrue(a1.equals(a3));
+ assertEquals(a1.hashCode(), a3.hashCode());
+ assertEquals(a2.hashCode(), a3.hashCode());
+ Authentication a4 = new AuthenticationBuilder(a1).setExpiration(9).build();
+ assertFalse(a1.equals(a4));
+ assertNotEquals(a1.hashCode(), a4.hashCode());
+ Authentication a5 = new AuthenticationBuilder(a1).build();
+ assertFalse(a1.equals(a5));
+ assertNotEquals(a1.hashCode(), a5.hashCode());
+ }
+}
diff --git a/odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/AuthenticationManagerTest.java b/odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/AuthenticationManagerTest.java
new file mode 100644
index 00000000..540df287
--- /dev/null
+++ b/odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/AuthenticationManagerTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. 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;
+
+import static org.junit.Assert.assertEquals;
+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 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.junit.Test;
+import org.opendaylight.aaa.api.Authentication;
+import org.opendaylight.aaa.api.AuthenticationService;
+import org.osgi.service.cm.ConfigurationException;
+
+public class AuthenticationManagerTest {
+ @Test
+ public void testAuthenticationCrudSameThread() {
+ Authentication auth = new AuthenticationBuilder(new ClaimBuilder().setUser("Bob")
+ .setUserId("1234").addRole("admin").addRole("guest").build()).build();
+ AuthenticationService as = AuthenticationManager.instance();
+
+ assertNotNull(as);
+
+ as.set(auth);
+ assertEquals(auth, as.get());
+
+ as.clear();
+ assertNull(as.get());
+ }
+
+ @Test
+ public void testAuthenticationCrudSpawnedThread() throws InterruptedException,
+ ExecutionException {
+ AuthenticationService as = AuthenticationManager.instance();
+ Authentication auth = new AuthenticationBuilder(new ClaimBuilder().setUser("Bob")
+ .setUserId("1234").addRole("admin").addRole("guest").build()).build();
+
+ as.set(auth);
+ Future<Authentication> f = Executors.newSingleThreadExecutor().submit(new Worker());
+ assertEquals(auth, f.get());
+
+ as.clear();
+ f = Executors.newSingleThreadExecutor().submit(new Worker());
+ assertNull(f.get());
+ }
+
+ @Test
+ public void testAuthenticationCrudSpawnedThreadPool() throws InterruptedException,
+ ExecutionException {
+ AuthenticationService as = AuthenticationManager.instance();
+ Authentication auth = new AuthenticationBuilder(new ClaimBuilder().setUser("Bob")
+ .setUserId("1234").addRole("admin").addRole("guest").build()).build();
+
+ as.set(auth);
+ List<Future<Authentication>> fs = Executors.newFixedThreadPool(2).invokeAll(
+ Arrays.asList(new Worker(), new Worker()));
+ for (Future<Authentication> f : fs) {
+ assertEquals(auth, f.get());
+ }
+
+ as.clear();
+ fs = Executors.newFixedThreadPool(2).invokeAll(Arrays.asList(new Worker(), new Worker()));
+ for (Future<Authentication> f : fs) {
+ assertNull(f.get());
+ }
+ }
+
+ @Test
+ public void testUpdatedValid() throws ConfigurationException {
+ Dictionary<String, String> props = new Hashtable<>();
+ AuthenticationManager as = AuthenticationManager.instance();
+
+ assertFalse(as.isAuthEnabled());
+
+ props.put(AuthenticationManager.AUTH_ENABLED, "TrUe");
+ as.updated(props);
+ assertTrue(as.isAuthEnabled());
+
+ props.put(AuthenticationManager.AUTH_ENABLED, "FaLsE");
+ as.updated(props);
+ assertFalse(as.isAuthEnabled());
+ }
+
+ @Test
+ public void testUpdatedNullProperty() throws ConfigurationException {
+ AuthenticationManager as = AuthenticationManager.instance();
+
+ assertFalse(as.isAuthEnabled());
+ as.updated(null);
+ assertFalse(as.isAuthEnabled());
+ }
+
+ @Test(expected = ConfigurationException.class)
+ public void testUpdatedInvalidValue() throws ConfigurationException {
+ AuthenticationManager as = AuthenticationManager.instance();
+ Dictionary<String, String> props = new Hashtable<>();
+
+ props.put(AuthenticationManager.AUTH_ENABLED, "yes");
+ as.updated(props);
+ }
+
+ @Test(expected = ConfigurationException.class)
+ public void testUpdatedInvalidKey() throws ConfigurationException {
+ AuthenticationManager as = AuthenticationManager.instance();
+ Dictionary<String, String> props = new Hashtable<>();
+
+ props.put("Invalid Key", "true");
+ as.updated(props);
+ }
+
+ private class Worker implements Callable<Authentication> {
+ @Override
+ public Authentication call() throws Exception {
+ AuthenticationService as = AuthenticationManager.instance();
+ return as.get();
+ }
+ }
+} \ No newline at end of file
diff --git a/odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/ClaimBuilderTest.java b/odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/ClaimBuilderTest.java
new file mode 100644
index 00000000..372eb6d2
--- /dev/null
+++ b/odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/ClaimBuilderTest.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import org.junit.Test;
+import org.opendaylight.aaa.api.Claim;
+
+/**
+ *
+ * @author liemmn
+ *
+ */
+public class ClaimBuilderTest {
+ @Test
+ public void testBuildWithAll() {
+ Claim c1 = new ClaimBuilder().setClientId("dlux").setDomain("pepsi").setUser("john")
+ .setUserId("1234").addRole("foo").addRole("foo2")
+ .addRoles(new HashSet<>(Arrays.asList("foo", "bar"))).build();
+ assertEquals("dlux", c1.clientId());
+ assertEquals("pepsi", c1.domain());
+ assertEquals("john", c1.user());
+ assertEquals("1234", c1.userId());
+ assertTrue(c1.roles().contains("foo"));
+ assertTrue(c1.roles().contains("foo2"));
+ assertTrue(c1.roles().contains("bar"));
+ assertEquals(3, c1.roles().size());
+ Claim c2 = new ClaimBuilder(c1).build();
+ assertEquals(c1, c2);
+ }
+
+ @Test
+ public void testBuildWithRequired() {
+ Claim c1 = new ClaimBuilder().setUser("john").setUserId("1234").addRole("foo").build();
+ assertEquals("john", c1.user());
+ assertEquals("1234", c1.userId());
+ assertTrue(c1.roles().contains("foo"));
+ assertEquals(1, c1.roles().size());
+ assertEquals("", c1.domain());
+ assertEquals("", c1.clientId());
+ }
+
+ @Test
+ public void testBuildWithEmptyOptional() {
+ Claim c1 = new ClaimBuilder().setDomain(" ").setClientId(" ").setUser("john")
+ .setUserId("1234").addRole("foo").build();
+ assertEquals("", c1.domain());
+ assertEquals("", c1.clientId());
+ assertEquals("john", c1.user());
+ assertEquals("1234", c1.userId());
+ assertTrue(c1.roles().contains("foo"));
+ assertEquals(1, c1.roles().size());
+ }
+
+ @Test
+ public void testBuildWithNullOptional() {
+ Claim c1 = new ClaimBuilder().setDomain(null).setClientId(null).setUser("john")
+ .setUserId("1234").addRole("foo").build();
+ assertEquals("", c1.domain());
+ assertEquals("", c1.clientId());
+ assertEquals("john", c1.user());
+ assertEquals("1234", c1.userId());
+ assertTrue(c1.roles().contains("foo"));
+ assertEquals(1, c1.roles().size());
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testBuildWithDefault() {
+ ClaimBuilder c1 = new ClaimBuilder();
+ c1.build();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testBuildWithoutUser() {
+ ClaimBuilder c1 = new ClaimBuilder().setUserId("1234").addRole("foo");
+ c1.build();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testBuildWithNullUser() {
+ ClaimBuilder c1 = new ClaimBuilder().setUser(null).setUserId("1234").addRole("foo");
+ c1.build();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testBuildWithEmptyUser() {
+ ClaimBuilder c1 = new ClaimBuilder().setUser(" ").setUserId("1234").addRole("foo");
+ c1.build();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testBuildWithoutUserId() {
+ ClaimBuilder c1 = new ClaimBuilder().setUser("john").addRole("foo");
+ c1.build();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testBuildWithNullUserId() {
+ ClaimBuilder c1 = new ClaimBuilder().setUser("john").setUserId(null).addRole("foo");
+ c1.build();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testBuildWithEmptyUserId() {
+ ClaimBuilder c1 = new ClaimBuilder().setUser("john").setUserId(" ").addRole("foo");
+ c1.build();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testBuildWithoutRole() {
+ ClaimBuilder c1 = new ClaimBuilder().setUser("john").setUserId("1234");
+ c1.build();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testBuildWithNullRole() {
+ ClaimBuilder c1 = new ClaimBuilder().setUser("john").setUserId("1234").addRole(null);
+ c1.build();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testBuildWithEmptyRole() {
+ ClaimBuilder c1 = new ClaimBuilder().setUser("john").setUserId("1234").addRole(" ");
+ c1.build();
+ }
+
+ @Test
+ public void testEquals() {
+ Claim c1 = new ClaimBuilder().setClientId("dlux").setDomain("pepsi").setUser("john")
+ .setUserId("1234").addRole("foo").build();
+ assertTrue(c1.equals(c1));
+ Claim c2 = new ClaimBuilder(c1).addRole("foo").build();
+ assertTrue(c1.equals(c2));
+ assertTrue(c2.equals(c1));
+ Claim c3 = new ClaimBuilder().setClientId("dlux").setDomain("pepsi").setUser("john")
+ .setUserId("1234").addRole("foo").build();
+ assertTrue(c1.equals(c3));
+ assertTrue(c3.equals(c2));
+ assertTrue(c1.equals(c2));
+ }
+
+ @Test
+ public void testNotEquals() {
+ Claim c1 = new ClaimBuilder().setClientId("dlux").setDomain("pepsi").setUser("john")
+ .setUserId("1234").addRole("foo").build();
+ assertFalse(c1.equals(null));
+ assertFalse(c1.equals("wrong object"));
+ Claim c2 = new ClaimBuilder(c1).addRoles(new HashSet<>(Arrays.asList("foo", "bar")))
+ .build();
+ assertEquals(2, c2.roles().size());
+ assertFalse(c1.equals(c2));
+ assertFalse(c2.equals(c1));
+ Claim c3 = new ClaimBuilder().setClientId("dlux").setDomain("pepsi").setUser("john")
+ .setUserId("1234").addRole("foo").build();
+ assertFalse(c1.equals(c2));
+ assertTrue(c1.equals(c3));
+ assertFalse(c2.equals(c3));
+ Claim c5 = new ClaimBuilder().setUser("john").setUserId("1234").addRole("foo").build();
+ assertFalse(c1.equals(c5));
+ assertFalse(c5.equals(c1));
+ }
+
+ @Test
+ public void testHash() {
+ Claim c1 = new ClaimBuilder().setClientId("dlux").setDomain("pepsi").setUser("john")
+ .setUserId("1234").addRole("foo").build();
+ assertEquals(c1.hashCode(), c1.hashCode());
+ Claim c2 = new ClaimBuilder(c1).addRole("foo").build();
+ assertTrue(c1.equals(c2));
+ assertEquals(c1.hashCode(), c2.hashCode());
+ Claim c3 = new ClaimBuilder(c1).addRoles(new HashSet<>(Arrays.asList("foo", "bar")))
+ .build();
+ assertFalse(c1.equals(c3));
+ assertNotEquals(c1.hashCode(), c3.hashCode());
+ Claim c4 = new ClaimBuilder().setClientId("dlux").setDomain("pepsi").setUser("john")
+ .setUserId("1234").addRole("foo").build();
+ assertTrue(c1.equals(c4));
+ assertEquals(c1.hashCode(), c4.hashCode());
+ assertEquals(c2.hashCode(), c4.hashCode());
+ Claim c5 = new ClaimBuilder().setUser("john").setUserId("1234").addRole("foo").build();
+ assertFalse(c1.equals(c5));
+ assertNotEquals(c1.hashCode(), c5.hashCode());
+ }
+
+ @Test
+ public void testToString() {
+ Claim c1 = new ClaimBuilder().setUser("john").setUserId("1234").addRole("foo").build();
+ assertEquals("clientId:,userId:1234,userName:john,domain:,roles:[foo]", c1.toString());
+ c1 = new ClaimBuilder(c1).setClientId("dlux").setDomain("pepsi").build();
+ assertEquals("clientId:dlux,userId:1234,userName:john,domain:pepsi,roles:[foo]",
+ c1.toString());
+ c1 = new ClaimBuilder(c1).addRole("bar").build();
+ assertEquals("clientId:dlux,userId:1234,userName:john,domain:pepsi,roles:[foo, bar]",
+ c1.toString());
+ }
+} \ No newline at end of file
diff --git a/odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/ClientManagerTest.java b/odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/ClientManagerTest.java
new file mode 100644
index 00000000..059ba9a3
--- /dev/null
+++ b/odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/ClientManagerTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. 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;
+
+import static org.junit.Assert.fail;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.aaa.api.AuthenticationException;
+import org.osgi.service.cm.ConfigurationException;
+
+/**
+ *
+ * @author liemmn
+ *
+ */
+public class ClientManagerTest {
+ private static final ClientManager cm = new ClientManager();
+
+ @Before
+ public void setup() throws ConfigurationException {
+ cm.init(null);
+ }
+
+ @Test
+ public void testValidate() {
+ cm.validate("dlux", "secrete");
+ }
+
+ @Test(expected = AuthenticationException.class)
+ public void testFailValidate() {
+ cm.validate("dlux", "what?");
+ }
+
+ @Test
+ public void testUpdate() throws ConfigurationException {
+ Dictionary<String, String> configs = new Hashtable<>();
+ configs.put(ClientManager.CLIENTS, "aws:amazon dlux:xxx");
+ cm.updated(configs);
+ cm.validate("aws", "amazon");
+ cm.validate("dlux", "xxx");
+ }
+
+ @Test
+ public void testFailUpdate() {
+ Dictionary<String, String> configs = new Hashtable<>();
+ configs.put(ClientManager.CLIENTS, "aws:amazon dlux");
+ try {
+ cm.updated(configs);
+ fail("Shoulda failed updating bad configuration");
+ } catch (ConfigurationException ce) {
+ // Expected
+ }
+ cm.validate("dlux", "secrete");
+ try {
+ cm.validate("aws", "amazon");
+ fail("Shoulda failed updating bad configuration");
+ } catch (AuthenticationException ae) {
+ // Expected
+ }
+ }
+}
diff --git a/odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/PasswordCredentialTest.java b/odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/PasswordCredentialTest.java
new file mode 100644
index 00000000..2dabb77b
--- /dev/null
+++ b/odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/PasswordCredentialTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. 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;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.HashSet;
+import org.junit.Test;
+import org.opendaylight.aaa.api.PasswordCredentials;
+
+public class PasswordCredentialTest {
+
+ @Test
+ public void testBuilder() {
+ PasswordCredentials pc1 = new PasswordCredentialBuilder().setUserName("bob")
+ .setPassword("secrete").build();
+ assertEquals("bob", pc1.username());
+ assertEquals("secrete", pc1.password());
+
+ PasswordCredentials pc2 = new PasswordCredentialBuilder().setUserName("bob")
+ .setPassword("secrete").build();
+ assertEquals(pc1, pc2);
+
+ PasswordCredentials pc3 = new PasswordCredentialBuilder().setUserName("bob")
+ .setPassword("secret").build();
+ HashSet<PasswordCredentials> pcs = new HashSet<>();
+ pcs.add(pc1);
+ pcs.add(pc2);
+ pcs.add(pc3);
+ assertEquals(2, pcs.size());
+ }
+
+}
diff --git a/odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/SecureBlockingQueueTest.java b/odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/SecureBlockingQueueTest.java
new file mode 100644
index 00000000..16627d9f
--- /dev/null
+++ b/odl-aaa-moon/aaa/aaa-authn/src/test/java/org/opendaylight/aaa/SecureBlockingQueueTest.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.aaa.SecureBlockingQueue.SecureData;
+import org.opendaylight.aaa.api.Authentication;
+
+public class SecureBlockingQueueTest {
+ private final int MAX_TASKS = 100;
+
+ @Before
+ public void setup() {
+ AuthenticationManager.instance().clear();
+ }
+
+ @Test
+ public void testSecureThreadPoolExecutor() throws InterruptedException, ExecutionException {
+ BlockingQueue<Runnable> queue = new SecureBlockingQueue<>(
+ new ArrayBlockingQueue<SecureData<Runnable>>(10));
+ ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 500, TimeUnit.MILLISECONDS,
+ queue);
+ executor.prestartAllCoreThreads();
+ for (int cnt = 1; cnt <= MAX_TASKS; cnt++) {
+ assertEquals(Integer.toString(cnt),
+ executor.submit(new Task(Integer.toString(cnt), "1111", "user")).get().user());
+ }
+ executor.shutdown();
+ }
+
+ @Test
+ public void testNormalThreadPoolExecutor() throws InterruptedException, ExecutionException {
+ BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(10);
+ ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 500, TimeUnit.MILLISECONDS,
+ queue);
+ executor.prestartAllCoreThreads();
+ for (int cnt = 1; cnt <= MAX_TASKS; cnt++) {
+ assertNull(executor.submit(new Task(Integer.toString(cnt), "1111", "user")).get());
+ }
+ executor.shutdown();
+ }
+
+ @Test
+ public void testQueueOps() throws InterruptedException, ExecutionException {
+ BlockingQueue<String> queue = new SecureBlockingQueue<>(
+ new ArrayBlockingQueue<SecureData<String>>(3));
+ ExecutorService es = Executors.newFixedThreadPool(3);
+ es.submit(new Producer("foo", "1111", "user", queue)).get();
+ assertEquals(1, queue.size());
+ assertEquals("foo", es.submit(new Consumer(queue)).get());
+ es.submit(new Producer("bar", "2222", "user", queue)).get();
+ assertEquals("bar", queue.peek());
+ assertEquals("bar", queue.element());
+ assertEquals(1, queue.size());
+ assertEquals("bar", queue.poll());
+ assertTrue(queue.isEmpty());
+ es.shutdown();
+ }
+
+ @Test
+ public void testCollectionOps() throws InterruptedException, ExecutionException {
+ BlockingQueue<String> queue = new SecureBlockingQueue<>(
+ new ArrayBlockingQueue<SecureData<String>>(6));
+ for (int i = 1; i <= 3; i++)
+ queue.add("User" + i);
+ Iterator<String> it = queue.iterator();
+ while (it.hasNext())
+ assertTrue(it.next().startsWith("User"));
+ assertEquals(3, queue.toArray().length);
+ List<String> actual = Arrays.asList(queue.toArray(new String[0]));
+ assertEquals("User1", actual.iterator().next());
+ assertTrue(queue.containsAll(actual));
+ queue.addAll(actual);
+ assertEquals(6, queue.size());
+ queue.retainAll(Arrays.asList(new String[] { "User2" }));
+ assertEquals(2, queue.size());
+ assertEquals("User2", queue.iterator().next());
+ queue.removeAll(actual);
+ assertTrue(queue.isEmpty());
+ queue.add("hello");
+ assertEquals(1, queue.size());
+ queue.clear();
+ assertTrue(queue.isEmpty());
+ }
+
+ @Test
+ public void testBlockingQueueOps() throws InterruptedException {
+ BlockingQueue<String> queue = new SecureBlockingQueue<>(
+ new ArrayBlockingQueue<SecureData<String>>(3));
+ queue.offer("foo");
+ assertEquals(1, queue.size());
+ queue.offer("bar", 500, TimeUnit.MILLISECONDS);
+ assertEquals(2, queue.size());
+ assertEquals("foo", queue.poll());
+ assertTrue(queue.contains("bar"));
+ queue.remove("bar");
+ assertEquals(3, queue.remainingCapacity());
+ queue.addAll(Arrays.asList(new String[] { "foo", "bar", "tom" }));
+ assertEquals(3, queue.size());
+ assertEquals("foo", queue.poll(500, TimeUnit.MILLISECONDS));
+ assertEquals(2, queue.size());
+ List<String> drain = new LinkedList<>();
+ queue.drainTo(drain);
+ assertTrue(queue.isEmpty());
+ assertEquals(2, drain.size());
+ queue.addAll(Arrays.asList(new String[] { "foo", "bar", "tom" }));
+ drain.clear();
+ queue.drainTo(drain, 1);
+ assertEquals(2, queue.size());
+ assertEquals(1, drain.size());
+ }
+
+ // Task to run in a ThreadPoolExecutor
+ private class Task implements Callable<Authentication> {
+ Task(String name, String userId, String role) {
+ // Mock that each task has its original authentication context
+ AuthenticationManager.instance().set(
+ new AuthenticationBuilder(new ClaimBuilder().setUser(name).setUserId(userId)
+ .addRole(role).build()).build());
+ }
+
+ @Override
+ public Authentication call() throws Exception {
+ return AuthenticationManager.instance().get();
+ }
+ }
+
+ // Producer sets auth context
+ private class Producer implements Callable<String> {
+ private final String name;
+ private final String userId;
+ private final String role;
+ private final BlockingQueue<String> queue;
+
+ Producer(String name, String userId, String role, BlockingQueue<String> queue) {
+ this.name = name;
+ this.userId = userId;
+ this.role = role;
+ this.queue = queue;
+ }
+
+ @Override
+ public String call() throws InterruptedException {
+ AuthenticationManager.instance().set(
+ new AuthenticationBuilder(new ClaimBuilder().setUser(name).setUserId(userId)
+ .addRole(role).build()).build());
+ queue.put(name);
+ return name;
+ }
+ }
+
+ // Consumer gets producer's auth context via data element in queue
+ private class Consumer implements Callable<String> {
+ private final BlockingQueue<String> queue;
+
+ Consumer(BlockingQueue<String> queue) {
+ this.queue = queue;
+ }
+
+ @Override
+ public String call() {
+ queue.remove();
+ Authentication auth = AuthenticationManager.instance().get();
+ return (auth == null) ? null : auth.user();
+ }
+ }
+
+}