You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by de...@apache.org on 2012/06/07 13:58:41 UTC
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/
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;
+ }
+}