git-svn-id: https://svn.apache.org/repos/asf/activemq/trunk@1486063 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Timothy A. Bish 2013-05-24 14:10:23 +00:00
parent 75245da626
commit 02a58c2a34
2 changed files with 422 additions and 367 deletions

View File

@ -16,37 +16,57 @@
*/ */
package org.apache.activemq.security; package org.apache.activemq.security;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.InvalidNameException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.event.EventDirContext;
import javax.naming.event.NamespaceChangeListener;
import javax.naming.event.NamingEvent;
import javax.naming.event.NamingExceptionEvent;
import javax.naming.event.ObjectChangeListener;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import org.apache.activemq.command.ActiveMQDestination; import org.apache.activemq.command.ActiveMQDestination;
import org.apache.activemq.command.ActiveMQQueue; import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic; import org.apache.activemq.command.ActiveMQTopic;
import org.apache.activemq.filter.DestinationMapEntry; import org.apache.activemq.filter.DestinationMapEntry;
import org.apache.activemq.jaas.GroupPrincipal;
import org.apache.activemq.jaas.UserPrincipal; import org.apache.activemq.jaas.UserPrincipal;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.naming.*; public class SimpleCachedLDAPAuthorizationMap implements AuthorizationMap {
import javax.naming.directory.*;
import javax.naming.event.*;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import java.util.*;
/**
*/
public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
private static final Logger LOG = LoggerFactory.getLogger(SimpleCachedLDAPAuthorizationMap.class); private static final Logger LOG = LoggerFactory.getLogger(SimpleCachedLDAPAuthorizationMap.class);
// Configuration Options // Configuration Options
private String initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory"; private final String initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
private String connectionURL = "ldap://localhost:1024"; private String connectionURL = "ldap://localhost:1024";
private String connectionUsername = "uid=admin,ou=system"; private String connectionUsername = "uid=admin,ou=system";
private String connectionPassword = "secret"; private String connectionPassword = "secret";
private String connectionProtocol = "s"; private String connectionProtocol = "s";
private String authentication = "simple"; private String authentication = "simple";
private int queuePrefixLength = 4; private int queuePrefixLength = 4;
private int topicPrefixLength = 4; private int topicPrefixLength = 4;
private int tempPrefixLength = 4; private int tempPrefixLength = 4;
@ -55,7 +75,6 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
private String topicSearchBase = "ou=Topic,ou=Destination,ou=ActiveMQ,ou=system"; private String topicSearchBase = "ou=Topic,ou=Destination,ou=ActiveMQ,ou=system";
private String tempSearchBase = "ou=Temp,ou=Destination,ou=ActiveMQ,ou=system"; private String tempSearchBase = "ou=Temp,ou=Destination,ou=ActiveMQ,ou=system";
private String permissionGroupMemberAttribute = "member"; private String permissionGroupMemberAttribute = "member";
private String adminPermissionGroupSearchFilter = "(cn=Admin)"; private String adminPermissionGroupSearchFilter = "(cn=Admin)";
@ -68,7 +87,6 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
private String groupNameAttribute = "cn"; private String groupNameAttribute = "cn";
private String userNameAttribute = "uid"; private String userNameAttribute = "uid";
private int refreshInterval = -1; private int refreshInterval = -1;
private boolean refreshDisabled = false; private boolean refreshDisabled = false;
@ -80,8 +98,27 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
protected DirContext context; protected DirContext context;
private EventDirContext eventContext; private EventDirContext eventContext;
protected HashMap<ActiveMQDestination, AuthorizationEntry> entries = private final AtomicReference<DefaultAuthorizationMap> map =
new HashMap<ActiveMQDestination, AuthorizationEntry>(); new AtomicReference<DefaultAuthorizationMap>(new DefaultAuthorizationMap());
private final ThreadPoolExecutor updaterService;
protected Map<ActiveMQDestination, AuthorizationEntry> entries =
new ConcurrentHashMap<ActiveMQDestination, AuthorizationEntry>();
public SimpleCachedLDAPAuthorizationMap() {
// Allow for only a couple outstanding update requests, they can be slow so we
// don't want a bunch to pile up for no reason.
updaterService = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(2),
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "SimpleCachedLDAPAuthorizationMap update thread");
}
});
updaterService.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
}
protected DirContext createContext() throws NamingException { protected DirContext createContext() throws NamingException {
Hashtable<String, String> env = new Hashtable<String, String>(); Hashtable<String, String> env = new Hashtable<String, String>();
@ -104,19 +141,20 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
try { try {
context.getAttributes(""); context.getAttributes("");
alive = true; alive = true;
} catch (Exception e) {} } catch (Exception e) {
}
} }
return alive; return alive;
} }
/** /**
* Returns the existing open context or creates a new one and registers listeners for * Returns the existing open context or creates a new one and registers listeners for push notifications if such an
* push notifications if such an update style is enabled. This implementation should not * update style is enabled. This implementation should not be invoked concurrently.
* be invoked concurrently.
* *
* @return the current context * @return the current context
* *
* @throws NamingException if there is an error setting things up * @throws NamingException
* if there is an error setting things up
*/ */
protected DirContext open() throws NamingException { protected DirContext open() throws NamingException {
if (isContextAlive()) { if (isContextAlive()) {
@ -139,8 +177,8 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
this.new CachedLDAPAuthorizationMapNamespaceChangeListener(DestinationType.QUEUE, permissionType)); this.new CachedLDAPAuthorizationMapNamespaceChangeListener(DestinationType.QUEUE, permissionType));
} }
// Listener for changes to the destination pattern entry itself and not a permission entry. // Listener for changes to the destination pattern entry itself and not a permission entry.
eventContext.addNamingListener(queueSearchBase, "cn=*", new SearchControls(), eventContext.addNamingListener(queueSearchBase, "cn=*", new SearchControls(), this.new CachedLDAPAuthorizationMapNamespaceChangeListener(
this.new CachedLDAPAuthorizationMapNamespaceChangeListener(DestinationType.QUEUE, null)); DestinationType.QUEUE, null));
// Listeners for Topic policy // // Listeners for Topic policy //
@ -150,8 +188,8 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
this.new CachedLDAPAuthorizationMapNamespaceChangeListener(DestinationType.TOPIC, permissionType)); this.new CachedLDAPAuthorizationMapNamespaceChangeListener(DestinationType.TOPIC, permissionType));
} }
// Listener for changes to the destination pattern entry itself and not a permission entry. // Listener for changes to the destination pattern entry itself and not a permission entry.
eventContext.addNamingListener(topicSearchBase, "cn=*", new SearchControls(), eventContext.addNamingListener(topicSearchBase, "cn=*", new SearchControls(), this.new CachedLDAPAuthorizationMapNamespaceChangeListener(
this.new CachedLDAPAuthorizationMapNamespaceChangeListener(DestinationType.TOPIC, null)); DestinationType.TOPIC, null));
// Listeners for Temp policy // // Listeners for Temp policy //
@ -171,10 +209,11 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
} }
/** /**
* Queries the directory and initializes the policy based on the data in the directory. * Queries the directory and initializes the policy based on the data in the directory. This implementation should
* This implementation should not be invoked concurrently. * not be invoked concurrently.
* *
* @throws Exception if there is an unrecoverable error processing the directory contents * @throws Exception
* if there is an unrecoverable error processing the directory contents
*/ */
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
protected void query() throws Exception { protected void query() throws Exception {
@ -183,65 +222,72 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
final SearchControls constraints = new SearchControls(); final SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
DefaultAuthorizationMap newMap = new DefaultAuthorizationMap();
for (PermissionType permissionType : PermissionType.values()) { for (PermissionType permissionType : PermissionType.values()) {
try { try {
processQueryResults( processQueryResults(newMap,
currentContext.search(queueSearchBase, getFilterForPermissionType(permissionType), constraints), currentContext.search(queueSearchBase, getFilterForPermissionType(permissionType),
DestinationType.QUEUE, permissionType); constraints), DestinationType.QUEUE, permissionType);
} catch (Exception e) { } catch (Exception e) {
LOG.error("Policy not applied!. Error processing policy under '" + queueSearchBase + "' with filter '" LOG.error("Policy not applied!. Error processing policy under '" + queueSearchBase +
+ getFilterForPermissionType(permissionType) + "'", e); "' with filter '" + getFilterForPermissionType(permissionType) + "'", e);
} }
} }
for (PermissionType permissionType : PermissionType.values()) { for (PermissionType permissionType : PermissionType.values()) {
try { try {
processQueryResults( processQueryResults(newMap,
currentContext.search(topicSearchBase, getFilterForPermissionType(permissionType), constraints), currentContext.search(topicSearchBase, getFilterForPermissionType(permissionType),
DestinationType.TOPIC, permissionType); constraints), DestinationType.TOPIC, permissionType);
} catch (Exception e) { } catch (Exception e) {
LOG.error("Policy not applied!. Error processing policy under '" + topicSearchBase + "' with filter '" LOG.error("Policy not applied!. Error processing policy under '" + topicSearchBase +
+ getFilterForPermissionType(permissionType) + "'", e); "' with filter '" + getFilterForPermissionType(permissionType) + "'", e);
} }
} }
for (PermissionType permissionType : PermissionType.values()) { for (PermissionType permissionType : PermissionType.values()) {
try { try {
processQueryResults( processQueryResults(newMap,
currentContext.search(tempSearchBase, getFilterForPermissionType(permissionType), constraints), currentContext.search(tempSearchBase, getFilterForPermissionType(permissionType),
DestinationType.TEMP, permissionType); constraints), DestinationType.TEMP, permissionType);
} catch (Exception e) { } catch (Exception e) {
LOG.error("Policy not applied!. Error processing policy under '" + tempSearchBase + "' with filter '" LOG.error("Policy not applied!. Error processing policy under '" + tempSearchBase +
+ getFilterForPermissionType(permissionType) + "'", e); "' with filter '" + getFilterForPermissionType(permissionType) + "'", e);
} }
} }
setEntries(new ArrayList<DestinationMapEntry>(entries.values())); // Create and swap in the new instance with updated LDAP data.
newMap.setAuthorizationEntries(new ArrayList<DestinationMapEntry>(entries.values()));
this.map.set(newMap);
updated(); updated();
} }
/** /**
* Processes results from a directory query in the context of a given destination type and permission type. * Processes results from a directory query in the context of a given destination type and permission type. This
* This implementation should not be invoked concurrently. * implementation should not be invoked concurrently.
* *
* @param results the results to process * @param results
* @param destinationType the type of the destination for which the directory results apply * the results to process
* @param permissionType the type of the permission for which the directory results apply * @param destinationType
* the type of the destination for which the directory results apply
* @param permissionType
* the type of the permission for which the directory results apply
* *
* @throws Exception if there is an error processing the results * @throws Exception
* if there is an error processing the results
*/ */
protected void processQueryResults(NamingEnumeration<SearchResult> results, protected void processQueryResults(DefaultAuthorizationMap map, NamingEnumeration<SearchResult> results, DestinationType destinationType, PermissionType permissionType)
DestinationType destinationType, PermissionType permissionType) throws Exception { throws Exception {
while (results.hasMore()) { while (results.hasMore()) {
SearchResult result = results.next(); SearchResult result = results.next();
AuthorizationEntry entry = null; AuthorizationEntry entry = null;
try { try {
entry = getEntry(new LdapName(result.getNameInNamespace()), destinationType); entry = getEntry(map, new LdapName(result.getNameInNamespace()), destinationType);
} catch (Exception e) { } catch (Exception e) {
LOG.error("Policy not applied! Error parsing authorization policy entry under " LOG.error("Policy not applied! Error parsing authorization policy entry under " + result.getNameInNamespace(), e);
+ result.getNameInNamespace(), e);
continue; continue;
} }
@ -250,20 +296,23 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
} }
/** /**
* Marks the time at which the authorization state was last refreshed. Relevant for synchronous policy updates. * Marks the time at which the authorization state was last refreshed. Relevant for synchronous
* This implementation should not be invoked concurrently. * policy updates. This implementation should not be invoked concurrently.
*/ */
protected void updated() { protected void updated() {
lastUpdated = System.currentTimeMillis(); lastUpdated = System.currentTimeMillis();
} }
/** /**
* Retrieves or creates the {@link AuthorizationEntry} that corresponds to * Retrieves or creates the {@link AuthorizationEntry} that corresponds to the DN in {@code dn}. This implementation
* the DN in {@code dn}. This implementation should not be invoked concurrently. * should not be invoked concurrently.
* *
* @param map
* the DefaultAuthorizationMap to operate on.
* @param dn * @param dn
* the DN representing the policy entry in the directory * the DN representing the policy entry in the directory
* @param destinationType the type of the destination to get/create the entry for * @param destinationType
* the type of the destination to get/create the entry for
* *
* @return the corresponding authorization entry for the DN * @return the corresponding authorization entry for the DN
* *
@ -271,7 +320,7 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
* if destination type is not one of {@link DestinationType#QUEUE}, {@link DestinationType#TOPIC}, * if destination type is not one of {@link DestinationType#QUEUE}, {@link DestinationType#TOPIC},
* {@link DestinationType#TEMP} or if the policy entry DN is malformed * {@link DestinationType#TEMP} or if the policy entry DN is malformed
*/ */
protected AuthorizationEntry getEntry(LdapName dn, DestinationType destinationType) { protected AuthorizationEntry getEntry(DefaultAuthorizationMap map, LdapName dn, DestinationType destinationType) {
AuthorizationEntry entry = null; AuthorizationEntry entry = null;
switch (destinationType) { switch (destinationType) {
case TEMP: case TEMP:
@ -279,13 +328,12 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
if (dn.size() != getPrefixLengthForDestinationType(destinationType) + 1) { if (dn.size() != getPrefixLengthForDestinationType(destinationType) + 1) {
// handle unknown entry // handle unknown entry
throw new IllegalArgumentException("Malformed policy structure for a temporary destination " throw new IllegalArgumentException("Malformed policy structure for a temporary destination "
+ "policy entry. The permission group entries should be immediately below the " + "policy entry. The permission group entries should be immediately below the " + "temporary policy base DN.");
+ "temporary policy base DN.");
} }
entry = getTempDestinationAuthorizationEntry(); entry = map.getTempDestinationAuthorizationEntry();
if (entry == null) { if (entry == null) {
entry = new TempDestinationAuthorizationEntry(); entry = new TempDestinationAuthorizationEntry();
setTempDestinationAuthorizationEntry((TempDestinationAuthorizationEntry) entry); map.setTempDestinationAuthorizationEntry((TempDestinationAuthorizationEntry) entry);
} }
break; break;
@ -295,8 +343,7 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
// handle regular destinations // handle regular destinations
if (dn.size() != getPrefixLengthForDestinationType(destinationType) + 2) { if (dn.size() != getPrefixLengthForDestinationType(destinationType) + 2) {
throw new IllegalArgumentException("Malformed policy structure for a queue or topic destination " throw new IllegalArgumentException("Malformed policy structure for a queue or topic destination "
+ "policy entry. The destination pattern and permission group entries should be " + "policy entry. The destination pattern and permission group entries should be " + "nested below the queue or topic policy base DN.");
+ "nested below the queue or topic policy base DN.");
} }
ActiveMQDestination dest = formatDestination(dn, destinationType); ActiveMQDestination dest = formatDestination(dn, destinationType);
@ -320,17 +367,19 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
} }
/** /**
* Applies the policy from the directory to the given entry within the context of the provided * Applies the policy from the directory to the given entry within the context of the provided permission type.
* permission type.
* *
* @param entry the policy entry to apply the policy to * @param entry
* @param result the results from the directory to apply to the policy entry * the policy entry to apply the policy to
* @param permissionType the permission type of the data in the directory * @param result
* the results from the directory to apply to the policy entry
* @param permissionType
* the permission type of the data in the directory
* *
* @throws NamingException if there is an error applying the ACL * @throws NamingException
* if there is an error applying the ACL
*/ */
protected void applyACL(AuthorizationEntry entry, SearchResult result, protected void applyACL(AuthorizationEntry entry, SearchResult result, PermissionType permissionType) throws NamingException {
PermissionType permissionType) throws NamingException {
// Find members // Find members
Attribute memberAttribute = result.getAttributes().get(permissionGroupMemberAttribute); Attribute memberAttribute = result.getAttributes().get(permissionGroupMemberAttribute);
@ -348,13 +397,9 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
// Lookup of member to determine principal type (group or user) and name. // Lookup of member to determine principal type (group or user) and name.
Attributes memberAttributes; Attributes memberAttributes;
try { try {
memberAttributes = context.getAttributes(memberDn, memberAttributes = context.getAttributes(memberDn, new String[] { "objectClass", groupNameAttribute, userNameAttribute });
new String[] {"objectClass", groupNameAttribute, userNameAttribute});
} catch (NamingException e) { } catch (NamingException e) {
LOG.error( LOG.error("Policy not applied! Unknown member " + memberDn + " in policy entry " + result.getNameInNamespace(), e);
"Policy not applied! Unknown member " + memberDn
+ " in policy entry "
+ result.getNameInNamespace(), e);
continue; continue;
} }
@ -368,10 +413,8 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
group = true; group = true;
Attribute name = memberAttributes.get(groupNameAttribute); Attribute name = memberAttributes.get(groupNameAttribute);
if (name == null) { if (name == null) {
LOG.error("Policy not applied! Group " LOG.error("Policy not applied! Group " + memberDn + "does not have name attribute " + groupNameAttribute + " under entry "
+ memberDn + result.getNameInNamespace());
+ "does not have name attribute "
+ groupNameAttribute + " under entry " + result.getNameInNamespace());
break; break;
} }
@ -382,9 +425,8 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
user = true; user = true;
Attribute name = memberAttributes.get(userNameAttribute); Attribute name = memberAttributes.get(userNameAttribute);
if (name == null) { if (name == null) {
LOG.error("Policy not applied! User " LOG.error("Policy not applied! User " + memberDn + " does not have name attribute " + userNameAttribute + " under entry "
+ memberDn + " does not have name attribute " + result.getNameInNamespace());
+ userNameAttribute + " under entry " + result.getNameInNamespace());
break; break;
} }
@ -398,14 +440,15 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
} }
if ((!group && !user) || (group && user)) { if ((!group && !user) || (group && user)) {
LOG.error("Policy not applied! Can't determine type of member " LOG.error("Policy not applied! Can't determine type of member " + memberDn + " under entry " + result.getNameInNamespace());
+ memberDn + " under entry " + result.getNameInNamespace());
} else if (principalName != null) { } else if (principalName != null) {
DefaultAuthorizationMap map = this.map.get();
if (group && !user) { if (group && !user) {
try { try {
members.add(createGroupPrincipal(principalName, getGroupClass())); members.add(DefaultAuthorizationMap.createGroupPrincipal(principalName, map.getGroupClass()));
} catch (Exception e) { } catch (Exception e) {
NamingException ne = new NamingException("Can't create a group " + principalName + " of class " + getGroupClass()); NamingException ne = new NamingException(
"Can't create a group " + principalName + " of class " + map.getGroupClass());
ne.initCause(e); ne.initCause(e);
throw ne; throw ne;
} }
@ -418,18 +461,19 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
try { try {
applyAcl(entry, permissionType, members); applyAcl(entry, permissionType, members);
} catch (Exception e) { } catch (Exception e) {
LOG.error( LOG.error("Policy not applied! Error adding principals to ACL under " + result.getNameInNamespace(), e);
"Policy not applied! Error adding principals to ACL under "
+ result.getNameInNamespace(), e);
} }
} }
/** /**
* Applies policy to the entry given the actual principals that will be applied to the policy entry. * Applies policy to the entry given the actual principals that will be applied to the policy entry.
* *
* @param entry the policy entry to which the policy should be applied * @param entry
* @param permissionType the type of the permission that the policy will be applied to * the policy entry to which the policy should be applied
* @param acls the principals that represent the actual policy * @param permissionType
* the type of the permission that the policy will be applied to
* @param acls
* the principals that represent the actual policy
* *
* @throw IllegalArgumentException if {@code permissionType} is unsupported * @throw IllegalArgumentException if {@code permissionType} is unsupported
*/ */
@ -451,18 +495,20 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
} }
/** /**
* Parses a DN into the equivalent {@link ActiveMQDestination}. The default implementation * Parses a DN into the equivalent {@link ActiveMQDestination}. The default implementation expects a format of
* expects a format of cn=<PERMISSION_NAME>,ou=<DESTINATION_PATTERN>,.... or * cn=<PERMISSION_NAME>,ou=<DESTINATION_PATTERN>,.... or ou=<DESTINATION_PATTERN>,.... for permission and
* ou=<DESTINATION_PATTERN>,.... for permission and destination entries, respectively. * destination entries, respectively. For example {@code cn=admin,ou=$,ou=...} or {@code ou=$,ou=...}.
* For example {@code cn=admin,ou=$,ou=...} or {@code ou=$,ou=...}.
* *
* @param dn the DN to parse * @param dn
* @param destinationType the type of the destination that we are parsing * the DN to parse
* @param destinationType
* the type of the destination that we are parsing
* *
* @return the destination that the DN represents * @return the destination that the DN represents
* *
* @throws IllegalArgumentException if {@code destinationType} is {@link DestinationType#TEMP} or * @throws IllegalArgumentException
* if the format of {@code dn} is incorrect for for a topic or queue * if {@code destinationType} is {@link DestinationType#TEMP} or if the format of {@code dn} is
* incorrect for for a topic or queue
* *
* @see #formatDestination(Rdn, DestinationType) * @see #formatDestination(Rdn, DestinationType)
*/ */
@ -480,21 +526,19 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
} else if (dn.size() == (getPrefixLengthForDestinationType(destinationType) + 1)) { } else if (dn.size() == (getPrefixLengthForDestinationType(destinationType) + 1)) {
destination = formatDestination(dn.getRdn(dn.size() - 1), destinationType); destination = formatDestination(dn.getRdn(dn.size() - 1), destinationType);
} else { } else {
throw new IllegalArgumentException( throw new IllegalArgumentException("Malformed DN for representing a permission or destination entry.");
"Malformed DN for representing a permission or destination entry.");
} }
break; break;
default: default:
throw new IllegalArgumentException( throw new IllegalArgumentException("Cannot format destination for destination type " + destinationType);
"Cannot format destination for destination type " + destinationType);
} }
return destination; return destination;
} }
/** /**
* Parses RDN values representing the destination name/pattern and * Parses RDN values representing the destination name/pattern and destination type into the equivalent
* destination type into the equivalent {@link ActiveMQDestination}. * {@link ActiveMQDestination}.
* *
* @param destinationName * @param destinationName
* the RDN representing the name or pattern for the destination * the RDN representing the name or pattern for the destination
@ -521,19 +565,18 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
dest = new ActiveMQTopic(formatDestinationName(destinationName)); dest = new ActiveMQTopic(formatDestinationName(destinationName));
break; break;
default: default:
throw new IllegalArgumentException("Unknown destination type: " throw new IllegalArgumentException("Unknown destination type: " + destinationType);
+ destinationType);
} }
return dest; return dest;
} }
/** /**
* Parses the RDN representing a destination name/pattern into the standard string representation * Parses the RDN representing a destination name/pattern into the standard string representation of the
* of the name/pattern. This implementation does not care about the type of the RDN such that the RDN could * name/pattern. This implementation does not care about the type of the RDN such that the RDN could be a CN or OU.
* be a CN or OU.
* *
* @param destinationName the RDN representing the name or pattern for the destination * @param destinationName
* the RDN representing the name or pattern for the destination
* *
* @see #formatDestination(Rdn, Rdn) * @see #formatDestination(Rdn, Rdn)
*/ */
@ -542,14 +585,13 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
} }
/** /**
* Transcribes an existing set into a new set. Used to make defensive copies * Transcribes an existing set into a new set. Used to make defensive copies for concurrent access.
* for concurrent access.
* *
* @param source * @param source
* the source set or {@code null} * the source set or {@code null}
* *
* @return a new set containing the same elements as {@code source} or * @return a new set containing the same elements as {@code source} or {@code null} if {@code source} is
* {@code null} if {@code source} is {@code null} * {@code null}
*/ */
protected <T> Set<T> transcribeSet(Set<T> source) { protected <T> Set<T> transcribeSet(Set<T> source) {
if (source != null) { if (source != null) {
@ -562,7 +604,8 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
/** /**
* Returns the filter string for the given permission type. * Returns the filter string for the given permission type.
* *
* @throws IllegalArgumentException if {@code permissionType} is not supported * @throws IllegalArgumentException
* if {@code permissionType} is not supported
* *
* @see #setAdminPermissionGroupSearchFilter(String) * @see #setAdminPermissionGroupSearchFilter(String)
* @see #setReadPermissionGroupSearchFilter(String) * @see #setReadPermissionGroupSearchFilter(String)
@ -591,7 +634,8 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
/** /**
* Returns the DN prefix size based on the given destination type. * Returns the DN prefix size based on the given destination type.
* *
* @throws IllegalArgumentException if {@code destinationType} is not supported * @throws IllegalArgumentException
* if {@code destinationType} is not supported
* *
* @see #setQueueSearchBase(String) * @see #setQueueSearchBase(String)
* @see #setTopicSearchBase(String) * @see #setTopicSearchBase(String)
@ -618,11 +662,24 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
} }
/** /**
* Performs a check for updates from the server in the event that synchronous updates are enabled * Performs a check for updates from the server in the event that synchronous updates are enabled and are the
* and are the refresh interval has elapsed. * refresh interval has elapsed.
*/ */
protected void checkForUpdates() { protected void checkForUpdates() {
if (context != null && refreshDisabled) {
return;
}
if (context == null || (!refreshDisabled && (refreshInterval != -1 && System.currentTimeMillis() >= lastUpdated + refreshInterval))) { if (context == null || (!refreshDisabled && (refreshInterval != -1 && System.currentTimeMillis() >= lastUpdated + refreshInterval))) {
this.updaterService.execute(new Runnable() {
@Override
public void run() {
// Check again in case of stacked update request.
if (context == null || (!refreshDisabled &&
(refreshInterval != -1 && System.currentTimeMillis() >= lastUpdated + refreshInterval))) {
if (!isContextAlive()) { if (!isContextAlive()) {
try { try {
context = createContext(); context = createContext();
@ -631,159 +688,160 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
return; return;
} }
} }
reset();
setTempDestinationAuthorizationEntry(null);
entries.clear(); entries.clear();
LOG.debug("Updating authorization map!"); LOG.debug("Updating authorization map!");
try { try {
query(); query();
} catch (Exception e) { } catch (Exception e) {
LOG.error("Error updating authorization map. Partial policy " LOG.error("Error updating authorization map. Partial policy " +
+ "may be applied until the next successful update.", e); "may be applied until the next successful update.", e);
} }
} }
} }
});
}
}
// Authorization Map // Authorization Map
/** /**
* Provides synchronous refresh capabilities if so configured before delegating to the super implementation, * Provides synchronized and defensive access to the admin ACLs for temp destinations as the super implementation
* and otherwise simply delegates to the super implementation. * returns live copies of the ACLs and {@link AuthorizationEntry} is not setup for concurrent access.
*/ */
@Override @Override
protected synchronized Set<AuthorizationEntry> getAllEntries(ActiveMQDestination destination) { public Set<Object> getTempDestinationAdminACLs() {
checkForUpdates(); checkForUpdates();
return super.getAllEntries(destination); DefaultAuthorizationMap map = this.map.get();
return transcribeSet(map.getTempDestinationAdminACLs());
} }
/** /**
* Provides synchronized and defensive access to the admin ACLs for temp destinations as the super * Provides synchronized and defensive access to the read ACLs for temp destinations as the super implementation
* implementation returns live copies of the ACLs and {@link AuthorizationEntry} is not * returns live copies of the ACLs and {@link AuthorizationEntry} is not setup for concurrent access.
* setup for concurrent access.
*/ */
@Override @Override
public synchronized Set<Object> getTempDestinationAdminACLs() { public Set<Object> getTempDestinationReadACLs() {
checkForUpdates(); checkForUpdates();
return transcribeSet(super.getTempDestinationAdminACLs()); DefaultAuthorizationMap map = this.map.get();
return transcribeSet(map.getTempDestinationReadACLs());
} }
/** /**
* Provides synchronized and defensive access to the read ACLs for temp destinations as the super * Provides synchronized and defensive access to the write ACLs for temp destinations as the super implementation
* implementation returns live copies of the ACLs and {@link AuthorizationEntry} is not * returns live copies of the ACLs and {@link AuthorizationEntry} is not setup for concurrent access.
* setup for concurrent access.
*/ */
public synchronized Set<Object> getTempDestinationReadACLs() { @Override
public Set<Object> getTempDestinationWriteACLs() {
checkForUpdates(); checkForUpdates();
return transcribeSet(super.getTempDestinationReadACLs()); DefaultAuthorizationMap map = this.map.get();
return transcribeSet(map.getTempDestinationWriteACLs());
} }
/** /**
* Provides synchronized and defensive access to the write ACLs for temp destinations as the super * Provides synchronized access to the admin ACLs for the destinations as {@link AuthorizationEntry}
* implementation returns live copies of the ACLs and {@link AuthorizationEntry} is not * is not setup for concurrent access.
* setup for concurrent access.
*/ */
public synchronized Set<Object> getTempDestinationWriteACLs() { @Override
public Set<Object> getAdminACLs(ActiveMQDestination destination) {
checkForUpdates(); checkForUpdates();
return transcribeSet(super.getTempDestinationWriteACLs()); DefaultAuthorizationMap map = this.map.get();
return map.getAdminACLs(destination);
} }
/** /**
* Provides synchronized access to the admin ACLs for the destinations as * Provides synchronized access to the read ACLs for the destinations as {@link AuthorizationEntry} is not setup for
* {@link AuthorizationEntry} is not setup for concurrent access. * concurrent access.
*/ */
public synchronized Set<Object> getAdminACLs(ActiveMQDestination destination) { @Override
return super.getAdminACLs(destination); public Set<Object> getReadACLs(ActiveMQDestination destination) {
checkForUpdates();
DefaultAuthorizationMap map = this.map.get();
return map.getReadACLs(destination);
} }
/** /**
* Provides synchronized access to the read ACLs for the destinations as * Provides synchronized access to the write ACLs for the destinations as {@link AuthorizationEntry} is not setup
* {@link AuthorizationEntry} is not setup for concurrent access. * for concurrent access.
*/ */
public synchronized Set<Object> getReadACLs(ActiveMQDestination destination) { @Override
public Set<Object> getWriteACLs(ActiveMQDestination destination) {
checkForUpdates(); checkForUpdates();
return super.getReadACLs(destination); DefaultAuthorizationMap map = this.map.get();
} return map.getWriteACLs(destination);
/**
* Provides synchronized access to the write ACLs for the destinations as
* {@link AuthorizationEntry} is not setup for concurrent access.
*/
public synchronized Set<Object> getWriteACLs(ActiveMQDestination destination) {
checkForUpdates();
return super.getWriteACLs(destination);
} }
/** /**
* Handler for new policy entries in the directory. * Handler for new policy entries in the directory.
* *
* @param namingEvent the new entry event that occurred * @param namingEvent
* @param destinationType the type of the destination to which the event applies * the new entry event that occurred
* @param permissionType the permission type to which the event applies * @param destinationType
* the type of the destination to which the event applies
* @param permissionType
* the permission type to which the event applies
*/ */
public synchronized void objectAdded(NamingEvent namingEvent, DestinationType destinationType, public void objectAdded(NamingEvent namingEvent, DestinationType destinationType, PermissionType permissionType) {
PermissionType permissionType) {
LOG.debug("Adding object: " + namingEvent.getNewBinding()); LOG.debug("Adding object: " + namingEvent.getNewBinding());
SearchResult result = (SearchResult) namingEvent.getNewBinding(); SearchResult result = (SearchResult) namingEvent.getNewBinding();
try { try {
DefaultAuthorizationMap map = this.map.get();
LdapName name = new LdapName(result.getName()); LdapName name = new LdapName(result.getName());
AuthorizationEntry entry = getEntry(map, name, destinationType);
AuthorizationEntry entry = getEntry(name, destinationType);
applyACL(entry, result, permissionType); applyACL(entry, result, permissionType);
if (!(entry instanceof TempDestinationAuthorizationEntry)) { if (!(entry instanceof TempDestinationAuthorizationEntry)) {
put(entry.getDestination(), entry); map.put(entry.getDestination(), entry);
} }
} catch (InvalidNameException e) { } catch (InvalidNameException e) {
LOG.error("Policy not applied! Error parsing DN for addition of " LOG.error("Policy not applied! Error parsing DN for addition of " + result.getName(), e);
+ result.getName(), e);
} catch (Exception e) { } catch (Exception e) {
LOG.error("Policy not applied! Error processing object addition for addition of " LOG.error("Policy not applied! Error processing object addition for addition of " + result.getName(), e);
+ result.getName(), e);
} }
} }
/** /**
* Handler for removed policy entries in the directory. * Handler for removed policy entries in the directory.
* *
* @param namingEvent the removed entry event that occurred * @param namingEvent
* @param destinationType the type of the destination to which the event applies * the removed entry event that occurred
* @param permissionType the permission type to which the event applies * @param destinationType
* the type of the destination to which the event applies
* @param permissionType
* the permission type to which the event applies
*/ */
public synchronized void objectRemoved(NamingEvent namingEvent, DestinationType destinationType, public void objectRemoved(NamingEvent namingEvent, DestinationType destinationType, PermissionType permissionType) {
PermissionType permissionType) {
LOG.debug("Removing object: " + namingEvent.getOldBinding()); LOG.debug("Removing object: " + namingEvent.getOldBinding());
Binding result = namingEvent.getOldBinding(); Binding result = namingEvent.getOldBinding();
try { try {
DefaultAuthorizationMap map = this.map.get();
LdapName name = new LdapName(result.getName()); LdapName name = new LdapName(result.getName());
AuthorizationEntry entry = getEntry(map, name, destinationType);
AuthorizationEntry entry = getEntry(name, destinationType);
applyAcl(entry, permissionType, new HashSet<Object>()); applyAcl(entry, permissionType, new HashSet<Object>());
} catch (InvalidNameException e) { } catch (InvalidNameException e) {
LOG.error("Policy not applied! Error parsing DN for object removal for removal of " LOG.error("Policy not applied! Error parsing DN for object removal for removal of " + result.getName(), e);
+ result.getName(), e);
} catch (Exception e) { } catch (Exception e) {
LOG.error("Policy not applied! Error processing object removal for removal of " LOG.error("Policy not applied! Error processing object removal for removal of " + result.getName(), e);
+ result.getName(), e);
} }
} }
/** /**
* Handler for renamed policy entries in the directory. This handler deals with the renaming * Handler for renamed policy entries in the directory. This handler deals with the renaming of destination entries
* of destination entries as well as permission entries. If the permission type is not null, it is * as well as permission entries. If the permission type is not null, it is assumed that we are dealing with the
* assumed that we are dealing with the renaming of a permission entry. Otherwise, it is assumed * renaming of a permission entry. Otherwise, it is assumed that we are dealing with the renaming of a destination
* that we are dealing with the renaming of a destination entry. * entry.
* *
* @param namingEvent the renaming entry event that occurred * @param namingEvent
* @param destinationType the type of the destination to which the event applies * the renaming entry event that occurred
* @param permissionType the permission type to which the event applies * @param destinationType
* the type of the destination to which the event applies
* @param permissionType
* the permission type to which the event applies
*/ */
public synchronized void objectRenamed(NamingEvent namingEvent, DestinationType destinationType, public void objectRenamed(NamingEvent namingEvent, DestinationType destinationType, PermissionType permissionType) {
PermissionType permissionType) {
Binding oldBinding = namingEvent.getOldBinding(); Binding oldBinding = namingEvent.getOldBinding();
Binding newBinding = namingEvent.getNewBinding(); Binding newBinding = namingEvent.getNewBinding();
LOG.debug("Renaming object: " + oldBinding + " to " + newBinding); LOG.debug("Renaming object: " + oldBinding + " to " + newBinding);
@ -805,9 +863,7 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
boolean matchedToType = false; boolean matchedToType = false;
for (PermissionType newPermissionType : PermissionType.values()) { for (PermissionType newPermissionType : PermissionType.values()) {
NamingEnumeration<SearchResult> results = context.search( NamingEnumeration<SearchResult> results = context.search(newName, getFilterForPermissionType(newPermissionType), controls);
newName,
getFilterForPermissionType(newPermissionType), controls);
if (results.hasMore()) { if (results.hasMore()) {
objectAdded(namingEvent, destinationType, newPermissionType); objectAdded(namingEvent, destinationType, newPermissionType);
@ -817,19 +873,18 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
} }
if (!matchedToType) { if (!matchedToType) {
LOG.error("Policy not applied! Error processing object rename for rename of " LOG.error("Policy not applied! Error processing object rename for rename of " + oldBinding.getName() + " to " + newBinding.getName()
+ oldBinding.getName() + " to " + newBinding.getName()
+ ". Could not determine permission type of new object."); + ". Could not determine permission type of new object.");
} }
} else { } else {
// Handle the case where a destination entry is being renamed. // Handle the case where a destination entry is being renamed.
if (oldDest != null && newDest != null) { if (oldDest != null && newDest != null) {
AuthorizationEntry entry = entries.remove(oldDest); AuthorizationEntry entry = entries.remove(oldDest);
if (entry != null) { if (entry != null) {
entry.setDestination(newDest); entry.setDestination(newDest);
put(newDest, entry); DefaultAuthorizationMap map = this.map.get();
remove(oldDest, entry); map.put(newDest, entry);
map.remove(oldDest, entry);
entries.put(newDest, entry); entries.put(newDest, entry);
} else { } else {
LOG.warn("No authorization entry for " + oldDest); LOG.warn("No authorization entry for " + oldDest);
@ -837,23 +892,23 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
} }
} }
} catch (InvalidNameException e) { } catch (InvalidNameException e) {
LOG.error("Policy not applied! Error parsing DN for object rename for rename of " LOG.error("Policy not applied! Error parsing DN for object rename for rename of " + oldBinding.getName() + " to " + newBinding.getName(), e);
+ oldBinding.getName() + " to " + newBinding.getName(), e);
} catch (Exception e) { } catch (Exception e) {
LOG.error("Policy not applied! Error processing object rename for rename of " LOG.error("Policy not applied! Error processing object rename for rename of " + oldBinding.getName() + " to " + newBinding.getName(), e);
+ oldBinding.getName() + " to " + newBinding.getName(), e);
} }
} }
/** /**
* Handler for changed policy entries in the directory. * Handler for changed policy entries in the directory.
* *
* @param namingEvent the changed entry event that occurred * @param namingEvent
* @param destinationType the type of the destination to which the event applies * the changed entry event that occurred
* @param permissionType the permission type to which the event applies * @param destinationType
* the type of the destination to which the event applies
* @param permissionType
* the permission type to which the event applies
*/ */
public synchronized void objectChanged(NamingEvent namingEvent, public void objectChanged(NamingEvent namingEvent, DestinationType destinationType, PermissionType permissionType) {
DestinationType destinationType, PermissionType permissionType) {
LOG.debug("Changing object " + namingEvent.getOldBinding() + " to " + namingEvent.getNewBinding()); LOG.debug("Changing object " + namingEvent.getOldBinding() + " to " + namingEvent.getNewBinding());
objectRemoved(namingEvent, destinationType, permissionType); objectRemoved(namingEvent, destinationType, permissionType);
objectAdded(namingEvent, destinationType, permissionType); objectAdded(namingEvent, destinationType, permissionType);
@ -862,7 +917,8 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
/** /**
* Handler for exception events from the registry. * Handler for exception events from the registry.
* *
* @param namingExceptionEvent the exception event * @param namingExceptionEvent
* the exception event
*/ */
public void namingExceptionThrown(NamingExceptionEvent namingExceptionEvent) { public void namingExceptionThrown(NamingExceptionEvent namingExceptionEvent) {
context = null; context = null;
@ -974,8 +1030,7 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
return permissionGroupMemberAttribute; return permissionGroupMemberAttribute;
} }
public void setPermissionGroupMemberAttribute( public void setPermissionGroupMemberAttribute(String permissionGroupMemberAttribute) {
String permissionGroupMemberAttribute) {
this.permissionGroupMemberAttribute = permissionGroupMemberAttribute; this.permissionGroupMemberAttribute = permissionGroupMemberAttribute;
} }
@ -983,8 +1038,7 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
return adminPermissionGroupSearchFilter; return adminPermissionGroupSearchFilter;
} }
public void setAdminPermissionGroupSearchFilter( public void setAdminPermissionGroupSearchFilter(String adminPermissionGroupSearchFilter) {
String adminPermissionGroupSearchFilter) {
this.adminPermissionGroupSearchFilter = adminPermissionGroupSearchFilter; this.adminPermissionGroupSearchFilter = adminPermissionGroupSearchFilter;
} }
@ -992,8 +1046,7 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
return readPermissionGroupSearchFilter; return readPermissionGroupSearchFilter;
} }
public void setReadPermissionGroupSearchFilter( public void setReadPermissionGroupSearchFilter(String readPermissionGroupSearchFilter) {
String readPermissionGroupSearchFilter) {
this.readPermissionGroupSearchFilter = readPermissionGroupSearchFilter; this.readPermissionGroupSearchFilter = readPermissionGroupSearchFilter;
} }
@ -1001,8 +1054,7 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
return writePermissionGroupSearchFilter; return writePermissionGroupSearchFilter;
} }
public void setWritePermissionGroupSearchFilter( public void setWritePermissionGroupSearchFilter(String writePermissionGroupSearchFilter) {
String writePermissionGroupSearchFilter) {
this.writePermissionGroupSearchFilter = writePermissionGroupSearchFilter; this.writePermissionGroupSearchFilter = writePermissionGroupSearchFilter;
} }
@ -1063,38 +1115,32 @@ public class SimpleCachedLDAPAuthorizationMap extends DefaultAuthorizationMap {
} }
protected static enum DestinationType { protected static enum DestinationType {
QUEUE, QUEUE, TOPIC, TEMP;
TOPIC,
TEMP;
} }
protected static enum PermissionType { protected static enum PermissionType {
READ, READ, WRITE, ADMIN;
WRITE,
ADMIN;
} }
/** /**
* Listener implementation for directory changes that maps change events to * Listener implementation for directory changes that maps change events to destination types.
* destination types.
*/ */
protected class CachedLDAPAuthorizationMapNamespaceChangeListener implements protected class CachedLDAPAuthorizationMapNamespaceChangeListener implements NamespaceChangeListener, ObjectChangeListener {
NamespaceChangeListener, ObjectChangeListener {
private final DestinationType destinationType; private final DestinationType destinationType;
private final PermissionType permissionType; private final PermissionType permissionType;
/** /**
* Creates a new listener. If {@code permissionType} is {@code null}, add * Creates a new listener. If {@code permissionType} is {@code null}, add and remove events are ignored as they
* and remove events are ignored as they do not directly affect policy state. * do not directly affect policy state. This configuration is used when listening for changes on entries that
* This configuration is used when listening for changes on entries that represent * represent destination patterns and not for entries that represent permissions.
* destination patterns and not for entries that represent permissions.
* *
* @param destinationType the type of the destination being listened for * @param destinationType
* @param permissionType the optional permission type being listened for * the type of the destination being listened for
* @param permissionType
* the optional permission type being listened for
*/ */
public CachedLDAPAuthorizationMapNamespaceChangeListener( public CachedLDAPAuthorizationMapNamespaceChangeListener(DestinationType destinationType, PermissionType permissionType) {
DestinationType destinationType, PermissionType permissionType) {
this.destinationType = destinationType; this.destinationType = destinationType;
this.permissionType = permissionType; this.permissionType = permissionType;
} }

View File

@ -112,8 +112,13 @@ public abstract class AbstractCachedLDAPAuthorizationMapLegacyTest extends Abstr
reader.close(); reader.close();
failedACLs = map.getReadACLs(new ActiveMQQueue("TEST.FOO")); assertTrue("did not get expected size. ", Wait.waitFor(new Wait.Condition() {
assertEquals("set size: " + failedACLs, 0, failedACLs.size());
@Override
public boolean isSatisified() throws Exception {
return map.getReadACLs(new ActiveMQQueue("TEST.FOO")).size() == 0;
}
}));
assertNull(map.getTempDestinationReadACLs()); assertNull(map.getTempDestinationReadACLs());
assertNull(map.getTempDestinationWriteACLs()); assertNull(map.getTempDestinationWriteACLs());
@ -335,10 +340,14 @@ public abstract class AbstractCachedLDAPAuthorizationMapLegacyTest extends Abstr
} }
reader.close(); reader.close();
Thread.sleep(2000);
failedACLs = map.getReadACLs(new ActiveMQQueue("FAILED")); assertTrue("did not get expected size. ", Wait.waitFor(new Wait.Condition() {
assertEquals("set size: " + failedACLs, 2, failedACLs.size());
@Override
public boolean isSatisified() throws Exception {
return map.getReadACLs(new ActiveMQQueue("FAILED")).size() == 2;
}
}));
} }
protected SimpleCachedLDAPAuthorizationMap createMap() { protected SimpleCachedLDAPAuthorizationMap createMap() {