From 56a700110c94f533b427ebdba427cc7d2190ef4f Mon Sep 17 00:00:00 2001 From: Bosanac Dejan Date: Fri, 15 Jul 2011 10:13:08 +0000 Subject: [PATCH] https://issues.apache.org/jira/browse/AMQ-3400 - cached ldap autorization module git-svn-id: https://svn.apache.org/repos/asf/activemq/trunk@1147073 13f79535-47bb-0310-9956-ffa450edef68 --- activemq-core/pom.xml | 2 + .../security/CachedLDAPAuthorizationMap.java | 366 ++++++++++++++++++ .../CachedLDAPAuthorizationModuleTest.java | 234 +++++++++++ .../security/CachedLDAPSecurityTest.java | 97 +++++ .../activemq/security/activemq-apacheds.ldif | 270 +++++++++++++ .../activemq/security/activemq-apacheds.xml | 57 +++ .../activemq/security/activemq-openldap.ldif | 270 +++++++++++++ .../activemq/security/activemq-openldap.xml | 62 +++ .../org/apache/activemq/security/add.ldif | 47 +++ .../org/apache/activemq/security/delete.ldif | 28 ++ 10 files changed, 1433 insertions(+) create mode 100644 activemq-core/src/main/java/org/apache/activemq/security/CachedLDAPAuthorizationMap.java create mode 100644 activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPAuthorizationModuleTest.java create mode 100644 activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPSecurityTest.java create mode 100644 activemq-core/src/test/resources/org/apache/activemq/security/activemq-apacheds.ldif create mode 100644 activemq-core/src/test/resources/org/apache/activemq/security/activemq-apacheds.xml create mode 100644 activemq-core/src/test/resources/org/apache/activemq/security/activemq-openldap.ldif create mode 100644 activemq-core/src/test/resources/org/apache/activemq/security/activemq-openldap.xml create mode 100644 activemq-core/src/test/resources/org/apache/activemq/security/add.ldif create mode 100644 activemq-core/src/test/resources/org/apache/activemq/security/delete.ldif diff --git a/activemq-core/pom.xml b/activemq-core/pom.xml index 78a310b772..5e7c8946b4 100755 --- a/activemq-core/pom.xml +++ b/activemq-core/pom.xml @@ -463,6 +463,8 @@ **/LDAPAuthorizationMapTest.* **/LDAPSecurityTest.* + **/CachedLDAPAuthorizationModuleTest.* + **/CachedLDAPSecurityTest.* **/FailoverConsumerTest.* diff --git a/activemq-core/src/main/java/org/apache/activemq/security/CachedLDAPAuthorizationMap.java b/activemq-core/src/main/java/org/apache/activemq/security/CachedLDAPAuthorizationMap.java new file mode 100644 index 0000000000..59e90b7588 --- /dev/null +++ b/activemq-core/src/main/java/org/apache/activemq/security/CachedLDAPAuthorizationMap.java @@ -0,0 +1,366 @@ +/** + * 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.ActiveMQDestination; +import org.apache.activemq.command.ActiveMQQueue; +import org.apache.activemq.command.ActiveMQTopic; +import org.apache.activemq.filter.DestinationMapNode; +import org.apache.activemq.filter.DestinationNode; +import org.apache.activemq.jaas.GroupPrincipal; +import org.apache.activemq.security.AuthorizationEntry; +import org.apache.activemq.security.DefaultAuthorizationMap; +import org.apache.activemq.security.TempDestinationAuthorizationEntry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; + +import javax.naming.Binding; +import javax.naming.Context; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.*; +import javax.naming.event.*; +import java.util.*; + +public class CachedLDAPAuthorizationMap extends DefaultAuthorizationMap implements NamespaceChangeListener, + ObjectChangeListener, InitializingBean { + + private static final Logger LOG = LoggerFactory.getLogger(CachedLDAPAuthorizationMap.class); + + + private String initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory"; + private String connectionURL = "ldap://localhost:1024"; + private String connectionUsername = "uid=admin,ou=system"; + private String connectionPassword = "secret"; + private String connectionProtocol = "s"; + private String authentication = "simple"; + + private String baseDn = "ou=system"; + private int cnsLength = 5; + + private int refreshInterval = -1; + private long lastUpdated; + + private static String ANY_DESCENDANT = "\\$"; + + private DirContext context; + private EventDirContext eventContext; + + protected DirContext open() throws NamingException { + if (context != null) { + return context; + } + + try { + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory); + if (connectionUsername != null || !"".equals(connectionUsername)) { + env.put(Context.SECURITY_PRINCIPAL, connectionUsername); + } + if (connectionPassword != null || !"".equals(connectionPassword)) { + env.put(Context.SECURITY_CREDENTIALS, connectionPassword); + } + env.put(Context.SECURITY_PROTOCOL, connectionProtocol); + env.put(Context.PROVIDER_URL, connectionURL); + env.put(Context.SECURITY_AUTHENTICATION, authentication); + context = new InitialDirContext(env); + + + if (refreshInterval == -1) { + 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); + } + } catch (NamingException e) { + LOG.error(e.toString()); + throw e; + } + return context; + } + + + HashMap entries = new HashMap(); + + + public void query() throws Exception { + try { + context = open(); + } catch (NamingException e) { + LOG.error(e.toString()); + } + + 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); + } + + setEntries(new ArrayList(entries.values())); + updated(); + } + + 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); + } + 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); + if (entry == null) { + entry = new AuthorizationEntry(); + entry.setDestination(dest); + entries.put(dest, entry); + } + return entry; + } else { + return null; + } + } + + 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)); + } else { + LOG.warn("Policy not applied! Unknown destination type " + destinationType); + } + return dest; + } + + 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 members = new HashSet(); + while (memberEnum.hasMoreElements()) { + String elem = (String) memberEnum.nextElement(); + members.add(new GroupPrincipal(elem.replaceAll("cn=", ""))); + } + + // 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()); + } + } + + protected String formatDestinationName(String cn) { + return cn.replaceFirst("cn=", "").replaceAll(ANY_DESCENDANT, ">"); + } + + protected boolean isPriviledge(Binding binding) { + String name = binding.getName(); + if (name.startsWith("cn=admin") || name.startsWith("cn=write") || name.startsWith("cn=read")) { + return true; + } else { + return false; + } + } + + @Override + protected Set getAllEntries(ActiveMQDestination destination) { + if (refreshInterval != -1 && System.currentTimeMillis() >= lastUpdated + refreshInterval) { + + reset(); + entries.clear(); + + LOG.debug("Updating authorization map!"); + try { + query(); + } catch (Exception e) { + LOG.error("Error updating authorization map", e); + } + } + + return super.getAllEntries(destination); + } + + @Override + public void objectAdded(NamingEvent namingEvent) { + LOG.debug("Adding object: " + namingEvent.getNewBinding()); + SearchResult result = (SearchResult)namingEvent.getNewBinding(); + String cn = null; + 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); + } + } + } + + @Override + public void objectRemoved(NamingEvent namingEvent) { + 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()); + } else if (cns[0].equalsIgnoreCase("cn=write")) { + entry.setWriteACLs(new HashSet()); + } else if (cns[0].equalsIgnoreCase("cn=read")) { + entry.setReadACLs(new HashSet()); + } else { + LOG.warn("Policy not removed! Unknown privilege " + result.getName()); + } + } + + @Override + public void objectRenamed(NamingEvent namingEvent) { + 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); + } else { + LOG.warn("No authorization entry for " + oldDest); + } + } + } + + @Override + public void objectChanged(NamingEvent namingEvent) { + LOG.debug("Changing object " + namingEvent.getOldBinding() + " to " + namingEvent.getNewBinding()); + objectRemoved(namingEvent); + objectAdded(namingEvent); + } + + @Override + public void namingExceptionThrown(NamingExceptionEvent namingExceptionEvent) { + LOG.error("Caught Unexpected Exception", namingExceptionEvent.getException()); + } + + // init + + @Override + public void afterPropertiesSet() throws Exception { + query(); + } + + // getters and setters + + public String getConnectionURL() { + return connectionURL; + } + + public void setConnectionURL(String connectionURL) { + this.connectionURL = connectionURL; + } + + public String getConnectionUsername() { + return connectionUsername; + } + + public void setConnectionUsername(String connectionUsername) { + this.connectionUsername = connectionUsername; + } + + public String getConnectionPassword() { + return connectionPassword; + } + + public void setConnectionPassword(String connectionPassword) { + this.connectionPassword = connectionPassword; + } + + public String getConnectionProtocol() { + return connectionProtocol; + } + + public void setConnectionProtocol(String connectionProtocol) { + this.connectionProtocol = connectionProtocol; + } + + public String getAuthentication() { + return authentication; + } + + public void setAuthentication(String authentication) { + this.authentication = authentication; + } + + public String getBaseDn() { + return baseDn; + } + + public void setBaseDn(String baseDn) { + this.baseDn = baseDn; + cnsLength = baseDn.split(",").length + 4; + } + + public int getRefreshInterval() { + return refreshInterval; + } + + public void setRefreshInterval(int refreshInterval) { + this.refreshInterval = refreshInterval; + } +} + diff --git a/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPAuthorizationModuleTest.java b/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPAuthorizationModuleTest.java new file mode 100644 index 0000000000..c85fe270c5 --- /dev/null +++ b/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPAuthorizationModuleTest.java @@ -0,0 +1,234 @@ +/** + * 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.ldap.client.api.message.BindResponse; +import org.apache.directory.ldap.client.api.message.ModifyDnResponse; +import org.apache.directory.ldap.client.api.message.ModifyRequest; +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.AbstractLdapTestUnit; +import org.apache.directory.server.core.integ.FrameworkRunner; +import org.apache.directory.shared.ldap.ldif.LdifEntry; +import org.apache.directory.shared.ldap.ldif.LdifReader; +import org.apache.directory.shared.ldap.message.ResultCodeEnum; +import org.apache.directory.shared.ldap.name.DN; +import org.apache.directory.shared.ldap.name.RDN; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNotNull; + + + + +@RunWith( FrameworkRunner.class ) +@CreateLdapServer(transports = {@CreateTransport(protocol = "LDAP")}) +@ApplyLdifFiles( + "org/apache/activemq/security/activemq-apacheds.ldif" +) +public class CachedLDAPAuthorizationModuleTest extends AbstractLdapTestUnit { + + static final GroupPrincipal GUESTS = new GroupPrincipal("guests"); + static final GroupPrincipal USERS = new GroupPrincipal("users"); + static final GroupPrincipal ADMINS = new GroupPrincipal("admins"); + + @Test + public void testQuery() throws Exception { + CachedLDAPAuthorizationMap map = new CachedLDAPAuthorizationMap(); + 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 testWildcards() throws Exception { + CachedLDAPAuthorizationMap map1 = new CachedLDAPAuthorizationMap(); + map1.query(); + Set fooACLs = map1.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)); + + CachedLDAPAuthorizationMap map2 = new CachedLDAPAuthorizationMap(); + map2.query(); + Set barACLs = map2.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 { + CachedLDAPAuthorizationMap map = new CachedLDAPAuthorizationMap(); + 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 { + CachedLDAPAuthorizationMap map = new CachedLDAPAuthorizationMap(); + 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 { + CachedLDAPAuthorizationMap map = new CachedLDAPAuthorizationMap(); + map.query(); + + Set failedACLs = map.getReadACLs(new ActiveMQQueue("FAILED")); + assertEquals("set size: " + failedACLs, 0, failedACLs.size()); + + LdapConnection connection = new LdapConnection( "localhost", 1024 ); + BindResponse bindResponse = connection.bind("uid=admin,ou=system", "secret"); + assertNotNull(bindResponse); + assertEquals(ResultCodeEnum.SUCCESS, bindResponse.getLdapResult().getResultCode()); + assertTrue(connection.isAuthenticated()); + + + LdifReader reader = new LdifReader(getClass().getClassLoader().getResourceAsStream("org/apache/activemq/security/add.ldif")); + + List entries = service.getTestEntries(); + for (LdifEntry entry : reader) { + connection.add(entry.getEntry()); + + } + + Thread.sleep(2000); + + failedACLs = map.getReadACLs(new ActiveMQQueue("FAILED")); + assertEquals("set size: " + failedACLs, 2, failedACLs.size()); + + connection.close(); + + + } + + @Test + public void testRemove() throws Exception { + CachedLDAPAuthorizationMap map = new CachedLDAPAuthorizationMap(); + map.query(); + + Set failedACLs = map.getReadACLs(new ActiveMQQueue("TEST.FOO")); + assertEquals("set size: " + failedACLs, 2, failedACLs.size()); + + LdapConnection connection = new LdapConnection( "localhost", 1024 ); + BindResponse bindResponse = connection.bind("uid=admin,ou=system", "secret"); + assertNotNull(bindResponse); + assertEquals(ResultCodeEnum.SUCCESS, bindResponse.getLdapResult().getResultCode()); + assertTrue(connection.isAuthenticated()); + + + LdifReader reader = new LdifReader(getClass().getClassLoader().getResourceAsStream("org/apache/activemq/security/delete.ldif")); + + List entries = service.getTestEntries(); + 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()); + + connection.close(); + } + + @Test + public void testRename() throws Exception { + CachedLDAPAuthorizationMap map = new CachedLDAPAuthorizationMap(); + map.query(); + + Set failedACLs = map.getReadACLs(new ActiveMQQueue("TEST.FOO")); + assertEquals("set size: " + failedACLs, 2, failedACLs.size()); + + LdapConnection connection = new LdapConnection( "localhost", 1024 ); + BindResponse bindResponse = connection.bind("uid=admin,ou=system", "secret"); + assertNotNull(bindResponse); + assertEquals(ResultCodeEnum.SUCCESS, bindResponse.getLdapResult().getResultCode()); + assertTrue(connection.isAuthenticated()); + + ModifyDnResponse resp = connection.rename(new DN("cn=TEST.FOO,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system"), + 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()); + + connection.close(); + } + + @Test + public void testChange() throws Exception { + CachedLDAPAuthorizationMap map = new CachedLDAPAuthorizationMap(); + map.query(); + + Set failedACLs = map.getReadACLs(new ActiveMQQueue("TEST.FOO")); + assertEquals("set size: " + failedACLs, 2, failedACLs.size()); + + LdapConnection connection = new LdapConnection( "localhost", 1024 ); + BindResponse bindResponse = connection.bind("uid=admin,ou=system", "secret"); + assertNotNull(bindResponse); + assertEquals(ResultCodeEnum.SUCCESS, bindResponse.getLdapResult().getResultCode()); + assertTrue(connection.isAuthenticated()); + + DN dn = new DN("cn=read,cn=TEST.FOO,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system"); + + ModifyRequest request = new ModifyRequest(dn); + request.remove("member", "cn=users"); + + connection.modify(request); + + Thread.sleep(2000); + + failedACLs = map.getReadACLs(new ActiveMQQueue("TEST.FOO")); + assertEquals("set size: " + failedACLs, 1, failedACLs.size()); + + connection.close(); + } + +} + diff --git a/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPSecurityTest.java b/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPSecurityTest.java new file mode 100644 index 0000000000..4f740aeb1a --- /dev/null +++ b/activemq-core/src/test/java/org/apache/activemq/security/CachedLDAPSecurityTest.java @@ -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 org.apache.activemq.ActiveMQConnectionFactory; +import org.apache.activemq.broker.BrokerFactory; +import org.apache.activemq.broker.BrokerService; +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.AbstractLdapTestUnit; +import org.apache.directory.server.core.integ.FrameworkRunner; +import org.apache.directory.server.ldap.LdapServer; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import javax.jms.*; + +import static org.junit.Assert.assertNotNull; + + +@RunWith( FrameworkRunner.class ) +@CreateLdapServer(transports = {@CreateTransport(protocol = "LDAP")}) +@ApplyLdifFiles( + "org/apache/activemq/security/activemq-apacheds.ldif" +) +public class CachedLDAPSecurityTest extends AbstractLdapTestUnit { + + public BrokerService broker; + + public static LdapServer ldapServer; + + @Before + public void setup() throws Exception { + broker = BrokerFactory.createBroker("xbean:org/apache/activemq/security/activemq-apacheds.xml"); + broker.start(); + broker.waitUntilStarted(); + //System.in.read(); + } + + @After + public void shutdown() throws Exception { + broker.stop(); + broker.waitUntilStopped(); + } + + @Test + public void testSendReceive() throws Exception { + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616"); + Connection conn = factory.createQueueConnection("jdoe", "sunflower"); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + conn.start(); + Queue queue = sess.createQueue("TEST.FOO"); + + MessageProducer producer = sess.createProducer(queue); + MessageConsumer consumer = sess.createConsumer(queue); + + producer.send(sess.createTextMessage("test")); + Message msg = consumer.receive(1000); + assertNotNull(msg); + } + + @Test + public void testTempDestinations() throws Exception { + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616"); + Connection conn = factory.createQueueConnection("jdoe", "sunflower"); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + conn.start(); + Queue queue = sess.createTemporaryQueue(); + + MessageProducer producer = sess.createProducer(queue); + MessageConsumer consumer = sess.createConsumer(queue); + + producer.send(sess.createTextMessage("test")); + Message msg = consumer.receive(1000); + assertNotNull(msg); + } + +} + + diff --git a/activemq-core/src/test/resources/org/apache/activemq/security/activemq-apacheds.ldif b/activemq-core/src/test/resources/org/apache/activemq/security/activemq-apacheds.ldif new file mode 100644 index 0000000000..1dcc1c1b10 --- /dev/null +++ b/activemq-core/src/test/resources/org/apache/activemq/security/activemq-apacheds.ldif @@ -0,0 +1,270 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + + +########################## +## Define basic objects ## +########################## + +# Uncomment if adding to open ldap +# dn: ou=system +# objectclass: organizationalUnit +# objectclass: top +# ou: system + +dn: ou=ActiveMQ,ou=system +objectClass: organizationalUnit +objectClass: top +ou: ActiveMQ + +dn: ou=Services,ou=system +ou: Services +objectClass: organizationalUnit +objectClass: top + +dn: cn=mqbroker,ou=Services,ou=system +cn: mqbroker +objectClass: organizationalRole +objectClass: top +objectClass: simpleSecurityObject +userPassword: {SSHA}YvMAkkd66cDecNoejo8jnw5uUUBziyl0 +description: Bind user for MQ broker + + +################### +## Define groups ## +################### + + +dn: ou=Group,ou=ActiveMQ,ou=system +objectClass: organizationalUnit +objectClass: top +ou: Group + +dn: cn=admins,ou=Group,ou=ActiveMQ,ou=system +cn: admins +member: uid=admin +objectClass: groupOfNames +objectClass: top + +dn: cn=users,ou=Group,ou=ActiveMQ,ou=system +cn: users +member: uid=jdoe +objectClass: groupOfNames +objectClass: top + + +################## +## Define users ## +################## + + +dn: ou=User,ou=ActiveMQ,ou=system +objectClass: organizationalUnit +objectClass: top +ou: User + +dn: uid=admin,ou=User,ou=ActiveMQ,ou=system +uid: admin +userPassword: {SSHA}YvMAkkd66cDecNoejo8jnw5uUUBziyl0 +objectClass: account +objectClass: simpleSecurityObject +objectClass: top + + +dn: uid=jdoe,ou=User,ou=ActiveMQ,ou=system +uid: jdoe +userPassword: {SSHA}YvMAkkd66cDecNoejo8jnw5uUUBziyl0 +objectclass: inetOrgPerson +objectclass: organizationalPerson +objectclass: person +objectclass: top +cn: Jane Doe +sn: Doe + + +######################### +## Define destinations ## +######################### + +dn: ou=Destination,ou=ActiveMQ,ou=system +objectClass: organizationalUnit +objectClass: top +ou: Destination + +dn: ou=Topic,ou=Destination,ou=ActiveMQ,ou=system +objectClass: organizationalUnit +objectClass: top +ou: Topic + +dn: ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +objectClass: organizationalUnit +objectClass: top +ou: Queue + +## TEST.FOO + +dn: cn=TEST.FOO,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +cn: TEST.FOO +description: A queue +objectClass: applicationProcess +objectClass: top + +dn: cn=admin,cn=TEST.FOO,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +cn: admin +description: Admin privilege group, members are roles +member: cn=admins +member: cn=users +objectClass: groupOfNames +objectClass: top + +dn: cn=read,cn=TEST.FOO,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +cn: read +member: cn=users +member: cn=admins +objectClass: groupOfNames +objectClass: top + +dn: cn=write,cn=TEST.FOO,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +cn: write +objectClass: groupOfNames +objectClass: top +member: cn=users +member: cn=admins + + +## FOO.> + +dn: cn=FOO.$,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +cn: FOO.$ +description: A queue +objectClass: applicationProcess +objectClass: top + +dn: cn=admin,cn=FOO.$,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +cn: admin +description: Admin privilege group, members are roles +member: cn=admins +member: cn=users +objectClass: groupOfNames +objectClass: top + +dn: cn=read,cn=FOO.$,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +cn: read +member: cn=users +member: cn=admins +objectClass: groupOfNames +objectClass: top + +dn: cn=write,cn=FOO.$,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +cn: write +objectClass: groupOfNames +objectClass: top +member: cn=users +member: cn=admins + + +## BAR.* + +dn: cn=BAR.*,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +cn: BAR.* +description: A queue +objectClass: applicationProcess +objectClass: top + +dn: cn=admin,cn=BAR.*,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +cn: admin +description: Admin privilege group, members are roles +member: cn=admins +member: cn=users +objectClass: groupOfNames +objectClass: top + +dn: cn=read,cn=BAR.*,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +cn: read +member: cn=users +member: cn=admins +objectClass: groupOfNames +objectClass: top + +dn: cn=write,cn=BAR.*,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +cn: write +objectClass: groupOfNames +objectClass: top +member: cn=users +member: cn=admins + +####################### +## Define advisories ## +####################### + +dn: cn=ActiveMQ.Advisory.$,ou=Topic,ou=Destination,ou=ActiveMQ,ou=system +cn: ActiveMQ.Advisory.$ +objectClass: applicationProcess +objectClass: top +description: Advisory topics + +dn: cn=read,cn=ActiveMQ.Advisory.$,ou=Topic,ou=Destination,ou=ActiveMQ,ou=system +cn: read +member: cn=admins +member: cn=users +objectClass: groupOfNames +objectClass: top + +dn: cn=write,cn=ActiveMQ.Advisory.$,ou=Topic,ou=Destination,ou=ActiveMQ,ou=system +cn: write +member: cn=admins +member: cn=users +objectClass: groupOfNames +objectClass: top + +dn: cn=admin,cn=ActiveMQ.Advisory.$,ou=Topic,ou=Destination,ou=ActiveMQ,ou=system +cn: admin +member: cn=admins +member: cn=users +objectClass: groupOfNames +objectClass: top + +###################### +## Define temporary ## +###################### + +dn: ou=Temp,ou=Destination,ou=ActiveMQ,ou=system +objectClass: organizationalUnit +objectClass: top +ou: Temp + +dn: cn=read,ou=Temp,ou=Destination,ou=ActiveMQ,ou=system +cn: read +member: cn=admins +member: cn=users +objectClass: groupOfNames +objectClass: top + +dn: cn=write,ou=Temp,ou=Destination,ou=ActiveMQ,ou=system +cn: write +member: cn=admins +member: cn=users +objectClass: groupOfNames +objectClass: top + +dn: cn=admin,ou=Temp,ou=Destination,ou=ActiveMQ,ou=system +cn: admin +member: cn=admins +member: cn=users +objectClass: groupOfNames +objectClass: top \ No newline at end of file diff --git a/activemq-core/src/test/resources/org/apache/activemq/security/activemq-apacheds.xml b/activemq-core/src/test/resources/org/apache/activemq/security/activemq-apacheds.xml new file mode 100644 index 0000000000..65548367e4 --- /dev/null +++ b/activemq-core/src/test/resources/org/apache/activemq/security/activemq-apacheds.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-core/src/test/resources/org/apache/activemq/security/activemq-openldap.ldif b/activemq-core/src/test/resources/org/apache/activemq/security/activemq-openldap.ldif new file mode 100644 index 0000000000..c8c4a09483 --- /dev/null +++ b/activemq-core/src/test/resources/org/apache/activemq/security/activemq-openldap.ldif @@ -0,0 +1,270 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + + +########################## +## Define basic objects ## +########################## + +# Uncomment if adding to open ldap +dn: dc=activemq,dc=apache,dc=org +dc: activemq +objectClass: domain +objectClass: top + +dn: ou=ActiveMQ,dc=activemq,dc=apache,dc=org +objectClass: organizationalUnit +objectClass: top +ou: ActiveMQ + +dn: ou=Services,dc=activemq,dc=apache,dc=org +ou: Services +objectClass: organizationalUnit +objectClass: top + +dn: cn=mqbroker,ou=Services,dc=activemq,dc=apache,dc=org +cn: mqbroker +objectClass: organizationalRole +objectClass: top +objectClass: simpleSecurityObject +userPassword: {SSHA}YvMAkkd66cDecNoejo8jnw5uUUBziyl0 +description: Bind user for MQ broker + + +################### +## Define groups ## +################### + + +dn: ou=Group,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +objectClass: organizationalUnit +objectClass: top +ou: Group + +dn: cn=admins,ou=Group,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: admins +member: uid=admin +objectClass: groupOfNames +objectClass: top + +dn: cn=users,ou=Group,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: users +member: uid=jdoe +objectClass: groupOfNames +objectClass: top + + +################## +## Define users ## +################## + + +dn: ou=User,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +objectClass: organizationalUnit +objectClass: top +ou: User + +dn: uid=admin,ou=User,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +uid: admin +userPassword: {SSHA}YvMAkkd66cDecNoejo8jnw5uUUBziyl0 +objectClass: account +objectClass: simpleSecurityObject +objectClass: top + + +dn: uid=jdoe,ou=User,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +uid: jdoe +userPassword: {SSHA}YvMAkkd66cDecNoejo8jnw5uUUBziyl0 +objectclass: inetOrgPerson +objectclass: organizationalPerson +objectclass: person +objectclass: top +cn: Jane Doe +sn: Doe + + +######################### +## Define destinations ## +######################### + +dn: ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +objectClass: organizationalUnit +objectClass: top +ou: Destination + +dn: ou=Topic,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +objectClass: organizationalUnit +objectClass: top +ou: Topic + +dn: ou=Queue,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +objectClass: organizationalUnit +objectClass: top +ou: Queue + +## TEST.FOO + +dn: cn=TEST.FOO,ou=Queue,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: TEST.FOO +description: A queue +objectClass: applicationProcess +objectClass: top + +dn: cn=admin,cn=TEST.FOO,ou=Queue,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: admin +description: Admin privilege group, members are roles +member: cn=admins +member: cn=users +objectClass: groupOfNames +objectClass: top + +dn: cn=read,cn=TEST.FOO,ou=Queue,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: read +member: cn=users +member: cn=admins +objectClass: groupOfNames +objectClass: top + +dn: cn=write,cn=TEST.FOO,ou=Queue,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: write +objectClass: groupOfNames +objectClass: top +member: cn=users +member: cn=admins + + +## FOO.> + +dn: cn=FOO.$,ou=Queue,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: FOO.$ +description: A queue +objectClass: applicationProcess +objectClass: top + +dn: cn=admin,cn=FOO.$,ou=Queue,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: admin +description: Admin privilege group, members are roles +member: cn=admins +member: cn=users +objectClass: groupOfNames +objectClass: top + +dn: cn=read,cn=FOO.$,ou=Queue,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: read +member: cn=users +member: cn=admins +objectClass: groupOfNames +objectClass: top + +dn: cn=write,cn=FOO.$,ou=Queue,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: write +objectClass: groupOfNames +objectClass: top +member: cn=users +member: cn=admins + + +## BAR.* + +dn: cn=BAR.*,ou=Queue,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: BAR.* +description: A queue +objectClass: applicationProcess +objectClass: top + +dn: cn=admin,cn=BAR.*,ou=Queue,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: admin +description: Admin privilege group, members are roles +member: cn=admins +member: cn=users +objectClass: groupOfNames +objectClass: top + +dn: cn=read,cn=BAR.*,ou=Queue,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: read +member: cn=users +member: cn=admins +objectClass: groupOfNames +objectClass: top + +dn: cn=write,cn=BAR.*,ou=Queue,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: write +objectClass: groupOfNames +objectClass: top +member: cn=users +member: cn=admins + +####################### +## Define advisories ## +####################### + +dn: cn=ActiveMQ.Advisory.$,ou=Topic,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: ActiveMQ.Advisory.$ +objectClass: applicationProcess +objectClass: top +description: Advisory topics + +dn: cn=read,cn=ActiveMQ.Advisory.$,ou=Topic,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: read +member: cn=admins +member: cn=users +objectClass: groupOfNames +objectClass: top + +dn: cn=write,cn=ActiveMQ.Advisory.$,ou=Topic,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: write +member: cn=admins +member: cn=users +objectClass: groupOfNames +objectClass: top + +dn: cn=admin,cn=ActiveMQ.Advisory.$,ou=Topic,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: admin +member: cn=admins +member: cn=users +objectClass: groupOfNames +objectClass: top + +###################### +## Define temporary ## +###################### + +dn: ou=Temp,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +objectClass: organizationalUnit +objectClass: top +ou: Temp + +dn: cn=read,ou=Temp,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: read +member: cn=admins +member: cn=users +objectClass: groupOfNames +objectClass: top + +dn: cn=write,ou=Temp,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: write +member: cn=admins +member: cn=users +objectClass: groupOfNames +objectClass: top + +dn: cn=admin,ou=Temp,ou=Destination,ou=ActiveMQ,dc=activemq,dc=apache,dc=org +cn: admin +member: cn=admins +member: cn=users +objectClass: groupOfNames +objectClass: top \ No newline at end of file diff --git a/activemq-core/src/test/resources/org/apache/activemq/security/activemq-openldap.xml b/activemq-core/src/test/resources/org/apache/activemq/security/activemq-openldap.xml new file mode 100644 index 0000000000..9833cbd307 --- /dev/null +++ b/activemq-core/src/test/resources/org/apache/activemq/security/activemq-openldap.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-core/src/test/resources/org/apache/activemq/security/add.ldif b/activemq-core/src/test/resources/org/apache/activemq/security/add.ldif new file mode 100644 index 0000000000..f48030ceff --- /dev/null +++ b/activemq-core/src/test/resources/org/apache/activemq/security/add.ldif @@ -0,0 +1,47 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + + +## FAILED + +dn: cn=FAILED,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +cn: FAILED +description: New queue +objectClass: applicationProcess +objectClass: top + +dn: cn=admin,cn=FAILED,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +cn: admin +description: Admin privilege group, members are roles +member: cn=admins +member: cn=users +objectClass: groupOfNames +objectClass: top + +dn: cn=read,cn=FAILED,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +cn: read +member: cn=users +member: cn=admins +objectClass: groupOfNames +objectClass: top + +dn: cn=write,cn=FAILED,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +cn: write +objectClass: groupOfNames +objectClass: top +member: cn=users +member: cn=admins \ No newline at end of file diff --git a/activemq-core/src/test/resources/org/apache/activemq/security/delete.ldif b/activemq-core/src/test/resources/org/apache/activemq/security/delete.ldif new file mode 100644 index 0000000000..1a9ec0bf80 --- /dev/null +++ b/activemq-core/src/test/resources/org/apache/activemq/security/delete.ldif @@ -0,0 +1,28 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +dn: cn=TEST.FOO,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +changetype: delete + +dn: cn=admin,cn=TEST.FOO,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +changetype: delete + +dn: cn=read,cn=TEST.FOO,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +changetype: delete + +dn: cn=write,cn=TEST.FOO,ou=Queue,ou=Destination,ou=ActiveMQ,ou=system +changetype: delete \ No newline at end of file