activemq-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dej...@apache.org
Subject svn commit: r1347580 [1/2] - in /activemq/trunk: ./ activemq-core/src/main/java/org/apache/activemq/security/ activemq-core/src/test/java/org/apache/activemq/security/ activemq-core/src/test/resources/org/apache/activemq/security/
Date Thu, 07 Jun 2012 11:58:41 GMT
Author: dejanb
Date: Thu Jun  7 11:58:40 2012
New Revision: 1347580

URL: http://svn.apache.org/viewvc?rev=1347580&view=rev
Log:
https://issues.apache.org/jira/browse/AMQ-3791 - CachedLDAPAuthorizationMap improvements

Added:
    activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/AbstractCachedLDAPAuthorizationMapLegacyTest.java
    activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/AbstractCachedLDAPAuthorizationModuleTest.java
    activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPAuthorizationModuleLegacyOpenLDAPTest.java
    activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPAuthorizationModuleLegacyTest.java
    activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPAuthorizationModuleOpenLDAPTest.java
    activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPSecurityLegacyTest.java
      - copied, changed from r1346818, activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPSecurityTest.java
    activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/activemq-apacheds-add.ldif
      - copied, changed from r1346818, activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/add.ldif
    activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/activemq-apacheds-delete.ldif
      - copied, changed from r1346818, activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/delete.ldif
    activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/activemq-apacheds-legacy-add.ldif
      - copied, changed from r1346818, activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/add.ldif
    activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/activemq-apacheds-legacy-delete.ldif
      - copied, changed from r1346818, activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/delete.ldif
    activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/activemq-apacheds-legacy.ldif
      - copied, changed from r1346818, activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/activemq-apacheds.ldif
    activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/activemq-apacheds-legacy.xml
      - copied, changed from r1346818, activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/activemq-apacheds.xml
    activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/activemq-openldap-add.ldif
      - copied, changed from r1346818, activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/add.ldif
    activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/activemq-openldap-delete.ldif
      - copied, changed from r1346818, activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/delete.ldif
    activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/activemq-openldap-legacy-add.ldif
      - copied, changed from r1346818, activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/add.ldif
    activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/activemq-openldap-legacy-delete.ldif
      - copied, changed from r1346818, activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/delete.ldif
    activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/activemq-openldap-legacy.ldif
      - copied, changed from r1346818, activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/activemq-openldap.ldif
Removed:
    activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/add.ldif
    activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/delete.ldif
Modified:
    activemq/trunk/activemq-core/src/main/java/org/apache/activemq/security/CachedLDAPAuthorizationMap.java
    activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPAuthorizationModuleTest.java
    activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPSecurityTest.java
    activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/LDAPAuthorizationMapTest.java
    activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/LDAPSecurityTest.java
    activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/activemq-apacheds.ldif
    activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/activemq-apacheds.xml
    activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/activemq-ldap.xml
    activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/activemq-openldap.ldif
    activemq/trunk/activemq-core/src/test/resources/org/apache/activemq/security/activemq-openldap.xml
    activemq/trunk/pom.xml

Modified: activemq/trunk/activemq-core/src/main/java/org/apache/activemq/security/CachedLDAPAuthorizationMap.java
URL: http://svn.apache.org/viewvc/activemq/trunk/activemq-core/src/main/java/org/apache/activemq/security/CachedLDAPAuthorizationMap.java?rev=1347580&r1=1347579&r2=1347580&view=diff
==============================================================================
--- activemq/trunk/activemq-core/src/main/java/org/apache/activemq/security/CachedLDAPAuthorizationMap.java (original)
+++ activemq/trunk/activemq-core/src/main/java/org/apache/activemq/security/CachedLDAPAuthorizationMap.java Thu Jun  7 11:58:40 2012
@@ -21,30 +21,35 @@ import org.apache.activemq.command.Activ
 import org.apache.activemq.command.ActiveMQTopic;
 import org.apache.activemq.filter.DestinationMapEntry;
 import org.apache.activemq.jaas.GroupPrincipal;
+import org.apache.activemq.jaas.UserPrincipal;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
 import org.springframework.beans.factory.InitializingBean;
 
 import javax.naming.Binding;
 import javax.naming.Context;
+import javax.naming.InvalidNameException;
 import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
 import javax.naming.directory.*;
 import javax.naming.event.*;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+
 import java.util.*;
 
 /**
- * A {@link DefaultAuthorizationMap} implementation which uses LDAP to initialize and update
+ * A {@link DefaultAuthorizationMap} implementation which uses LDAP to initialize and update authorization
+ * policy.
  *
  * @org.apache.xbean.XBean
- *
  */
-public class CachedLDAPAuthorizationMap extends DefaultAuthorizationMap implements NamespaceChangeListener,
-        ObjectChangeListener, InitializingBean {
+public class CachedLDAPAuthorizationMap extends DefaultAuthorizationMap implements InitializingBean, DisposableBean {
 
     private static final Logger LOG = LoggerFactory.getLogger(CachedLDAPAuthorizationMap.class);
 
-
+    // Configuration Options
     private String initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
     private String connectionURL = "ldap://localhost:1024";
     private String connectionUsername = "uid=admin,ou=system";
@@ -52,17 +57,52 @@ public class CachedLDAPAuthorizationMap 
     private String connectionProtocol = "s";
     private String authentication = "simple";
 
-    private String baseDn = "ou=system";
-    private int cnsLength = 5;
+    
+    private int queuePrefixLength = 4;
+    private int topicPrefixLength = 4;
+    private int tempPrefixLength = 4;
+    
+    private String queueSearchBase = "ou=Queue,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 permissionGroupMemberAttribute = "member";
+    
+    private String adminPermissionGroupSearchFilter = "(cn=Admin)";
+    private String readPermissionGroupSearchFilter = "(cn=Read)";
+    private String writePermissionGroupSearchFilter = "(cn=Write)";
+    
+    private boolean legacyGroupMapping = true;
+    private String groupObjectClass = "groupOfNames";
+    private String userObjectClass = "person";
+    private String groupNameAttribute = "cn";
+    private String userNameAttribute = "uid";
 
+    
     private int refreshInterval = -1;
+    private boolean refreshDisabled = false;
+    
+    // Internal State
     private long lastUpdated;
 
     private static String ANY_DESCENDANT = "\\$";
 
     private DirContext context;
     private EventDirContext eventContext;
-
+    
+    protected HashMap<ActiveMQDestination, AuthorizationEntry> entries = 
+            new HashMap<ActiveMQDestination, AuthorizationEntry>();
+
+    /**
+     * Returns the existing open context or creates a new one and registers listeners for
+     * push notifications if such an update style is enabled.  This implementation should not
+     * be invoked concurrently.
+     *
+     * @return the current context
+     *
+     * @throws NamingException if there is an error setting things up
+     */
     protected DirContext open() throws NamingException {
         if (context != null) {
             return context;
@@ -83,228 +123,757 @@ public class CachedLDAPAuthorizationMap 
             context = new InitialDirContext(env);
 
 
-            if (refreshInterval == -1) {
+            if (refreshInterval == -1 && !refreshDisabled) {
                 eventContext = ((EventDirContext)context.lookup(""));
+                
                 final SearchControls constraints = new SearchControls();
                 constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
-                LOG.debug("Listening for: " + "'ou=Destination,ou=ActiveMQ," + baseDn + "'");
-                eventContext.addNamingListener("ou=Destination,ou=ActiveMQ," + baseDn, "cn=*", constraints, this);
+
+                // Listeners for Queue policy //
+                
+                // Listeners for each type of permission
+                for (PermissionType permissionType : PermissionType.values()) {
+                    eventContext.addNamingListener(queueSearchBase, getFilterForPermissionType(permissionType), constraints,
+                            this.new CachedLDAPAuthorizationMapNamespaceChangeListener(DestinationType.QUEUE, permissionType));
+                }
+                // Listener for changes to the destination pattern entry itself and not a permission entry.
+                eventContext.addNamingListener(queueSearchBase, "cn=*", new SearchControls(),
+                        this.new CachedLDAPAuthorizationMapNamespaceChangeListener(DestinationType.QUEUE, null));
+                
+                // Listeners for Topic policy //
+                
+                // Listeners for each type of permission
+                for (PermissionType permissionType : PermissionType.values()) {
+                    eventContext.addNamingListener(topicSearchBase, getFilterForPermissionType(permissionType), constraints,
+                            this.new CachedLDAPAuthorizationMapNamespaceChangeListener(DestinationType.TOPIC, permissionType));
+                }
+                // Listener for changes to the destination pattern entry itself and not a permission entry.
+                eventContext.addNamingListener(topicSearchBase, "cn=*", new SearchControls(),
+                        this.new CachedLDAPAuthorizationMapNamespaceChangeListener(DestinationType.TOPIC, null));
+                
+                // Listeners for Temp policy //
+                
+                // Listeners for each type of permission
+                for (PermissionType permissionType : PermissionType.values()) {
+                    eventContext.addNamingListener(tempSearchBase, getFilterForPermissionType(permissionType), constraints,
+                            this.new CachedLDAPAuthorizationMapNamespaceChangeListener(DestinationType.TEMP, permissionType));
+                }
             }
         } catch (NamingException e) {
-            LOG.error(e.toString());
+            context = null;
             throw e;
         }
-        return context;
-    }
 
-    HashMap<ActiveMQDestination, AuthorizationEntry> entries = new HashMap<ActiveMQDestination, AuthorizationEntry>();
+        return context;
+    } 
 
+    /**
+     * Queries the directory and initializes the policy based on the data in the directory.
+     * This implementation should not be invoked concurrently.
+     * 
+     * @throws Exception if there is an unrecoverable error processing the directory contents
+     */
     @SuppressWarnings("rawtypes")
-    public void query() throws Exception {
-        try {
-            context = open();
-        } catch (NamingException e) {
-            LOG.error(e.toString());
-        }
-
+    protected void query() throws Exception {
+        DirContext currentContext = open();
+    
         final SearchControls constraints = new SearchControls();
         constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
-
-        NamingEnumeration<?> results = context.search("ou=Destination,ou=ActiveMQ," + baseDn, "(|(cn=admin)(cn=write)(cn=read))", constraints);
-        while (results.hasMore()) {
-            SearchResult result = (SearchResult) results.next();
-            AuthorizationEntry entry = getEntry(result.getNameInNamespace());
-            applyACL(entry, result);
+       
+        for (PermissionType permissionType : PermissionType.values()) {
+            try {
+                processQueryResults(
+                        currentContext.search(queueSearchBase, getFilterForPermissionType(permissionType), constraints),
+                        DestinationType.QUEUE, permissionType);
+            } catch (Exception e) {
+                LOG.error("Policy not applied!.  Error processing policy under '" + queueSearchBase + "' with filter '" 
+                        + getFilterForPermissionType(permissionType) + "'", e);
+            }
         }
-
+        
+        for (PermissionType permissionType : PermissionType.values()) {
+            try {
+                processQueryResults(
+                        currentContext.search(topicSearchBase, getFilterForPermissionType(permissionType), constraints),
+                        DestinationType.TOPIC, permissionType);
+            } catch (Exception e) {
+                LOG.error("Policy not applied!.  Error processing policy under '" + topicSearchBase + "' with filter '" 
+                        + getFilterForPermissionType(permissionType) + "'", e);
+            }
+        }
+        
+        for (PermissionType permissionType : PermissionType.values()) {
+            try {
+                processQueryResults(
+                        currentContext.search(tempSearchBase, getFilterForPermissionType(permissionType), constraints),
+                        DestinationType.TEMP, permissionType);
+            } catch (Exception e) {
+                LOG.error("Policy not applied!.  Error processing policy under '" + tempSearchBase + "' with filter '" 
+                        + getFilterForPermissionType(permissionType) + "'", e);
+            }
+        }
+        
         setEntries(new ArrayList<DestinationMapEntry>(entries.values()));
         updated();
     }
+    
+    /**
+     * Processes results from a directory query in the context of a given destination type and permission type.
+     * This implementation should not be invoked concurrently.
+     *
+     * @param results the results to process
+     * @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
+     */
+    protected void processQueryResults(NamingEnumeration<SearchResult> results,
+            DestinationType destinationType, PermissionType permissionType) throws Exception {
+        
+        while (results.hasMore()) {
+            SearchResult result = results.next();
+            AuthorizationEntry entry = null;
+            
+            try {
+                entry = getEntry(new LdapName(result.getNameInNamespace()), destinationType);
+            } catch (Exception e) {
+                LOG.error("Policy not applied!  Error parsing authorization policy entry under "
+                        + result.getNameInNamespace(), e);
+                continue;
+            }
+                
+            applyACL(entry, result, permissionType);
+        }
+    }
 
+    /**
+     * Marks the time at which the authorization state was last refreshed.  Relevant for synchronous policy updates.
+     * This implementation should not be invoked concurrently.
+     */
     protected void updated() {
         lastUpdated = System.currentTimeMillis();
     }
 
-    protected AuthorizationEntry getEntry(String name) {;
-            String[] cns = name.split(",");
-
-            // handle temp entry
-            if (cns.length == cnsLength && cns[1].equals("ou=Temp")) {
-                TempDestinationAuthorizationEntry tempEntry = getTempDestinationAuthorizationEntry();
-                if (tempEntry == null) {
-                    tempEntry = new TempDestinationAuthorizationEntry();
-                    setTempDestinationAuthorizationEntry(tempEntry);
+    /**
+     * Retrieves or creates the {@link AuthorizationEntry} that corresponds to
+     * the DN in {@code dn}.  This implementation should not be invoked concurrently.
+     * 
+     * @param dn
+     *            the DN representing the policy entry in the directory
+     * @param destinationType the type of the destination to get/create the entry for
+     * 
+     * @return the corresponding authorization entry for the DN
+     * 
+     * @throws IllegalArgumentException
+     *             if destination type is not one of {@link DestinationType#QUEUE}, {@link DestinationType#TOPIC},
+     *             {@link DestinationType#TEMP} or if the policy entry DN is malformed
+     */
+    protected AuthorizationEntry getEntry(LdapName dn, DestinationType destinationType) {
+        
+        AuthorizationEntry entry = null;
+        
+        
+        switch (destinationType) {
+            case TEMP:
+                // handle temp entry
+                if (dn.size() != getPrefixLengthForDestinationType(destinationType) + 1) {
+                    // handle unknown entry
+                    throw new IllegalArgumentException("Malformed policy structure for a temporary destination "
+                            + "policy entry.  The permission group entries should be immediately below the "
+                            + "temporary policy base DN.");
                 }
-                return tempEntry;
-            }
-
-            // handle regular destinations
-            if (cns.length != (cnsLength + 1)) {
-                LOG.warn("Policy not applied! Wrong cn for authorization entry " + name);
-            }
-
-            ActiveMQDestination dest = formatDestination(cns[1], cns[2]);
-
-            if (dest != null) {
-                AuthorizationEntry entry = entries.get(dest);
+                entry = getTempDestinationAuthorizationEntry();
                 if (entry == null) {
-                    entry = new AuthorizationEntry();
-                    entry.setDestination(dest);
-                    entries.put(dest, entry);
+                    entry = new TempDestinationAuthorizationEntry();
+                    setTempDestinationAuthorizationEntry((TempDestinationAuthorizationEntry) entry);
                 }
-                return entry;
-            } else {
-                return null;
-            }
+                
+                break;
+                
+            case QUEUE:
+            case TOPIC:
+                // handle regular destinations
+                if (dn.size() != getPrefixLengthForDestinationType(destinationType) + 2) {
+                    throw new IllegalArgumentException("Malformed policy structure for a queue or topic destination "
+                            + "policy entry.  The destination pattern and permission group entries should be "
+                            + "nested below the queue or topic policy base DN.");
+                }
+                
+                ActiveMQDestination dest = formatDestination(dn, destinationType);
+
+                if (dest != null) {
+                    entry = entries.get(dest);
+                    if (entry == null) {
+                        entry = new AuthorizationEntry();
+                        entry.setDestination(dest);
+                        entries.put(dest, entry);
+                    }
+                }
+                
+                break;
+            default:
+                // handle unknown entry
+                throw new IllegalArgumentException("Unknown destination type " + destinationType);
+        }
+        
+        return entry;
     }
 
-    protected ActiveMQDestination formatDestination(String destinationName, String destinationType) {
-            ActiveMQDestination dest = null;
-            if (destinationType.equalsIgnoreCase("ou=queue")) {
-               dest = new ActiveMQQueue(formatDestinationName(destinationName));
-            } else if (destinationType.equalsIgnoreCase("ou=topic")) {
-               dest = new ActiveMQTopic(formatDestinationName(destinationName));
+    /**
+     * Applies the policy from the directory to the given entry within the context of the provided
+     * permission type.
+     *
+     * @param entry the policy entry to apply the policy to
+     * @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
+     */
+    protected void applyACL(AuthorizationEntry entry, SearchResult result,
+            PermissionType permissionType) throws NamingException {
+        
+        // Find members
+        Attribute memberAttribute = result.getAttributes().get(permissionGroupMemberAttribute);
+        NamingEnumeration<?> memberAttributeEnum = memberAttribute.getAll();
+        
+        HashSet<Object> members = new HashSet<Object>();
+        
+        while (memberAttributeEnum.hasMoreElements()) {
+            String memberDn = (String) memberAttributeEnum.nextElement();
+            boolean group = false;
+            boolean user = false;
+            String principalName = null;
+            
+            if (!legacyGroupMapping) {
+                // Lookup of member to determine principal type (group or user) and name.
+                Attributes memberAttributes;
+                try {
+                    memberAttributes = context.getAttributes(memberDn, 
+                            new String[] {"objectClass", groupNameAttribute, userNameAttribute});
+                } catch (NamingException e) {
+                    LOG.error(
+                            "Policy not applied! Unknown member " + memberDn
+                                    + " in policy entry "
+                                    + result.getNameInNamespace(), e);
+                    continue;
+                }
+                
+                Attribute memberEntryObjectClassAttribute = memberAttributes.get("objectClass");
+                NamingEnumeration<?> memberEntryObjectClassAttributeEnum = memberEntryObjectClassAttribute.getAll();
+                
+                while (memberEntryObjectClassAttributeEnum.hasMoreElements()) {
+                    String objectClass = (String) memberEntryObjectClassAttributeEnum.nextElement();
+                    
+                    if (objectClass.equalsIgnoreCase(groupObjectClass)) {
+                        group = true;
+                        Attribute name = memberAttributes.get(groupNameAttribute);
+                        if (name == null) {
+                            LOG.error("Policy not applied! Group "
+                                    + memberDn
+                                    + "does not have name attribute "
+                                    + groupNameAttribute + " under entry " + result.getNameInNamespace());
+                            break;
+                        }
+                        
+                        principalName = (String) name.get();
+                    }
+                    
+                    if (objectClass.equalsIgnoreCase(userObjectClass)) {
+                        user = true;
+                        Attribute name = memberAttributes.get(userNameAttribute);
+                        if (name == null) {
+                            LOG.error("Policy not applied! User "
+                                    + memberDn + " does not have name attribute "
+                                    + userNameAttribute + " under entry " + result.getNameInNamespace());
+                            break;
+                        }
+                        
+                        principalName = (String) name.get();
+                    }
+                }
+                
             } else {
-                LOG.warn("Policy not applied! Unknown destination type " + destinationType);
+                group = true;
+                principalName = memberDn.replaceAll("(cn|CN)=", "");
             }
-            return dest;
+            
+            if ((!group && !user) || (group && user)) {
+                LOG.error("Policy not applied! Can't determine type of member "
+                        + memberDn + " under entry " + result.getNameInNamespace());
+            } else if (principalName != null){
+                if (group && !user) {
+                    members.add(new GroupPrincipal(principalName));
+                } else if (!group && user) {
+                    members.add(new UserPrincipal(principalName));
+                }
+            }
+        }
+        
+        try {
+            applyAcl(entry, permissionType, members);
+        } catch (Exception e) {
+            LOG.error(
+                    "Policy not applied! Error adding principals to ACL under "
+                            + result.getNameInNamespace(), e);
+        }
     }
-
-    protected void applyACL(AuthorizationEntry entry, SearchResult result) throws NamingException {
-        // find members
-        Attribute cn = result.getAttributes().get("cn");
-        Attribute member = result.getAttributes().get("member");
-        NamingEnumeration<?> memberEnum = member.getAll();
-        HashSet<Object> members = new HashSet<Object>();
-        while (memberEnum.hasMoreElements()) {
-            String elem = (String) memberEnum.nextElement();
-            members.add(new GroupPrincipal(elem.replaceAll("cn=", "")));
+    
+    /**
+     * 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 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
+     */
+    protected void applyAcl(AuthorizationEntry entry, PermissionType permissionType, Set<Object> acls) {
+        
+        switch (permissionType) {
+            case READ:
+                entry.setReadACLs(acls);
+                break;
+            case WRITE:
+                entry.setWriteACLs(acls);
+                break;
+            case ADMIN:
+                entry.setAdminACLs(acls);
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown permission " + permissionType + ".");
         }
-
-        // apply privilege
-        if (cn.get().equals("admin")) {
-            entry.setAdminACLs(members);
-        } else if (cn.get().equals("write")) {
-            entry.setWriteACLs(members);
-        } else if (cn.get().equals("read")) {
-            entry.setReadACLs(members);
-        } else {
-            LOG.warn("Policy not applied! Unknown privilege " + result.getName());
+    }
+    
+    /**
+     * Parses a DN into the equivalent {@link ActiveMQDestination}.  The default implementation
+     * expects a format of cn=<PERMISSION_NAME>,ou=<DESTINATION_PATTERN>,.... or 
+     * ou=<DESTINATION_PATTERN>,.... for permission and destination entries, respectively.
+     * For example {@code cn=admin,ou=$,ou=...} or {@code ou=$,ou=...}. 
+     *
+     * @param dn the DN to parse
+     * @param destinationType the type of the destination that we are parsing
+     *
+     * @return the destination that the DN represents
+     *
+     * @throws IllegalArgumentException 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)
+     */
+    protected ActiveMQDestination formatDestination(LdapName dn, DestinationType destinationType) {
+        ActiveMQDestination destination = null;
+        
+        switch (destinationType) {
+            case QUEUE:
+            case TOPIC:
+                // There exists a need to deal with both names representing a permission or simply a
+                // destination.  As such, we need to determine the proper RDN to work with based
+                // on the destination type and the DN size.
+                if (dn.size() == (getPrefixLengthForDestinationType(destinationType) + 2)) {
+                    destination = formatDestination(dn.getRdn(dn.size() - 2), destinationType);
+                } else if (dn.size() == (getPrefixLengthForDestinationType(destinationType) + 1)){
+                    destination = formatDestination(dn.getRdn(dn.size() - 1), destinationType);
+                } else {
+                    throw new IllegalArgumentException(
+                            "Malformed DN for representing a permission or destination entry.");
+                }
+                break;
+            default:
+                throw new IllegalArgumentException(
+                        "Cannot format destination for destination type " + destinationType);
         }
+        
+        return destination;
     }
+    
+    /**
+     * Parses RDN values representing the destination name/pattern and
+     * destination type into the equivalent {@link ActiveMQDestination}.
+     * 
+     * @param destinationName
+     *            the RDN representing the name or pattern for the destination
+     * @param destinationType
+     *            the type of the destination
+     * 
+     * @return the destination that the RDN represent
+     * 
+     * @throws IllegalArgumentException
+     *             if {@code destinationType} is not one of {@link DestinationType#TOPIC} or
+     *             {@link DestinationType#QUEUE}.
+     * 
+     * @see #formatDestinationName(Rdn)
+     * @see #formatDestination(LdapName, DestinationType)
+     */
+    protected ActiveMQDestination formatDestination(Rdn destinationName, DestinationType destinationType) {
+        ActiveMQDestination dest = null;
+        
+        switch (destinationType) {
+            case QUEUE:
+                dest = new ActiveMQQueue(formatDestinationName(destinationName));
+                break;
+            case TOPIC:
+                dest = new ActiveMQTopic(formatDestinationName(destinationName));
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown destination type: "
+                        + destinationType);
+        }
 
-    protected String formatDestinationName(String cn) {
-        return cn.replaceFirst("cn=", "").replaceAll(ANY_DESCENDANT, ">");
+        return dest;
     }
 
-    protected boolean isPriviledge(Binding binding) {
-        String name = binding.getName();
-        if (name.startsWith("cn=admin") || name.startsWith("cn=write") || name.startsWith("cn=read")) {
-            return true;
+    /**
+     * Parses the RDN representing a destination name/pattern into the standard string representation
+     * of the name/pattern.  This implementation does not care about the type of the RDN such that the RDN could
+     * be a CN or OU.
+     *
+     * @param destinationName the RDN representing the name or pattern for the destination
+     *
+     * @see #formatDestination(Rdn, Rdn)
+     */
+    protected String formatDestinationName(Rdn destinationName) {
+        return destinationName.getValue().toString().replaceAll(ANY_DESCENDANT, ">");
+    }
+    
+    /**
+     * Transcribes an existing set into a new set. Used to make defensive copies
+     * for concurrent access.
+     * 
+     * @param source
+     *            the source set or {@code null}
+     * 
+     * @return a new set containing the same elements as {@code source} or
+     *         {@code null} if {@code source} is {@code null}
+     */
+    protected <T> Set<T> transcribeSet(Set<T> source) {
+        if (source != null) {
+            return new HashSet<T>(source);
         } else {
-            return false;
+            return null;
         }
     }
-
-    @Override
-    protected Set<AuthorizationEntry> getAllEntries(ActiveMQDestination destination) {
-        if (refreshInterval != -1 && System.currentTimeMillis() >= lastUpdated + refreshInterval) {
+    
+    /**
+     * Returns the filter string for the given permission type.
+     * 
+     * @throws IllegalArgumentException if {@code permissionType} is not supported
+     *
+     * @see #setAdminPermissionGroupSearchFilter(String)
+     * @see #setReadPermissionGroupSearchFilter(String)
+     * @see #setWritePermissionGroupSearchFilter(String)
+     */
+    protected String getFilterForPermissionType(PermissionType permissionType) {
+        String filter = null;
+        
+        switch (permissionType) {
+            case ADMIN:
+                filter = adminPermissionGroupSearchFilter;
+                break;
+            case READ:
+                filter = readPermissionGroupSearchFilter;
+                break;
+            case WRITE:
+                filter = writePermissionGroupSearchFilter;
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown permission type " + permissionType);
+        }
+        
+        return filter;
+    }
+    
+    /**
+     * Returns the DN prefix size based on the given destination type.
+     *
+     * @throws IllegalArgumentException if {@code destinationType} is not supported
+     *
+     * @see #setQueueSearchBase(String)
+     * @see #setTopicSearchBase(String)
+     * @see #setTempSearchBase(String)
+     */
+    protected int getPrefixLengthForDestinationType(DestinationType destinationType) {
+        int filter = 0;
+        
+        switch (destinationType) {
+            case QUEUE:
+                filter = queuePrefixLength;
+                break;
+            case TOPIC:
+                filter = topicPrefixLength;
+                break;
+            case TEMP:
+                filter = tempPrefixLength;
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown permission type " + destinationType);
+        }
+        
+        return filter;
+    }
+    
+    /**
+     * Performs a check for updates from the server in the event that synchronous updates are enabled 
+     * and are the refresh interval has elapsed.
+     */
+    protected void checkForUpdates() {
+        if (!refreshDisabled && (refreshInterval != -1 && System.currentTimeMillis() >= lastUpdated + refreshInterval)) {
 
             reset();
+            setTempDestinationAuthorizationEntry(null);
             entries.clear();
 
             LOG.debug("Updating authorization map!");
             try {
                 query();
             } catch (Exception e) {
-                LOG.error("Error updating authorization map", e);
+                LOG.error("Error updating authorization map.  Partial policy "
+                        + "may be applied until the next successful update.", e);
             }
         }
-
+    }
+    
+    // Authorization Map
+    
+    /**
+     * Provides synchronous refresh capabilities if so configured before delegating to the super implementation,
+     * and otherwise simply delegates to the super implementation.
+     */
+    @Override
+    protected synchronized Set<AuthorizationEntry> getAllEntries(ActiveMQDestination destination) {
+        checkForUpdates();
         return super.getAllEntries(destination);
     }
-
+    
+    /**
+     * Provides synchronized and defensive access to the admin ACLs for temp destinations as the super
+     * implementation returns live copies of the ACLs and {@link AuthorizationEntry} is not
+     * setup for concurrent access.
+     */
     @Override
-    public void objectAdded(NamingEvent namingEvent) {
+    public synchronized Set<Object> getTempDestinationAdminACLs() {
+        checkForUpdates();
+        return transcribeSet(super.getTempDestinationAdminACLs());
+    }
+    
+    /**
+     * Provides synchronized and defensive access to the read ACLs for temp destinations as the super
+     * implementation returns live copies of the ACLs and {@link AuthorizationEntry} is not
+     * setup for concurrent access.
+     */
+    public synchronized Set<Object> getTempDestinationReadACLs() {
+        checkForUpdates();
+        return transcribeSet(super.getTempDestinationReadACLs());
+    }
+
+    /**
+     * Provides synchronized and defensive access to the write ACLs for temp destinations as the super
+     * implementation returns live copies of the ACLs and {@link AuthorizationEntry} is not
+     * setup for concurrent access.
+     */
+    public synchronized Set<Object> getTempDestinationWriteACLs() {
+        checkForUpdates();
+        return transcribeSet(super.getTempDestinationWriteACLs());
+    }
+    
+    /**
+     * Provides synchronized access to the admin ACLs for the destinations as 
+     * {@link AuthorizationEntry} is not setup for concurrent access.
+     */
+    public synchronized Set<Object> getAdminACLs(ActiveMQDestination destination) {
+        return super.getAdminACLs(destination);
+    }
+
+    /**
+     * Provides synchronized access to the read ACLs for the destinations as 
+     * {@link AuthorizationEntry} is not setup for concurrent access.
+     */
+    public synchronized Set<Object> getReadACLs(ActiveMQDestination destination) {
+        return super.getReadACLs(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) {
+        return super.getWriteACLs(destination);
+    }
+
+    /**
+     * Handler for new policy entries in the directory.
+     *
+     * @param namingEvent the new entry event that occurred 
+     * @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,
+            PermissionType permissionType) {
         LOG.debug("Adding object: " + namingEvent.getNewBinding());
-        SearchResult result = (SearchResult)namingEvent.getNewBinding();
-        if (!isPriviledge(result)) return;
-        AuthorizationEntry entry = getEntry(result.getName());
-        if (entry != null) {
-            try {
-                applyACL(entry, result);
-                if (!(entry instanceof TempDestinationAuthorizationEntry)) {
-                    put(entry.getDestination(), entry);
-                }
-            } catch (NamingException ne) {
-                LOG.warn("Unable to add entry", ne);
+        SearchResult result = (SearchResult) namingEvent.getNewBinding();
+        
+        try {
+            LdapName name = new LdapName(result.getName());
+            
+            AuthorizationEntry entry = getEntry(name, destinationType);
+               
+            applyACL(entry, result, permissionType);
+            if (!(entry instanceof TempDestinationAuthorizationEntry)) {
+                put(entry.getDestination(), entry);
             }
+            
+        } catch (InvalidNameException e) {
+            LOG.error("Policy not applied!  Error parsing DN for addition of "
+                    + result.getName(), e);
+        } catch (Exception e) {
+            LOG.error("Policy not applied!  Error processing object addition for addition of "
+                    + result.getName(), e);
         }
     }
 
-    @Override
-    public void objectRemoved(NamingEvent namingEvent) {
+    /**
+     * Handler for removed policy entries in the directory.
+     *
+     * @param namingEvent the removed entry event that occurred 
+     * @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,
+            PermissionType permissionType) {
         LOG.debug("Removing object: " + namingEvent.getOldBinding());
         Binding result = namingEvent.getOldBinding();
-        if (!isPriviledge(result)) return;
-        AuthorizationEntry entry = getEntry(result.getName());
-        String[] cns = result.getName().split(",");
-        if (!isPriviledge(result)) return;
-        if (cns[0].equalsIgnoreCase("cn=admin")) {
-            entry.setAdminACLs(new HashSet<Object>());
-        } else if (cns[0].equalsIgnoreCase("cn=write")) {
-            entry.setWriteACLs(new HashSet<Object>());
-        } else if (cns[0].equalsIgnoreCase("cn=read")) {
-            entry.setReadACLs(new HashSet<Object>());
-        } else {
-            LOG.warn("Policy not removed! Unknown privilege " + result.getName());
+        
+        try {
+            LdapName name = new LdapName(result.getName());
+            
+            AuthorizationEntry entry = getEntry(name, destinationType);
+
+            applyAcl(entry, permissionType, new HashSet<Object>());
+        } catch (InvalidNameException e) {
+            LOG.error("Policy not applied!  Error parsing DN for object removal for removal of "
+                    + result.getName(), e);
+        } catch (Exception e) {
+            LOG.error("Policy not applied!  Error processing object removal for removal of "
+                    + result.getName(), e);
         }
     }
 
-    @Override
-    public void objectRenamed(NamingEvent namingEvent) {
+    /**
+     * Handler for renamed policy entries in the directory.  This handler deals with the renaming
+     * of destination entries as well as permission entries.  If the permission type is not null, it is
+     * assumed that we are dealing with the renaming of a permission entry.  Otherwise, it is assumed
+     * that we are dealing with the renaming of a destination entry.
+     *
+     * @param namingEvent the renaming entry event that occurred 
+     * @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,
+            PermissionType permissionType) {
         Binding oldBinding = namingEvent.getOldBinding();
         Binding newBinding = namingEvent.getNewBinding();
         LOG.debug("Renaming object: " + oldBinding + " to " + newBinding);
 
-        String[] oldCns = oldBinding.getName().split(",");
-        ActiveMQDestination oldDest = formatDestination(oldCns[0], oldCns[1]);
-
-        String[] newCns = newBinding.getName().split(",");
-        ActiveMQDestination newDest = formatDestination(newCns[0], newCns[1]);
-
-        if (oldDest != null && newDest != null) {
-            AuthorizationEntry entry = entries.remove(oldDest);
-            if (entry != null) {
-                entry.setDestination(newDest);
-                put(newDest, entry);
-                remove(oldDest, entry);
+        try {
+            LdapName oldName = new LdapName(oldBinding.getName());
+            ActiveMQDestination oldDest = formatDestination(oldName, destinationType);
+    
+            LdapName newName = new LdapName(newBinding.getName());
+            ActiveMQDestination newDest = formatDestination(newName, destinationType);
+            
+            if (permissionType != null) {
+                // Handle the case where a permission entry is being renamed.
+                objectRemoved(namingEvent, destinationType, permissionType);
+                
+                SearchControls controls = new SearchControls();
+                controls.setSearchScope(SearchControls.OBJECT_SCOPE);
+                
+                boolean matchedToType = false;
+                
+                for (PermissionType newPermissionType : PermissionType.values()) {
+                    NamingEnumeration<SearchResult> results = context.search(
+                            newName,
+                            getFilterForPermissionType(newPermissionType), controls);
+                    
+                    if (results.hasMore()) {
+                        objectAdded(namingEvent, destinationType, newPermissionType);
+                        matchedToType = true;
+                        break;
+                    }
+                }
+                
+                if (!matchedToType) {
+                    LOG.error("Policy not applied!  Error processing object rename for rename of "
+                            + oldBinding.getName() + " to " + newBinding.getName()
+                            + ".  Could not determine permission type of new object.");
+                }
+                
             } else {
-                LOG.warn("No authorization entry for " + oldDest);
+                // Handle the case where a destination entry is being renamed.
+                if (oldDest != null && newDest != null) {
+                    AuthorizationEntry entry = entries.remove(oldDest);
+                    if (entry != null) {
+                        entry.setDestination(newDest);
+                        put(newDest, entry);
+                        remove(oldDest, entry);
+                        entries.put(newDest, entry);
+                    } else {
+                        LOG.warn("No authorization entry for " + oldDest);
+                    }
+                }
             }
+        } catch (InvalidNameException e) {
+            LOG.error("Policy not applied!  Error parsing DN for object rename for rename of "
+                    + oldBinding.getName() + " to " + newBinding.getName(), e);
+        } catch (Exception e) {
+            LOG.error("Policy not applied!  Error processing object rename for rename of "
+                    + oldBinding.getName() + " to " + newBinding.getName(), e);
         }
     }
 
-    @Override
-    public void objectChanged(NamingEvent namingEvent) {
+    /**
+     * Handler for changed policy entries in the directory.
+     *
+     * @param namingEvent the changed entry event that occurred 
+     * @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,
+            DestinationType destinationType, PermissionType permissionType) {
         LOG.debug("Changing object " + namingEvent.getOldBinding() + " to " + namingEvent.getNewBinding());
-        objectRemoved(namingEvent);
-        objectAdded(namingEvent);
+        objectRemoved(namingEvent, destinationType, permissionType);
+        objectAdded(namingEvent, destinationType, permissionType);
     }
 
-    @Override
+    /**
+     * Handler for exception events from the registry.
+     *
+     * @param namingExceptionEvent the exception event
+     */
     public void namingExceptionThrown(NamingExceptionEvent namingExceptionEvent) {
-        LOG.error("Caught Unexpected Exception", namingExceptionEvent.getException());
+        LOG.error("Caught unexpected exception.", namingExceptionEvent.getException());
     }
-
-    // init
-
+    
+    // Init / Destroy
+    
     @Override
     public void afterPropertiesSet() throws Exception {
         query();
     }
+    
+    @Override
+    public void destroy() throws Exception {
+        if (eventContext != null) {
+            eventContext.close();
+            eventContext = null;
+        }
+        
+        if (context != null) {
+            context.close();
+            context = null;
+        }
+    }
 
-    // getters and setters
+    // Getters and Setters
 
     public String getConnectionURL() {
         return connectionURL;
@@ -345,16 +914,133 @@ public class CachedLDAPAuthorizationMap 
     public void setAuthentication(String authentication) {
         this.authentication = authentication;
     }
+    
+    public String getQueueSearchBase() {
+        return queueSearchBase;
+    }
+
+    public void setQueueSearchBase(String queueSearchBase) {
+        try {
+            LdapName baseName = new LdapName(queueSearchBase);
+            queuePrefixLength = baseName.size();
+            this.queueSearchBase = queueSearchBase;
+        } catch (InvalidNameException e) {
+            throw new IllegalArgumentException("Invalid base DN value " + queueSearchBase, e);
+        }
+    }
+
+    public String getTopicSearchBase() {
+        return topicSearchBase;
+    }
+
+    public void setTopicSearchBase(String topicSearchBase) {
+        try {
+            LdapName baseName = new LdapName(topicSearchBase);
+            topicPrefixLength = baseName.size();
+            this.topicSearchBase = topicSearchBase;
+        } catch (InvalidNameException e) {
+            throw new IllegalArgumentException("Invalid base DN value " + topicSearchBase, e);
+        }
+    }
+
+    public String getTempSearchBase() {
+        return tempSearchBase;
+    }
+
+    public void setTempSearchBase(String tempSearchBase) {
+        try {
+            LdapName baseName = new LdapName(tempSearchBase);
+            tempPrefixLength = baseName.size();
+            this.tempSearchBase = tempSearchBase;
+        } catch (InvalidNameException e) {
+            throw new IllegalArgumentException("Invalid base DN value " + tempSearchBase, e);
+        }
+    }
+
+    public String getPermissionGroupMemberAttribute() {
+        return permissionGroupMemberAttribute;
+    }
+
+    public void setPermissionGroupMemberAttribute(
+            String permissionGroupMemberAttribute) {
+        this.permissionGroupMemberAttribute = permissionGroupMemberAttribute;
+    }
+    
+    public String getAdminPermissionGroupSearchFilter() {
+        return adminPermissionGroupSearchFilter;
+    }
+
+    public void setAdminPermissionGroupSearchFilter(
+            String adminPermissionGroupSearchFilter) {
+        this.adminPermissionGroupSearchFilter = adminPermissionGroupSearchFilter;
+    }
+
+    public String getReadPermissionGroupSearchFilter() {
+        return readPermissionGroupSearchFilter;
+    }
 
-    public String getBaseDn() {
-        return baseDn;
+    public void setReadPermissionGroupSearchFilter(
+            String readPermissionGroupSearchFilter) {
+        this.readPermissionGroupSearchFilter = readPermissionGroupSearchFilter;
     }
 
-    public void setBaseDn(String baseDn) {
-        this.baseDn = baseDn;
-        cnsLength = baseDn.split(",").length + 4;
+    public String getWritePermissionGroupSearchFilter() {
+        return writePermissionGroupSearchFilter;
     }
 
+    public void setWritePermissionGroupSearchFilter(
+            String writePermissionGroupSearchFilter) {
+        this.writePermissionGroupSearchFilter = writePermissionGroupSearchFilter;
+    }
+    
+    public boolean isLegacyGroupMapping() {
+        return legacyGroupMapping;
+    }
+
+    public void setLegacyGroupMapping(boolean legacyGroupMapping) {
+        this.legacyGroupMapping = legacyGroupMapping;
+    }
+
+    public String getGroupObjectClass() {
+        return groupObjectClass;
+    }
+
+    public void setGroupObjectClass(String groupObjectClass) {
+        this.groupObjectClass = groupObjectClass;
+    }
+
+    public String getUserObjectClass() {
+        return userObjectClass;
+    }
+
+    public void setUserObjectClass(String userObjectClass) {
+        this.userObjectClass = userObjectClass;
+    }
+    
+    public String getGroupNameAttribute() {
+        return groupNameAttribute;
+    }
+
+    public void setGroupNameAttribute(String groupNameAttribute) {
+        this.groupNameAttribute = groupNameAttribute;
+    }
+
+    public String getUserNameAttribute() {
+        return userNameAttribute;
+    }
+
+    public void setUserNameAttribute(String userNameAttribute) {
+        this.userNameAttribute = userNameAttribute;
+    }
+
+    public boolean isRefreshDisabled() {
+        return refreshDisabled;
+    }
+
+    public void setRefreshDisabled(boolean refreshDisabled) {
+        this.refreshDisabled = refreshDisabled;
+    }
+    
     public int getRefreshInterval() {
         return refreshInterval;
     }
@@ -362,5 +1048,79 @@ public class CachedLDAPAuthorizationMap 
     public void setRefreshInterval(int refreshInterval) {
         this.refreshInterval = refreshInterval;
     }
-}
+    
+    protected static enum DestinationType {
+        QUEUE,
+        TOPIC,
+        TEMP;
+    }
+    
+    protected static enum PermissionType {
+        READ,
+        WRITE,
+        ADMIN;
+    }
+    
+    /**
+     * Listener implementation for directory changes that maps change events to
+     * destination types.
+     */
+    protected class CachedLDAPAuthorizationMapNamespaceChangeListener implements
+            NamespaceChangeListener, ObjectChangeListener {
+        
+        private final DestinationType destinationType;
+        private final PermissionType permissionType;
+        
+        /**
+         * Creates a new listener.  If {@code permissionType} is {@code null}, add
+         * and remove events are ignored as they do not directly affect policy state.
+         * This configuration is used when listening for changes on entries that represent
+         * destination patterns and not for entries that represent permissions.
+         *
+         * @param destinationType the type of the destination being listened for
+         * @param permissionType the optional permission type being listened for
+         */
+        public CachedLDAPAuthorizationMapNamespaceChangeListener(
+                DestinationType destinationType, PermissionType permissionType) {
+            this.destinationType = destinationType;
+            this.permissionType = permissionType;
+        }
+
+        @Override
+        public void namingExceptionThrown(NamingExceptionEvent evt) {
+            CachedLDAPAuthorizationMap.this.namingExceptionThrown(evt);
+        }
+
+        @Override
+        public void objectAdded(NamingEvent evt) {
+            // This test is a hack to work around the fact that Apache DS 2.0 seems to trigger notifications
+            // for the entire sub-tree even when one-level is the selected search scope.
+            if (permissionType != null) {
+                CachedLDAPAuthorizationMap.this.objectAdded(evt, destinationType, permissionType);
+            }
+        }
 
+        @Override
+        public void objectRemoved(NamingEvent evt) {
+            // This test is a hack to work around the fact that Apache DS 2.0 seems to trigger notifications
+            // for the entire sub-tree even when one-level is the selected search scope.
+            if (permissionType != null) {
+                CachedLDAPAuthorizationMap.this.objectRemoved(evt, destinationType, permissionType);
+            }
+        }
+
+        @Override
+        public void objectRenamed(NamingEvent evt) {
+            CachedLDAPAuthorizationMap.this.objectRenamed(evt, destinationType, permissionType);
+        }
+
+        @Override
+        public void objectChanged(NamingEvent evt) {
+            // This test is a hack to work around the fact that Apache DS 2.0 seems to trigger notifications
+            // for the entire sub-tree even when one-level is the selected search scope.
+            if (permissionType != null) {
+                CachedLDAPAuthorizationMap.this.objectChanged(evt, destinationType, permissionType);
+            }
+        }
+    }
+}

Added: activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/AbstractCachedLDAPAuthorizationMapLegacyTest.java
URL: http://svn.apache.org/viewvc/activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/AbstractCachedLDAPAuthorizationMapLegacyTest.java?rev=1347580&view=auto
==============================================================================
--- activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/AbstractCachedLDAPAuthorizationMapLegacyTest.java (added)
+++ activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/AbstractCachedLDAPAuthorizationMapLegacyTest.java Thu Jun  7 11:58:40 2012
@@ -0,0 +1,358 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.security;
+
+import org.apache.activemq.command.ActiveMQQueue;
+import org.apache.activemq.command.ActiveMQTopic;
+import org.apache.activemq.jaas.GroupPrincipal;
+import org.apache.directory.ldap.client.api.LdapConnection;
+import org.apache.directory.server.core.integ.AbstractLdapTestUnit;
+import org.apache.directory.shared.ldap.model.ldif.LdifEntry;
+import org.apache.directory.shared.ldap.model.ldif.LdifReader;
+import org.apache.directory.shared.ldap.model.message.ModifyRequest;
+import org.apache.directory.shared.ldap.model.message.ModifyRequestImpl;
+import org.apache.directory.shared.ldap.model.name.Dn;
+import org.apache.directory.shared.ldap.model.name.Rdn;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.Context;
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.directory.DirContext;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public abstract class AbstractCachedLDAPAuthorizationMapLegacyTest extends AbstractLdapTestUnit {
+
+    static final GroupPrincipal GUESTS = new GroupPrincipal("guests");
+    static final GroupPrincipal USERS = new GroupPrincipal("users");
+    static final GroupPrincipal ADMINS = new GroupPrincipal("admins");
+    
+    protected LdapConnection connection;
+    protected CachedLDAPAuthorizationMap map;
+    
+    @Before
+    public void setup() throws Exception {
+        connection = getLdapConnection();
+        map = createMap();
+    }
+    
+    @After
+    public void cleanup() throws Exception {
+        if (connection != null) {
+            try {
+                connection.close();
+            } catch (IOException e) {
+                // Ignore
+            }
+        }
+        
+        if (map != null) {
+            map.destroy();
+        }
+    }
+
+    @Test
+    public void testQuery() throws Exception {
+        map.query();
+        Set<?> readACLs = map.getReadACLs(new ActiveMQQueue("TEST.FOO"));
+        assertEquals("set size: " + readACLs, 2, readACLs.size());
+        assertTrue("Contains admin group", readACLs.contains(ADMINS));
+        assertTrue("Contains users group", readACLs.contains(USERS));
+
+        Set<?> failedACLs = map.getReadACLs(new ActiveMQQueue("FAILED"));
+        assertEquals("set size: " + failedACLs, 0, failedACLs.size());
+    }
+    
+    @Test
+    public void testSynchronousUpdate() throws Exception {
+        map.setRefreshInterval(1);
+        map.query();
+        Set<?> readACLs = map.getReadACLs(new ActiveMQQueue("TEST.FOO"));
+        assertEquals("set size: " + readACLs, 2, readACLs.size());
+        assertTrue("Contains admin group", readACLs.contains(ADMINS));
+        assertTrue("Contains users group", readACLs.contains(USERS));
+
+        Set<?> failedACLs = map.getReadACLs(new ActiveMQQueue("FAILED"));
+        assertEquals("set size: " + failedACLs, 0, failedACLs.size());
+        
+        LdifReader reader = new LdifReader(getRemoveLdif());
+
+        for (LdifEntry entry : reader) {
+            connection.delete(entry.getDn());
+        }
+        
+        failedACLs = map.getReadACLs(new ActiveMQQueue("TEST.FOO"));
+        assertEquals("set size: " + failedACLs, 0, failedACLs.size());
+        
+        assertNull(map.getTempDestinationReadACLs());
+        assertNull(map.getTempDestinationWriteACLs());
+        assertNull(map.getTempDestinationAdminACLs());
+    }
+
+    @Test
+    public void testWildcards() throws Exception {
+        map.query();
+        Set<?> fooACLs = map.getReadACLs(new ActiveMQQueue("FOO.1"));
+        assertEquals("set size: " + fooACLs, 2, fooACLs.size());
+        assertTrue("Contains admin group", fooACLs.contains(ADMINS));
+        assertTrue("Contains users group", fooACLs.contains(USERS));
+
+        Set<?> barACLs = map.getReadACLs(new ActiveMQQueue("BAR.2"));
+        assertEquals("set size: " + barACLs, 2, barACLs.size());
+        assertTrue("Contains admin group", barACLs.contains(ADMINS));
+        assertTrue("Contains users group", barACLs.contains(USERS));
+    }
+
+    @Test
+    public void testAdvisory() throws Exception {
+        map.query();
+        Set<?> readACLs = map.getReadACLs(new ActiveMQTopic("ActiveMQ.Advisory.Connection"));
+        assertEquals("set size: " + readACLs, 2, readACLs.size());
+        assertTrue("Contains admin group", readACLs.contains(ADMINS));
+        assertTrue("Contains users group", readACLs.contains(USERS));
+    }
+
+    @Test
+    public void testTemporary() throws Exception {
+        map.query();
+        
+        Thread.sleep(1000);
+        Set<?> readACLs = map.getTempDestinationReadACLs();
+        assertEquals("set size: " + readACLs, 2, readACLs.size());
+        assertTrue("Contains admin group", readACLs.contains(ADMINS));
+        assertTrue("Contains users group", readACLs.contains(USERS));
+    }
+
+    @Test
+    public void testAdd() throws Exception {
+        map.query();
+
+        Set<?> failedACLs = map.getReadACLs(new ActiveMQQueue("FAILED"));
+        assertEquals("set size: " + failedACLs, 0, failedACLs.size());
+
+        LdifReader reader = new LdifReader(getAddLdif());
+
+        for (LdifEntry entry : reader) {
+            connection.add(entry.getEntry());
+        }
+
+        Thread.sleep(2000);
+
+        failedACLs = map.getReadACLs(new ActiveMQQueue("FAILED"));
+        assertEquals("set size: " + failedACLs, 2, failedACLs.size());
+    }
+
+    @Test
+    public void testRemove() throws Exception {
+        map.query();
+
+        Set<?> failedACLs = map.getReadACLs(new ActiveMQQueue("TEST.FOO"));
+        assertEquals("set size: " + failedACLs, 2, failedACLs.size());
+
+        LdifReader reader = new LdifReader(getRemoveLdif());
+
+        for (LdifEntry entry : reader) {
+            connection.delete(entry.getDn());
+        }
+
+        Thread.sleep(2000);
+
+        failedACLs = map.getReadACLs(new ActiveMQQueue("TEST.FOO"));
+        assertEquals("set size: " + failedACLs, 0, failedACLs.size());
+        
+        assertTrue(map.getTempDestinationReadACLs() == null || map.getTempDestinationReadACLs().isEmpty());
+        assertTrue(map.getTempDestinationWriteACLs() == null || map.getTempDestinationWriteACLs().isEmpty());
+        assertTrue(map.getTempDestinationAdminACLs() == null || map.getTempDestinationAdminACLs().isEmpty());
+    }
+    
+    @Test
+    public void testRenameDestination() throws Exception {
+        map.query();
+
+        // Test for a destination rename
+        Set<?> failedACLs = map.getReadACLs(new ActiveMQQueue("TEST.FOO"));
+        assertEquals("set size: " + failedACLs, 2, failedACLs.size());
+
+        connection.rename(new Dn("cn=TEST.FOO," + getQueueBaseDn()),
+                new Rdn("cn=TEST.BAR"));
+
+        Thread.sleep(2000);
+
+        failedACLs = map.getReadACLs(new ActiveMQQueue("TEST.FOO"));
+        assertEquals("set size: " + failedACLs, 0, failedACLs.size());
+
+
+        failedACLs = map.getReadACLs(new ActiveMQQueue("TEST.BAR"));
+        assertEquals("set size: " + failedACLs, 2, failedACLs.size());
+    }
+    
+    @Test
+    public void testRenamePermission() throws Exception {
+        map.query();
+        
+        // Test for a permission rename
+        connection.delete(new Dn("cn=Read,cn=TEST.FOO," + getQueueBaseDn()));
+        
+        Thread.sleep(2000);
+        
+        Set<?> failedACLs = map.getReadACLs(new ActiveMQQueue("TEST.FOO"));
+        assertEquals("set size: " + failedACLs, 0, failedACLs.size());
+        
+        failedACLs = map.getWriteACLs(new ActiveMQQueue("TEST.FOO"));
+        assertEquals("set size: " + failedACLs, 2, failedACLs.size());
+        
+        connection.rename(new Dn("cn=Write,cn=TEST.FOO," + getQueueBaseDn()),
+                new Rdn("cn=Read"));
+        
+        Thread.sleep(2000);
+        
+        failedACLs = map.getReadACLs(new ActiveMQQueue("TEST.FOO"));
+        assertEquals("set size: " + failedACLs, 2, failedACLs.size());
+        
+        failedACLs = map.getWriteACLs(new ActiveMQQueue("TEST.FOO"));
+        assertEquals("set size: " + failedACLs, 0, failedACLs.size());
+    }
+
+    @Test
+    public void testChange() throws Exception {
+        map.query();
+
+        // Change permission entry
+        Set<?> failedACLs = map.getReadACLs(new ActiveMQQueue("TEST.FOO"));
+        assertEquals("set size: " + failedACLs, 2, failedACLs.size());
+
+        Dn dn = new Dn("cn=read,cn=TEST.FOO," + getQueueBaseDn());
+
+        ModifyRequest request = new ModifyRequestImpl();
+        request.setName(dn);
+        setupModifyRequest(request);
+
+        connection.modify(request);
+
+        Thread.sleep(2000);
+
+        failedACLs = map.getReadACLs(new ActiveMQQueue("TEST.FOO"));
+        assertEquals("set size: " + failedACLs, 1, failedACLs.size());
+        
+        // Change destination entry
+        request = new ModifyRequestImpl();
+        request.setName(new Dn("cn=TEST.FOO," + getQueueBaseDn()));
+        request.add("description", "This is a description!  In fact, it is a very good description.");
+        
+        connection.modify(request);
+
+        Thread.sleep(2000);
+        
+        failedACLs = map.getReadACLs(new ActiveMQQueue("TEST.FOO"));
+        assertEquals("set size: " + failedACLs, 1, failedACLs.size());
+    }
+    
+    protected CachedLDAPAuthorizationMap createMap() {
+        return new CachedLDAPAuthorizationMap();
+    }
+    
+    protected abstract InputStream getAddLdif();
+    
+    protected abstract InputStream getRemoveLdif();
+    
+    protected void setupModifyRequest(ModifyRequest request) {
+        request.remove("member", "cn=users");
+    }
+    
+    protected abstract String getQueueBaseDn();
+    
+    protected abstract LdapConnection getLdapConnection() throws Exception;
+    
+    public static void cleanAndLoad(String deleteFromDn, String ldifResourcePath,
+            String ldapHost, int ldapPort, String ldapUser, String ldapPass,
+            DirContext context) throws Exception {
+        // Cleanup everything used for testing.
+        List<String> dns = new LinkedList<String>();
+        dns.add(deleteFromDn);
+        
+        while (!dns.isEmpty()) {
+            String name = dns.get(dns.size() - 1);
+            Context currentContext = (Context) context.lookup(name);
+            NamingEnumeration<NameClassPair> namingEnum = currentContext.list("");
+            
+            if (namingEnum.hasMore()) {
+                while (namingEnum.hasMore()) {
+                    dns.add(namingEnum.next().getNameInNamespace());
+                }
+            } else {
+                context.unbind(name);
+                dns.remove(dns.size() - 1);
+            }
+        }
+        
+        // A bit of a hacked approach to loading an LDIF into OpenLDAP since there isn't an easy way to do it
+        // otherwise.  This approach invokes the command line tool programmatically but has
+        // to short-circuit the call to System.exit that the command line tool makes when it finishes.
+        // We are assuming that there isn't already a security manager in place.
+        final SecurityManager securityManager = new SecurityManager() {
+
+            public void checkPermission(java.security.Permission permission) {
+                if (permission.getName().contains("exitVM")) {
+                    throw new SecurityException("System.exit calls disabled for the moment.");
+                }
+            }
+        };
+
+        System.setSecurityManager(securityManager);
+
+        
+        File file = new File(AbstractCachedLDAPAuthorizationMapLegacyTest.class.getClassLoader().getResource(
+                ldifResourcePath).toURI());
+        
+        Class<?> clazz = Class.forName("LDAPModify");
+        Method mainMethod = clazz.getMethod("main", String[].class);
+        
+        try {
+            mainMethod.invoke(null, new Object[] {
+                    new String[] {
+                            "-v",
+                            "-h", ldapHost,
+                            "-p", String.valueOf(ldapPort),
+                            "-D", ldapUser,
+                            "-w", ldapPass,
+                            "-a",
+                            "-f", file.toString()}});
+        } catch (InvocationTargetException e) {
+            if (!(e.getTargetException() instanceof SecurityException)) {
+                throw e;
+            }
+        }
+
+        System.setSecurityManager(null);
+    }
+}
+
+

Added: activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/AbstractCachedLDAPAuthorizationModuleTest.java
URL: http://svn.apache.org/viewvc/activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/AbstractCachedLDAPAuthorizationModuleTest.java?rev=1347580&view=auto
==============================================================================
--- activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/AbstractCachedLDAPAuthorizationModuleTest.java (added)
+++ activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/AbstractCachedLDAPAuthorizationModuleTest.java Thu Jun  7 11:58:40 2012
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.security;
+
+import org.apache.activemq.command.ActiveMQQueue;
+import org.apache.activemq.jaas.UserPrincipal;
+import org.apache.directory.shared.ldap.model.message.ModifyRequest;
+import org.junit.Test;
+
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public abstract class AbstractCachedLDAPAuthorizationModuleTest 
+    extends AbstractCachedLDAPAuthorizationMapLegacyTest {
+
+    static final UserPrincipal JDOE = new UserPrincipal("jdoe");
+
+    @Test
+    public void testQuery() throws Exception {
+        map.query();
+        Set<?> readACLs = map.getReadACLs(new ActiveMQQueue("TEST.FOOBAR"));
+        assertEquals("set size: " + readACLs, 3, readACLs.size());
+        assertTrue("Contains admin group", readACLs.contains(ADMINS));
+        assertTrue("Contains users group", readACLs.contains(USERS));
+        assertTrue("Contains jdoe user", readACLs.contains(JDOE));
+
+        Set<?> failedACLs = map.getReadACLs(new ActiveMQQueue("FAILED"));
+        assertEquals("set size: " + failedACLs, 0, failedACLs.size());
+        
+        super.testQuery();
+    }
+
+    @Override
+    protected final void setupModifyRequest(ModifyRequest request) {
+        request.remove("member", getMemberAttributeValueForModifyRequest());
+    }
+    
+    protected abstract String getMemberAttributeValueForModifyRequest();
+
+    @Override
+    protected CachedLDAPAuthorizationMap createMap() {
+        CachedLDAPAuthorizationMap map = super.createMap();
+        map.setLegacyGroupMapping(false);
+        return map;
+    }
+}
+

Added: activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPAuthorizationModuleLegacyOpenLDAPTest.java
URL: http://svn.apache.org/viewvc/activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPAuthorizationModuleLegacyOpenLDAPTest.java?rev=1347580&view=auto
==============================================================================
--- activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPAuthorizationModuleLegacyOpenLDAPTest.java (added)
+++ activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPAuthorizationModuleLegacyOpenLDAPTest.java Thu Jun  7 11:58:40 2012
@@ -0,0 +1,97 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.security;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.directory.ldap.client.api.LdapConnection;
+import org.apache.directory.ldap.client.api.LdapNetworkConnection;
+import org.apache.directory.shared.ldap.model.exception.LdapException;
+import org.apache.directory.shared.ldap.model.name.Dn;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * Test of the {@link CachedLDAPAuthorizationMap} that tests against a basic OpenLDAP instance.
+ * Disabled by default because it requires external setup to provide the OpenLDAP instance.
+ * 
+ * To enable, you need an OpenLDAP with a minimum of the following in the slapd.conf file: 
+ * 
+ * suffix   "dc=apache,dc=org"
+ * rootdn   "cn=Manager,dc=apache,dc=org"
+ * rootpw   {SSHA}+Rx8kj98q3FlK5rUkT2hAtMP5v2ImQ82
+ * 
+ * If you wish to use different settings or don't use the default port, change the constants 
+ * below for your environment.
+ */
+@Ignore
+public class CachedLDAPAuthorizationModuleLegacyOpenLDAPTest extends
+        AbstractCachedLDAPAuthorizationMapLegacyTest {
+    
+    protected static final String LDAP_USER = "cn=Manager,dc=apache,dc=org";
+    protected static final String LDAP_PASS = "password";
+    protected static final String LDAP_HOST = "localhost";
+    protected static final int LDAP_PORT = 389;
+    
+    @Before
+    @Override
+    public void setup() throws Exception {
+        
+        super.setup();
+        
+        cleanAndLoad("dc=apache,dc=org", "org/apache/activemq/security/activemq-openldap-legacy.ldif",
+                LDAP_HOST, LDAP_PORT, LDAP_USER, LDAP_PASS, map.open());
+    }
+    
+    @Test
+    public void testRenameDestination() throws Exception {
+        // Subtree rename not implemented by OpenLDAP.
+    }
+    
+    protected CachedLDAPAuthorizationMap createMap() {
+        CachedLDAPAuthorizationMap newMap = super.createMap();
+        newMap.setConnectionURL("ldap://" + LDAP_HOST + ":" + String.valueOf(LDAP_PORT));
+        newMap.setConnectionUsername(LDAP_USER);
+        newMap.setConnectionPassword(LDAP_PASS);
+        // Persistent search is not supported in OpenLDAP
+        newMap.setRefreshInterval(10);
+        newMap.setQueueSearchBase("ou=Queue,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org");
+        newMap.setTopicSearchBase("ou=Topic,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org");
+        newMap.setTempSearchBase("ou=Temp,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org");
+        return newMap;
+    }
+    
+    protected InputStream getAddLdif() {
+        return getClass().getClassLoader().getResourceAsStream("org/apache/activemq/security/activemq-openldap-legacy-add.ldif");
+    }
+    
+    protected InputStream getRemoveLdif() {
+        return getClass().getClassLoader().getResourceAsStream("org/apache/activemq/security/activemq-openldap-legacy-delete.ldif");
+    }
+    
+    protected String getQueueBaseDn() {
+        return "ou=Queue,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org";
+    }
+    
+    protected LdapConnection getLdapConnection() throws LdapException, IOException {
+        LdapConnection connection = new LdapNetworkConnection(LDAP_HOST, LDAP_PORT);
+        connection.bind(new Dn(LDAP_USER), LDAP_PASS);
+        return connection;
+    }
+}

Added: activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPAuthorizationModuleLegacyTest.java
URL: http://svn.apache.org/viewvc/activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPAuthorizationModuleLegacyTest.java?rev=1347580&view=auto
==============================================================================
--- activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPAuthorizationModuleLegacyTest.java (added)
+++ activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPAuthorizationModuleLegacyTest.java Thu Jun  7 11:58:40 2012
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.security;
+
+import org.apache.directory.ldap.client.api.LdapConnection;
+import org.apache.directory.ldap.client.api.LdapNetworkConnection;
+import org.apache.directory.server.annotations.CreateLdapServer;
+import org.apache.directory.server.annotations.CreateTransport;
+import org.apache.directory.server.core.annotations.ApplyLdifFiles;
+import org.apache.directory.server.core.integ.FrameworkRunner;
+import org.apache.directory.shared.ldap.model.exception.LdapException;
+import org.apache.directory.shared.ldap.model.name.Dn;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+
+@RunWith( FrameworkRunner.class )
+@CreateLdapServer(transports = {@CreateTransport(protocol = "LDAP")})
+@ApplyLdifFiles(
+        "org/apache/activemq/security/activemq-apacheds-legacy.ldif"
+)
+public class CachedLDAPAuthorizationModuleLegacyTest extends AbstractCachedLDAPAuthorizationMapLegacyTest {
+
+    @Override
+    protected CachedLDAPAuthorizationMap createMap() {
+        CachedLDAPAuthorizationMap map = super.createMap();
+        map.setConnectionURL("ldap://localhost:" + getLdapServer().getPort());
+        return map;
+    }
+    
+    protected InputStream getAddLdif() {
+        return getClass().getClassLoader().getResourceAsStream("org/apache/activemq/security/activemq-apacheds-legacy-add.ldif");
+    }
+    
+    protected InputStream getRemoveLdif() {
+        return getClass().getClassLoader().getResourceAsStream("org/apache/activemq/security/activemq-apacheds-legacy-delete.ldif");
+    }
+    
+    protected String getQueueBaseDn() {
+        return "ou=Queue,ou=Destination,ou=ActiveMQ,ou=system";
+    }
+    
+    protected LdapConnection getLdapConnection() throws LdapException, IOException {
+        LdapConnection connection = new LdapNetworkConnection("localhost", getLdapServer().getPort());
+        connection.bind(new Dn("uid=admin,ou=system"), "secret");
+        return connection;
+    }
+}
+

Added: activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPAuthorizationModuleOpenLDAPTest.java
URL: http://svn.apache.org/viewvc/activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPAuthorizationModuleOpenLDAPTest.java?rev=1347580&view=auto
==============================================================================
--- activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPAuthorizationModuleOpenLDAPTest.java (added)
+++ activemq/trunk/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPAuthorizationModuleOpenLDAPTest.java Thu Jun  7 11:58:40 2012
@@ -0,0 +1,106 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.security;
+
+import org.apache.directory.ldap.client.api.LdapConnection;
+import org.apache.directory.ldap.client.api.LdapNetworkConnection;
+import org.apache.directory.shared.ldap.model.exception.LdapException;
+import org.apache.directory.shared.ldap.model.name.Dn;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Test of the {@link CachedLDAPAuthorizationMap} that tests against a basic OpenLDAP instance.
+ * Disabled by default because it requires external setup to provide the OpenLDAP instance.
+ * 
+ * To enable, you need an OpenLDAP with a minimum of the following in the slapd.conf file: 
+ * 
+ * suffix   "dc=apache,dc=org"
+ * rootdn   "cn=Manager,dc=apache,dc=org"
+ * rootpw   {SSHA}+Rx8kj98q3FlK5rUkT2hAtMP5v2ImQ82
+ * 
+ * If you wish to use different settings or don't use the default port, change the constants 
+ * below for your environment.
+ */
+@Ignore
+public class CachedLDAPAuthorizationModuleOpenLDAPTest extends AbstractCachedLDAPAuthorizationModuleTest {
+
+    protected static final String LDAP_USER = "cn=Manager,dc=apache,dc=org";
+    protected static final String LDAP_PASS = "password";
+    protected static final String LDAP_HOST = "localhost";
+    protected static final int LDAP_PORT = 389;
+    
+    @Before
+    @Override
+    public void setup() throws Exception {
+        
+        super.setup();
+        
+        cleanAndLoad("dc=apache,dc=org", "org/apache/activemq/security/activemq-openldap.ldif",
+                LDAP_HOST, LDAP_PORT, LDAP_USER, LDAP_PASS, map.open());
+    }
+    
+    @Test
+    public void testRenameDestination() throws Exception {
+        // Subtree rename not implemented by OpenLDAP.
+    }
+    
+    @Override
+    protected CachedLDAPAuthorizationMap createMap() {
+        CachedLDAPAuthorizationMap newMap = super.createMap();
+        newMap.setConnectionURL("ldap://" + LDAP_HOST + ":" + String.valueOf(LDAP_PORT));
+        newMap.setConnectionUsername(LDAP_USER);
+        newMap.setConnectionPassword(LDAP_PASS);
+        // Persistent search is not supported in OpenLDAP
+        newMap.setRefreshInterval(10);
+        newMap.setQueueSearchBase("ou=Queue,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org");
+        newMap.setTopicSearchBase("ou=Topic,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org");
+        newMap.setTempSearchBase("ou=Temp,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org");
+        return newMap;
+    }
+    
+    @Override
+    protected InputStream getAddLdif() {
+        return getClass().getClassLoader().getResourceAsStream("org/apache/activemq/security/activemq-openldap-add.ldif");
+    }
+    
+    @Override
+    protected InputStream getRemoveLdif() {
+        return getClass().getClassLoader().getResourceAsStream("org/apache/activemq/security/activemq-openldap-delete.ldif");
+    }
+    
+    @Override
+    protected String getMemberAttributeValueForModifyRequest() {
+        return "cn=users,ou=Group,ou=ActiveMQ,dc=activemq,dc=apache,dc=org";
+    }
+    
+    @Override
+    protected String getQueueBaseDn() {
+        return "ou=Queue,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org";
+    }
+    
+    @Override
+    protected LdapConnection getLdapConnection() throws LdapException, IOException {
+        LdapConnection connection = new LdapNetworkConnection(LDAP_HOST, LDAP_PORT);
+        connection.bind(new Dn(LDAP_USER), LDAP_PASS);
+        return connection;
+    }
+}



Mime
View raw message