From e63b03f3d7e4851e008e4bb4d184982c2c0bd229 Mon Sep 17 00:00:00 2001 From: WuKong Date: Tue, 24 May 2016 17:13:17 +0200 Subject: odl/aaa clone Change-Id: I2b72c16aa3245e02d985a2c6189aacee7caad36e Signed-off-by: WuKong --- .../main/java/org/opendaylight/aaa/Activator.java | 51 ++++ .../opendaylight/aaa/AuthenticationBuilder.java | 122 ++++++++++ .../opendaylight/aaa/AuthenticationManager.java | 77 ++++++ .../java/org/opendaylight/aaa/ClaimBuilder.java | 160 +++++++++++++ .../java/org/opendaylight/aaa/ClientManager.java | 88 +++++++ .../main/java/org/opendaylight/aaa/EqualUtil.java | 42 ++++ .../java/org/opendaylight/aaa/HashCodeUtil.java | 104 +++++++++ .../aaa/PasswordCredentialBuilder.java | 87 +++++++ .../org/opendaylight/aaa/SecureBlockingQueue.java | 258 +++++++++++++++++++++ 9 files changed, 989 insertions(+) create mode 100644 odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight/aaa/Activator.java create mode 100644 odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight/aaa/AuthenticationBuilder.java create mode 100644 odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight/aaa/AuthenticationManager.java create mode 100644 odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight/aaa/ClaimBuilder.java create mode 100644 odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight/aaa/ClientManager.java create mode 100644 odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight/aaa/EqualUtil.java create mode 100644 odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight/aaa/HashCodeUtil.java create mode 100644 odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight/aaa/PasswordCredentialBuilder.java create mode 100644 odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight/aaa/SecureBlockingQueue.java (limited to 'odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight') diff --git a/odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight/aaa/Activator.java b/odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight/aaa/Activator.java new file mode 100644 index 00000000..cfe27ef0 --- /dev/null +++ b/odl-aaa-moon/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 addPid(Dictionary dict) { + dict.put(Constants.SERVICE_PID, AUTHN_PID); + return dict; + } +} diff --git a/odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight/aaa/AuthenticationBuilder.java b/odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight/aaa/AuthenticationBuilder.java new file mode 100644 index 00000000..948cbac6 --- /dev/null +++ b/odl-aaa-moon/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 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-authn/src/main/java/org/opendaylight/aaa/AuthenticationManager.java b/odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight/aaa/AuthenticationManager.java new file mode 100644 index 00000000..5f6420a3 --- /dev/null +++ b/odl-aaa-moon/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 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 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 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-authn/src/main/java/org/opendaylight/aaa/ClaimBuilder.java b/odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight/aaa/ClaimBuilder.java new file mode 100644 index 00000000..4e4a8ef3 --- /dev/null +++ b/odl-aaa-moon/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 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 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 roles; + + protected ImmutableClaim(ClaimBuilder base) { + clientId = base.clientId; + userId = base.userId; + user = base.user; + domain = base.domain; + roles = ImmutableSet. 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 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-authn/src/main/java/org/opendaylight/aaa/ClientManager.java b/odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight/aaa/ClientManager.java new file mode 100644 index 00000000..e7e51424 --- /dev/null +++ b/odl-aaa-moon/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 :"; + private static final String UNAUTHORIZED_CLIENT_ERR = "Unauthorized client"; + + // Defaults (needed only for non-Karaf deployments) + static final Dictionary defaults = new Hashtable<>(); + static { + defaults.put(CLIENTS, "dlux:secrete"); + } + + private final Map 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 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 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-authn/src/main/java/org/opendaylight/aaa/EqualUtil.java b/odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight/aaa/EqualUtil.java new file mode 100644 index 00000000..17204d0e --- /dev/null +++ b/odl-aaa-moon/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. + *

+ * + * Arrays are not handled by this class. This is because the + * Arrays.equals 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-authn/src/main/java/org/opendaylight/aaa/HashCodeUtil.java b/odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight/aaa/HashCodeUtil.java new file mode 100644 index 00000000..c295b3ed --- /dev/null +++ b/odl-aaa-moon/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 hashCode. + * + * Example use case: + * + *

+ * 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;
+ * }
+ * 
+ */ +public final class HashCodeUtil { + + /** + * An initial value for a hashCode, to which is added contributions + * from fields. Using a non-zero value decreases collisions of + * hashCode 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)); + } + + /** + * aObject is a possibly-null object field, and possibly an array. + * + * If aObject 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-authn/src/main/java/org/opendaylight/aaa/PasswordCredentialBuilder.java b/odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight/aaa/PasswordCredentialBuilder.java new file mode 100644 index 00000000..d8a2e87a --- /dev/null +++ b/odl-aaa-moon/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-authn/src/main/java/org/opendaylight/aaa/SecureBlockingQueue.java b/odl-aaa-moon/aaa-authn/src/main/java/org/opendaylight/aaa/SecureBlockingQueue.java new file mode 100644 index 00000000..3ded52da --- /dev/null +++ b/odl-aaa-moon/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 + * queue element type + */ +public class SecureBlockingQueue implements BlockingQueue { + private final BlockingQueue> queue; + + /** + * Constructor. + * + * @param queue + * blocking queue implementation to use + */ + public SecureBlockingQueue(BlockingQueue> 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 iterator() { + return new Iterator() { + Iterator> 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[] toArray(T[] a) { + return toData().toArray(a); + } + + @Override + public boolean containsAll(Collection c) { + return toData().containsAll(c); + } + + @Override + public boolean addAll(Collection 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(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> it = queue.iterator(); + while (it.hasNext()) { + SecureData sd = it.next(); + if (sd.data.equals(o)) { + return queue.remove(sd); + } + } + return false; + } + + @Override + public boolean contains(Object o) { + Iterator> it = queue.iterator(); + while (it.hasNext()) { + SecureData sd = it.next(); + if (sd.data.equals(o)) { + return true; + } + } + return false; + } + + @Override + public int drainTo(Collection c) { + Collection> sd = new ArrayList<>(); + int n = queue.drainTo(sd); + c.addAll(toData(sd)); + return n; + } + + @Override + public int drainTo(Collection c, int maxElements) { + Collection> sd = new ArrayList<>(); + int n = queue.drainTo(sd, maxElements); + c.addAll(toData(sd)); + return n; + } + + // Rehydrate security context + private T setAuth(SecureData i) { + AuthenticationManager.instance().set(i.auth); + return i.data; + } + + // Construct secure data collection from a plain old data collection + @SuppressWarnings("unchecked") + private Collection> fromData(Collection c) { + Collection> sd = new ArrayList<>(c.size()); + for (Object d : c) { + sd.add((SecureData) new SecureData<>(d)); + } + return sd; + } + + // Extract the data portion out from the secure data + @SuppressWarnings("unchecked") + private Collection toData() { + return toData(Arrays.> asList(queue.toArray(new SecureData[0]))); + } + + // Extract the data portion out from the secure data + private Collection toData(Collection> secureData) { + Collection data = new ArrayList<>(secureData.size()); + Iterator> it = secureData.iterator(); + while (it.hasNext()) { + data.add(it.next().data); + } + return data; + } + + // Inject security context + public static final class SecureData { + 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(); + } + } +} -- cgit 1.2.3-korg