You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by ce...@apache.org on 2014/02/24 16:30:48 UTC

[1/4] Fix for https://issues.apache.org/jira/browse/AMQ-3621 - Integrate Apache Shiro with ActiveMQ as "security solution" - many thanks to Les Hazlewood (lhazlewood) for the patch!

Repository: activemq
Updated Branches:
  refs/heads/trunk e7e317dc7 -> f9451e56e


http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/resources/org/apache/activemq/shiro/no-ini-config.xml
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/resources/org/apache/activemq/shiro/no-ini-config.xml b/activemq-shiro/src/test/resources/org/apache/activemq/shiro/no-ini-config.xml
new file mode 100644
index 0000000..f8e04a8
--- /dev/null
+++ b/activemq-shiro/src/test/resources/org/apache/activemq/shiro/no-ini-config.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+
+<!-- this file can only be parsed using the xbean-spring library -->
+<!-- START SNIPPET: example -->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:amq="http://activemq.apache.org/schema/core"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
+       http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">
+
+    <broker xmlns="http://activemq.apache.org/schema/core"
+            useJmx="false" persistent="false" populateJMSXUserID="true">
+
+        <destinations>
+            <queue physicalName="TEST.Q"/>
+        </destinations>
+
+        <transportConnectors>
+            <transportConnector name="default" uri="tcp://0.0.0.0:61616"/>
+        </transportConnectors>
+
+        <plugins>
+
+            <bean id="shiroPlugin" class="org.apache.activemq.shiro.ShiroPlugin"
+                  xmlns="http://www.springframework.org/schema/beans">
+                <!-- This configuration doesn't rely on an INI file for config, and expects that Shiro is configured
+                     entirely within Spring.  See the Shiro securityManager bean below. -->
+                <property name="securityManager" ref="securityManager"/>
+            </bean>
+
+        </plugins>
+    </broker>
+
+    <bean id="securityManager" class="org.apache.shiro.mgt.DefaultSecurityManager">
+        <!-- Do not use this cache manager in a clustered broker setup.  If your broker is clustered, you should
+            configure a cluster-aware CacheManager, e.g. Hazelcast, Terracotta+Ehcache, etc. -->
+        <property name="cacheManager">
+            <bean class="org.apache.shiro.cache.MemoryConstrainedCacheManager"/>
+        </property>
+        <property name="realms">
+            <list>
+                <!-- Replace this with the real realm you're going to use that accesses your security data store: -->
+                <bean id="myRealm" class="org.apache.shiro.realm.text.TextConfigurationRealm">
+                    <property name="userDefinitions">
+                        <value>
+                            system = manager,system
+                        </value>
+                    </property>
+                    <property name="roleDefinitions">
+                        <value>
+                            system = *
+                        </value>
+                    </property>
+                </bean>
+                <!-- Add other realms if necessary -->
+            </list>
+        </property>
+    </bean>
+    <bean class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/resources/shiro.ini
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/resources/shiro.ini b/activemq-shiro/src/test/resources/shiro.ini
new file mode 100644
index 0000000..5231365
--- /dev/null
+++ b/activemq-shiro/src/test/resources/shiro.ini
@@ -0,0 +1,64 @@
+#
+# 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.
+#
+
+[main]
+
+# Shiro object graph configuration here if desired/necessary
+
+[users]
+
+# users section format:
+#
+# username = password [, assignedRole1, assignedRole2, ..., assignedRoleN]
+#
+# for example:
+#
+# scott = tiger, users, administrators, advisory
+#
+# Roles and permissions assigned to roles are defined in the [roles] section
+# below. By transitive association, any user assigned a role is granted the
+# role's permissions.
+
+# ActiveMQ System User
+# needed for in-VM/local connections when authentication is enabled:
+system = manager, system
+
+[roles]
+
+# roles section format:
+#
+# roleName = wildcardPermission1, wildcardPermission2, ..., wildcardPermissionN
+
+# The 'system' role is assigned all permissions (*).  Be careful when assigning
+# this to actual users other than then system user:
+system = *
+
+# Full access rights should generally be given to the ActiveMQ.Advisory.*
+# destinations because by default an ActiveMQConnection uses advisory topics to
+# get early knowledge of temp destination creation and deletion. For more info:
+#
+#   http://activemq.apache.org/security.html
+#
+# So we create an 'advisory' role here with a wildcard/catch-all permissions
+# for all advisory topics.  To make your life easy, ensure you assign this to
+# any/all users, e.g.
+#
+#   jsmith = password, advisory, ...
+
+advisory = topic:ActiveMQ.Advisory*
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/assembly/pom.xml
----------------------------------------------------------------------
diff --git a/assembly/pom.xml b/assembly/pom.xml
index a25c085..4c29cf8 100755
--- a/assembly/pom.xml
+++ b/assembly/pom.xml
@@ -177,6 +177,10 @@
     </dependency>
     <dependency>
       <groupId>${project.groupId}</groupId>
+      <artifactId>activemq-shiro</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
       <artifactId>activemq-spring</artifactId>
     </dependency>
     <dependency>
@@ -286,6 +290,14 @@
       <artifactId>geronimo-jta_1.0.1B_spec</artifactId>
     </dependency>
     <dependency>
+      <groupId>org.apache.shiro</groupId>
+      <artifactId>shiro-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.shiro</groupId>
+      <artifactId>shiro-spring</artifactId>
+    </dependency>
+    <dependency>
        <groupId>org.apache.xbean</groupId>
        <artifactId>xbean-spring</artifactId>
        <optional>true</optional>

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/assembly/src/main/descriptors/common-bin.xml
----------------------------------------------------------------------
diff --git a/assembly/src/main/descriptors/common-bin.xml b/assembly/src/main/descriptors/common-bin.xml
index e08078e..67c293a 100644
--- a/assembly/src/main/descriptors/common-bin.xml
+++ b/assembly/src/main/descriptors/common-bin.xml
@@ -172,6 +172,7 @@
         <include>${pom.groupId}:activemq-jms-pool</include>
         <include>${pom.groupId}:activemq-pool</include>
         <include>${pom.groupId}:activemq-partition</include>
+        <include>${pom.groupId}:activemq-shiro</include>
         <include>${pom.groupId}:activeio-core</include>
         <include>commons-beanutils:commons-beanutils</include>
         <include>commons-collections:commons-collections</include>
@@ -223,7 +224,11 @@
         <include>org.linkedin:org.linkedin.zookeeper-impl</include>
         <include>org.linkedin:org.linkedin.util-core</include>
         <include>org.apache.zookeeper:zookeeper</include>
-        
+
+        <!-- Apache Shiro dependencies: -->
+        <include>org.apache.shiro:shiro-core</include>
+        <include>org.apache.shiro:shiro-spring</include>
+
       </includes>
     </dependencySet>
     <dependencySet>

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 35f7da2..54b1d64 100755
--- a/pom.xml
+++ b/pom.xml
@@ -107,7 +107,9 @@
     <scala-plugin-version>3.1.0</scala-plugin-version>
     <scala-version>2.9.1</scala-version>
     <scala-bundle-version>2.9.1_3</scala-bundle-version>
+    <shiro-version>1.2.2</shiro-version>
     <scalatest-version>1.8</scalatest-version>
+    <shiro-version>1.2.2</shiro-version>
     <slf4j-version>1.7.5</slf4j-version>
     <snappy-version>1.1.0.1</snappy-version>
     <spring-version>3.2.5.RELEASE</spring-version>
@@ -244,6 +246,7 @@
     <module>activemq-ra</module>
     <module>activemq-rar</module>
     <module>activemq-run</module>
+    <module>activemq-shiro</module>
     <module>activemq-spring</module>
     <module>activemq-runtime-config</module>
     <module>activemq-tooling</module>
@@ -386,6 +389,11 @@
       </dependency>
       <dependency>
         <groupId>org.apache.activemq</groupId>
+        <artifactId>activemq-shiro</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.activemq</groupId>
         <artifactId>activemq-spring</artifactId>
         <version>${project.version}</version>
       </dependency>
@@ -667,6 +675,34 @@
         <artifactId>openjpa-persistence-jdbc</artifactId>
         <version>${openjpa-version}</version>
       </dependency>
+      
+      <!-- Optional Shiro Support -->
+      <dependency>
+        <groupId>org.apache.shiro</groupId>
+        <artifactId>shiro-core</artifactId>
+        <version>${shiro-version}</version>
+        <optional>true</optional>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.shiro</groupId>
+        <artifactId>shiro-spring</artifactId>
+        <version>${shiro-version}</version>
+        <optional>true</optional>
+      </dependency>
+
+      <!-- Optional Shiro Support -->
+      <dependency>
+        <groupId>org.apache.shiro</groupId>
+        <artifactId>shiro-core</artifactId>
+        <version>${shiro-version}</version>
+        <optional>true</optional>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.shiro</groupId>
+        <artifactId>shiro-spring</artifactId>
+        <version>${shiro-version}</version>
+        <optional>true</optional>
+      </dependency>
 
       <!-- Optional Spring Support -->
       <dependency>


[3/4] Fix for https://issues.apache.org/jira/browse/AMQ-3621 - Integrate Apache Shiro with ActiveMQ as "security solution" - many thanks to Les Hazlewood (lhazlewood) for the patch!

Posted by ce...@apache.org.
http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/AuthorizationFilter.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/AuthorizationFilter.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/AuthorizationFilter.java
new file mode 100644
index 0000000..0c147d9
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/AuthorizationFilter.java
@@ -0,0 +1,226 @@
+/**
+ * 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.shiro.authz;
+
+import org.apache.activemq.broker.ConnectionContext;
+import org.apache.activemq.broker.ProducerBrokerExchange;
+import org.apache.activemq.broker.region.Destination;
+import org.apache.activemq.broker.region.Subscription;
+import org.apache.activemq.command.ActiveMQDestination;
+import org.apache.activemq.command.ConsumerInfo;
+import org.apache.activemq.command.DestinationInfo;
+import org.apache.activemq.command.Message;
+import org.apache.activemq.command.ProducerInfo;
+import org.apache.activemq.security.SecurityContext;
+import org.apache.activemq.shiro.env.EnvironmentFilter;
+import org.apache.activemq.shiro.subject.ConnectionSubjectResolver;
+import org.apache.shiro.authz.Permission;
+import org.apache.shiro.authz.UnauthorizedException;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.apache.shiro.subject.Subject;
+
+import java.util.Collection;
+
+/**
+ * The {@code AuthorizationFilter} asserts that actions are allowed to execute first before they are actually
+ * executed.  Such actions include creating, removing, reading from and writing to destinations.
+ * <p/>
+ * This implementation is strictly permission-based, allowing for the finest-grained security policies possible.
+ * Whenever a {@link Subject} associated with a connection attempts to perform an {@link org.apache.activemq.shiro.authz.Action} (such as creating a
+ * destination, or reading from a queue, etc), one or more {@link Permission}s representing that {@code action} are
+ * checked.
+ * <p/>
+ * If the {@code Subject}{@link Subject#isPermitted(org.apache.shiro.authz.Permission) isPermitted} to perform the
+ * {@code action}, the action is allowed to execute and the broker filter chain executes uninterrupted.
+ * <p/>
+ * However, if the {@code Subject} is not permitted to perform the action, an {@link UnauthorizedException} will be
+ * thrown, preventing the filter chain from executing that action.
+ * <h2>ActionPermissionResolver</h2>
+ * The attempted {@code Action} is guarded by one or more {@link Permission}s as indicated by a configurable
+ * {@link #setActionPermissionResolver(org.apache.activemq.shiro.authz.ActionPermissionResolver) actionPermissionResolver}.  The
+ * {@code actionPermissionResolver} indicates which permissions must be granted to the connection {@code Subject} in
+ * order for the action to execute.
+ * <p/>
+ * The default {@code actionPermissionResolver} instance is a
+ * {@link org.apache.activemq.shiro.authz.DestinationActionPermissionResolver DestinationActionPermissionResolver}, which indicates which permissions
+ * are required to perform any action on a particular destination.  Those familiar with Shiro's
+ * {@link org.apache.shiro.authz.permission.WildcardPermission WildcardPermission} syntax will find the
+ * {@code DestinationActionPermissionResolver}'s
+ * {@link org.apache.activemq.shiro.authz.DestinationActionPermissionResolver#createPermissionString createPermissionString} method
+ * documentation valuable for understanding how destination actions are represented as permissions.
+ *
+ * @see org.apache.activemq.shiro.authz.ActionPermissionResolver
+ * @see org.apache.activemq.shiro.authz.DestinationActionPermissionResolver
+ * @since 5.10.0
+ */
+public class AuthorizationFilter extends EnvironmentFilter {
+
+    private ActionPermissionResolver actionPermissionResolver;
+
+    public AuthorizationFilter() {
+        this.actionPermissionResolver = new DestinationActionPermissionResolver();
+    }
+
+    /**
+     * Returns the {@code ActionPermissionResolver} used to indicate which permissions are required to be granted to
+     * a {@link Subject} to perform a particular destination {@link org.apache.activemq.shiro.authz.Action}, (such as creating a
+     * destination, or reading from a queue, etc).  The default instance is a
+     * {@link DestinationActionPermissionResolver}.
+     *
+     * @return the {@code ActionPermissionResolver} used to indicate which permissions are required to be granted to
+     *         a {@link Subject} to perform a particular destination {@link org.apache.activemq.shiro.authz.Action}, (such as creating a
+     *         destination, or reading from a queue, etc).
+     */
+    public ActionPermissionResolver getActionPermissionResolver() {
+        return actionPermissionResolver;
+    }
+
+    /**
+     * Sets the {@code ActionPermissionResolver} used to indicate which permissions are required to be granted to
+     * a {@link Subject} to perform a particular destination {@link org.apache.activemq.shiro.authz.Action}, (such as creating a
+     * destination, or reading from a queue, etc).  Unless overridden by this method, the default instance is a
+     * {@link DestinationActionPermissionResolver}.
+     *
+     * @param actionPermissionResolver the {@code ActionPermissionResolver} used to indicate which permissions are
+     *                                 required to be granted to a {@link Subject} to perform a particular destination
+     *                                 {@link org.apache.activemq.shiro.authz.Action}, (such as creating a destination, or reading from a queue, etc).
+     */
+    public void setActionPermissionResolver(ActionPermissionResolver actionPermissionResolver) {
+        this.actionPermissionResolver = actionPermissionResolver;
+    }
+
+    /**
+     * Returns the {@code Subject} associated with the specified connection using a
+     * {@link org.apache.activemq.shiro.subject.ConnectionSubjectResolver}.
+     *
+     * @param ctx the connection context
+     * @return the {@code Subject} associated with the specified connection.
+     */
+    protected Subject getSubject(ConnectionContext ctx) {
+        return new ConnectionSubjectResolver(ctx).getSubject();
+    }
+
+    protected String toString(Subject subject) {
+        PrincipalCollection pc = subject.getPrincipals();
+        if (pc != null && !pc.isEmpty()) {
+            return "[" + pc.toString() + "] ";
+        }
+        return "";
+    }
+
+    protected void assertAuthorized(DestinationAction action) {
+        assertAuthorized(action, action.getVerb());
+    }
+
+    //ActiveMQ internals will create a ConnectionContext with a SecurityContext that is not
+    //Shiro specific.  We need to allow actions for internal system operations:
+    protected boolean isSystemBroker(DestinationAction action) {
+        ConnectionContext context = action.getConnectionContext();
+        SecurityContext securityContext = context.getSecurityContext();
+        return securityContext != null && securityContext.isBrokerContext();
+    }
+
+    protected void assertAuthorized(DestinationAction action, String verbText) {
+        if (!isEnabled() || isSystemBroker(action)) {
+            return;
+        }
+
+        final Subject subject = getSubject(action.getConnectionContext());
+
+        Collection<Permission> perms = this.actionPermissionResolver.getPermissions(action);
+
+        if (!subject.isPermittedAll(perms)) {
+            String msg = createUnauthorizedMessage(subject, action, verbText);
+            throw new UnauthorizedException(msg);
+        }
+    }
+
+    protected String createUnauthorizedMessage(Subject subject, DestinationAction action, String verbDisplayText) {
+        return "Subject " + toString(subject) + "is not authorized to " + verbDisplayText + " destination: " + action.getDestination();
+    }
+
+    @Override
+    public void addDestinationInfo(ConnectionContext context, DestinationInfo info) throws Exception {
+
+        DestinationAction action = new DestinationAction(context, info.getDestination(), "create");
+        assertAuthorized(action);
+
+        super.addDestinationInfo(context, info);
+    }
+
+    @Override
+    public Destination addDestination(ConnectionContext context, ActiveMQDestination destination, boolean create) throws Exception {
+
+        DestinationAction action = new DestinationAction(context, destination, "create");
+        assertAuthorized(action);
+
+        return super.addDestination(context, destination, create);
+    }
+
+    @Override
+    public void removeDestination(ConnectionContext context, ActiveMQDestination destination, long timeout) throws Exception {
+
+        DestinationAction action = new DestinationAction(context, destination, "remove");
+        assertAuthorized(action);
+
+        super.removeDestination(context, destination, timeout);
+    }
+
+    @Override
+    public void removeDestinationInfo(ConnectionContext context, DestinationInfo info) throws Exception {
+
+        DestinationAction action = new DestinationAction(context, info.getDestination(), "remove");
+        assertAuthorized(action);
+
+        super.removeDestinationInfo(context, info);
+    }
+
+    @Override
+    public Subscription addConsumer(ConnectionContext context, ConsumerInfo info) throws Exception {
+
+        //Unlike when adding a producer, consumers must specify the destination at creation time, so we can rely on
+        //a destination being available to perform the authz check:
+        DestinationAction action = new DestinationAction(context, info.getDestination(), "read");
+        assertAuthorized(action, "read from");
+
+        return super.addConsumer(context, info);
+    }
+
+    @Override
+    public void addProducer(ConnectionContext context, ProducerInfo info) throws Exception {
+
+        // JMS allows producers to be created without first specifying a destination.  In these cases, every send
+        // operation must specify a destination.  Because of this, we only authorize 'addProducer' if a destination is
+        // specified. If not specified, the authz check in the 'send' method below will ensure authorization.
+        if (info.getDestination() != null) {
+            DestinationAction action = new DestinationAction(context, info.getDestination(), "write");
+            assertAuthorized(action, "write to");
+        }
+
+        super.addProducer(context, info);
+    }
+
+    @Override
+    public void send(ProducerBrokerExchange exchange, Message message) throws Exception {
+
+        DestinationAction action = new DestinationAction(exchange.getConnectionContext(), message.getDestination(), "write");
+        assertAuthorized(action, "write to");
+
+        super.send(exchange, message);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/DestinationAction.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/DestinationAction.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/DestinationAction.java
new file mode 100644
index 0000000..07b6b48
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/DestinationAction.java
@@ -0,0 +1,90 @@
+/**
+ * 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.shiro.authz;
+
+import org.apache.activemq.broker.ConnectionContext;
+import org.apache.activemq.command.ActiveMQDestination;
+
+/**
+ * A {@code DestinationAction} represents behavior being taken on a particular {@link ActiveMQDestination}, such as
+ * creation, removal, and reading messages from it or writing messages to it.  The exact behavior being taken on the
+ * specific {@link #getDestination() destination} is represented as a {@link #getVerb() verb} property, which is one of
+ * the following string tokens:
+ * <table>
+ * <tr>
+ * <th>Verb</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <td>{@code create}</td>
+ * <td>Create a specific destination.</td>
+ * </tr>
+ * <tr>
+ * <td>{@code remove}</td>
+ * <td>Remove a specific destination.</td>
+ * </tr>
+ * <tr>
+ * <td>{@code read}</td>
+ * <td>Read (consume) messages from a specific destination.</td>
+ * </tr>
+ * <tr>
+ * <td>{@code write}</td>
+ * <td>Write messages to a specific destination.</td>
+ * </tr>
+ * </table>
+ *
+ * @since 5.10.0
+ */
+public class DestinationAction implements Action {
+
+    private final ConnectionContext connectionContext;
+    private final ActiveMQDestination destination;
+    private final String verb;
+
+    public DestinationAction(ConnectionContext connectionContext, ActiveMQDestination destination, String verb) {
+        if (connectionContext == null) {
+            throw new IllegalArgumentException("ConnectionContext argument cannot be null.");
+        }
+        if (destination == null) {
+            throw new IllegalArgumentException("ActiveMQDestination argument cannot be null.");
+        }
+        if (verb == null) {
+            throw new IllegalArgumentException("verb argument cannot be null.");
+        }
+
+        this.connectionContext = connectionContext;
+        this.destination = destination;
+        this.verb = verb;
+    }
+
+    public ConnectionContext getConnectionContext() {
+        return connectionContext;
+    }
+
+    public ActiveMQDestination getDestination() {
+        return destination;
+    }
+
+    public String getVerb() {
+        return verb;
+    }
+
+    @Override
+    public String toString() {
+        return this.verb + " destination: " + destination;
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/DestinationActionPermissionResolver.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/DestinationActionPermissionResolver.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/DestinationActionPermissionResolver.java
new file mode 100644
index 0000000..0947529
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/DestinationActionPermissionResolver.java
@@ -0,0 +1,272 @@
+/**
+ * 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.shiro.authz;
+
+import org.apache.activemq.command.ActiveMQDestination;
+import org.apache.shiro.authz.Permission;
+import org.apache.shiro.authz.permission.WildcardPermission;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * A {@code DestinationActionPermissionResolver} inspects {@link DestinationAction}s and returns one or more
+ * {@link WildcardPermission}s that must be granted to a {@code Subject} in order for that {@code Subject} to
+ * perform the action being taken on an {@link ActiveMQDestination}.
+ * <p/>
+ * See the {@link #createPermissionString createPermissionString documentation} to see what the
+ * resulting {@link WildcardPermission} instances would look like.
+ *
+ * @see #createPermissionString(org.apache.activemq.command.ActiveMQDestination, String) )
+ * @see #setPermissionStringPrefix(String)
+ * @since 5.10.0
+ */
+public class DestinationActionPermissionResolver implements ActionPermissionResolver {
+
+    private String permissionStringPrefix;
+    private boolean permissionStringCaseSensitive = true;
+
+    /**
+     * Returns the String prefix that should be automatically prepended to a permission String before the
+     * String is converted to a {@link WildcardPermission} instance.  This is convenient if you want to provide a
+     * 'scope' or 'namespace' for ActiveMQ Destinations to clearly distinguish ActiveMQ-specific permissions from any
+     * others you might assign to user accounts.  The default value is {@code null}, indicating no prefix will be
+     * set by default.
+     * <p/>
+     * For example, the default settings might result in permissions Strings that look like this:
+     * <pre>
+     * topic:TEST:create
+     * temp-queue:MyQueue:remove
+     * topic:ActiveMQ.Advisory.*:read
+     * </pre>
+     * <p/>
+     * However, if your application has any application-specific permissions that start with the tokens {@code topic},
+     * {@code temp-topic}, {@code queue}, or {@code temp-queue}, you wouldn't be able to distinguish between
+     * application-specific permissions and those specific to ActiveMQ.  In this case you might set the
+     * {@code permissionStringPrefix}. For example, if you set:
+     * {@code resolver.setPermissionStringPrefix(&quot;jms&quot;);}, the above permission strings would look like this:
+     * <pre>
+     * jms:topic:TEST:create
+     * jms:temp-queue:MyQueue:remove
+     * jms:topic:ActiveMQ.Advisory.*:read
+     * </pre>
+     * <p/>
+     * Similarly, if the {@code permissionStringPrefix} was equal to {@code activeMQ}:
+     * <pre>
+     * activeMQ:topic:TEST:create
+     * activeMQ:temp-queue:MyQueue:remove
+     * activeMQ:topic:ActiveMQ.Advisory.*:read
+     * </pre>
+     *
+     * @return any String prefix that should be automatically prepended to a permission String before the
+     *         String is converted to a {@link WildcardPermission} instance.  Useful for namespacing permissions.
+     */
+    public String getPermissionStringPrefix() {
+        return permissionStringPrefix;
+    }
+
+    /**
+     * Sets the String prefix that should be automatically prepended to a permission String before the
+     * String is converted to a {@link WildcardPermission} instance.  This is convenient if you want to provide a
+     * 'scope' or 'namespace' for ActiveMQ Destinations to clearly distinguish ActiveMQ-specific permissions from any
+     * others you might assign to user accounts. The default value is {@code null}, indicating no prefix will be
+     * set by default.
+     * <p/>
+     * For example, the default settings might result in permissions Strings that look like this:
+     * <pre>
+     * topic:TEST:create
+     * temp-queue:MyQueue:remove
+     * topic:ActiveMQ.Advisory.*:read
+     * </pre>
+     * <p/>
+     * However, if your application has any application-specific permissions that start with the tokens {@code topic},
+     * {@code temp-topic}, {@code queue}, or {@code temp-queue}, you wouldn't be able to distinguish between
+     * application-specific permissions and those specific to ActiveMQ.  In this case you might set the
+     * {@code permissionStringPrefix}. For example, if you set:
+     * {@code resolver.setPermissionStringPrefix(&quot;jms&quot;);}, the above permission strings would look like this:
+     * <pre>
+     * jms:topic:TEST:create
+     * jms:temp-queue:MyQueue:remove
+     * jms:topic:ActiveMQ.Advisory.*:read
+     * </pre>
+     * <p/>
+     * Similarly, if the {@code permissionStringPrefix} was equal to {@code activeMQ}:
+     * <pre>
+     * activeMQ:topic:TEST:create
+     * activeMQ:temp-queue:MyQueue:remove
+     * activeMQ:topic:ActiveMQ.Advisory.*:read
+     * </pre>
+     *
+     * @param permissionStringPrefix any String prefix that should be automatically prepended to a permission String
+     *                               before the String is converted to a {@link WildcardPermission} instance.  Useful
+     *                               for namespacing permissions.
+     */
+    public void setPermissionStringPrefix(String permissionStringPrefix) {
+        this.permissionStringPrefix = permissionStringPrefix;
+    }
+
+    /**
+     * Returns {@code true} if returned {@link WildcardPermission} instances should be considered case-sensitive,
+     * {@code false} otherwise.  The default value is {@code true}, which is <em>not</em> the normal
+     * {@link WildcardPermission} default setting.  This default was chosen to reflect ActiveMQ's
+     * <a href="http://activemq.apache.org/are-destinations-case-sensitive.html">case-sensitive destination names</a>.
+     *
+     * @return {@code true} if returned {@link WildcardPermission} instances should be considered case-sensitive,
+     *         {@code false} otherwise.
+     */
+    public boolean isPermissionStringCaseSensitive() {
+        return permissionStringCaseSensitive;
+    }
+
+    /**
+     * Sets whether returned {@link WildcardPermission} instances should be considered case-sensitive.
+     * The default value is {@code true}, which is <em>not</em> the normal
+     * {@link WildcardPermission} default setting.  This default was chosen to accurately reflect ActiveMQ's
+     * <a href="http://activemq.apache.org/are-destinations-case-sensitive.html">case-sensitive destination names</a>.
+     *
+     * @param permissionStringCaseSensitive whether returned {@link WildcardPermission} instances should be considered
+     *                                      case-sensitive.
+     */
+    public void setPermissionStringCaseSensitive(boolean permissionStringCaseSensitive) {
+        this.permissionStringCaseSensitive = permissionStringCaseSensitive;
+    }
+
+    @Override
+    public Collection<Permission> getPermissions(Action action) {
+        if (!(action instanceof DestinationAction)) {
+            throw new IllegalArgumentException("Action argument must be a " + DestinationAction.class.getName() + " instance.");
+        }
+        DestinationAction da = (DestinationAction) action;
+        return getPermissions(da);
+    }
+
+    protected Collection<Permission> getPermissions(DestinationAction da) {
+        ActiveMQDestination dest = da.getDestination();
+        String verb = da.getVerb();
+        return createPermissions(dest, verb);
+    }
+
+    protected Collection<Permission> createPermissions(ActiveMQDestination dest, String verb) {
+
+        Set<Permission> set;
+
+        if (dest.isComposite()) {
+            ActiveMQDestination[] composites = dest.getCompositeDestinations();
+            set = new LinkedHashSet<Permission>(composites.length);
+            for(ActiveMQDestination d : composites) {
+                Collection<Permission> perms = createPermissions(d, verb);
+                set.addAll(perms);
+            }
+        } else {
+            set = new HashSet<Permission>(1);
+            String permString = createPermissionString(dest, verb);
+            Permission perm = createPermission(permString);
+            set.add(perm);
+        }
+
+        return set;
+    }
+
+    /**
+     * Inspects the specified {@code destination} and {@code verb} and returns a {@link WildcardPermission}-compatible
+     * String the represents the action.
+     * <h3>Format</h3>
+     * This implementation returns WildcardPermission strings with the following format:
+     * <pre>
+     * optionalPermissionStringPrefix + destinationType + ':' + destinationPhysicalName + ':' + actionVerb
+     * </pre>
+     * where:
+     * <ol>
+     * <li>{@code optionalPermissionStringPrefix} is the {@link #getPermissionStringPrefix() permissionStringPrefix}
+     * followed by a colon delimiter (':').  This is only present if the {@code permissionStringPrefix} has been
+     * specified and is non-null</li>
+     * <li>{@code destinationType} is one of the following four string tokens:
+     * <ul>
+     * <li>{@code topic}</li>
+     * <li>{@code temp-topic}</li>
+     * <li>{@code queue}</li>
+     * <li>{@code temp-queue}</li>
+     * </ul>
+     * based on whether the {@link DestinationAction#getDestination() destination} is
+     * a topic, temporary topic, queue, or temporary queue (respectively).
+     * </li>
+     * <li>
+     * {@code destinationPhysicalName} is
+     * {@link org.apache.activemq.command.ActiveMQDestination#getPhysicalName() destination.getPhysicalName()}
+     * </li>
+     * <li>
+     * {@code actionVerb} is {@link DestinationAction#getVerb() action.getVerb()}
+     * </li>
+     * </ol>
+     * <h3>Examples</h3>
+     * With the default settings (no {@link #getPermissionStringPrefix() permissionStringPrefix}), this might produce
+     * strings that look like the following:
+     * <pre>
+     * topic:TEST:create
+     * temp-queue:MyTempQueue:remove
+     * queue:ActiveMQ.Advisory.*:read
+     * </pre>
+     * If {@link #getPermissionStringPrefix() permissionStringPrefix} was set to {@code jms}, the above examples would
+     * look like this:
+     * <pre>
+     * jms:topic:TEST:create
+     * jms:temp-queue:MyTempQueue:remove
+     * jms:queue:ActiveMQ.Advisory.*:read
+     * </pre>
+     *
+     * @param dest the destination to inspect and convert to a {@link WildcardPermission} string.
+     * @param verb the behavior taken on the destination
+     * @return a {@link WildcardPermission} string that represents the specified {@code action}.
+     * @see #getPermissionStringPrefix() getPermissionStringPrefix() for more on why you might want to set this value
+     */
+    protected String createPermissionString(ActiveMQDestination dest, String verb) {
+        if (dest.isComposite()) {
+            throw new IllegalArgumentException("Use createPermissionStrings for composite destinations.");
+        }
+
+        StringBuilder sb = new StringBuilder();
+
+        if (permissionStringPrefix != null) {
+            sb.append(permissionStringPrefix);
+            if (!permissionStringPrefix.endsWith(":")) {
+                sb.append(":");
+            }
+        }
+
+        if (dest.isTemporary()) {
+            sb.append("temp-");
+        }
+        if (dest.isTopic()) {
+            sb.append("topic:");
+        } else {
+            sb.append("queue:");
+        }
+
+        sb.append(dest.getPhysicalName());
+        sb.append(':');
+        sb.append(verb);
+
+        return sb.toString();
+
+    }
+
+    protected Permission createPermission(String permissionString) {
+        return new ActiveMQWildcardPermission(permissionString, isPermissionStringCaseSensitive());
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/env/EnvironmentFilter.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/env/EnvironmentFilter.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/env/EnvironmentFilter.java
new file mode 100644
index 0000000..085f376
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/env/EnvironmentFilter.java
@@ -0,0 +1,45 @@
+/**
+ * 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.shiro.env;
+
+import org.apache.activemq.shiro.SecurityFilter;
+import org.apache.shiro.env.Environment;
+
+/**
+ * An abstract {@code BrokerFilter} that makes the Shiro {@link Environment} available to subclasses.
+ *
+ * @since 5.10.0
+ */
+public abstract class EnvironmentFilter extends SecurityFilter {
+
+    private Environment environment;
+
+    public EnvironmentFilter() {
+    }
+
+    public Environment getEnvironment() {
+        if (this.environment == null) {
+            String msg = "Environment has not yet been set.  This should be done before this broker filter is used.";
+            throw new IllegalStateException(msg);
+        }
+        return environment;
+    }
+
+    public void setEnvironment(Environment environment) {
+        this.environment = environment;
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/env/IniEnvironment.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/env/IniEnvironment.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/env/IniEnvironment.java
new file mode 100644
index 0000000..9ca1980
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/env/IniEnvironment.java
@@ -0,0 +1,136 @@
+/**
+ * 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.shiro.env;
+
+import org.apache.activemq.shiro.authz.ActiveMQPermissionResolver;
+import org.apache.activemq.shiro.mgt.DefaultActiveMqSecurityManager;
+import org.apache.shiro.ShiroException;
+import org.apache.shiro.config.ConfigurationException;
+import org.apache.shiro.config.Ini;
+import org.apache.shiro.config.IniSecurityManagerFactory;
+import org.apache.shiro.env.DefaultEnvironment;
+import org.apache.shiro.io.ResourceUtils;
+import org.apache.shiro.mgt.SecurityManager;
+import org.apache.shiro.realm.Realm;
+import org.apache.shiro.realm.text.IniRealm;
+import org.apache.shiro.util.Initializable;
+import org.apache.shiro.util.LifecycleUtils;
+
+import java.util.Map;
+
+/**
+ * @since 5.10.0
+ */
+public class IniEnvironment extends DefaultEnvironment implements Initializable {
+
+    private Ini ini;
+    private String iniConfig;
+    private String iniResourePath;
+
+    public IniEnvironment() {
+    }
+
+    public IniEnvironment(Ini ini) {
+        this.ini = ini;
+        init();
+    }
+
+    public IniEnvironment(String iniConfig) {
+        Ini ini = new Ini();
+        ini.load(iniConfig);
+        this.ini = ini;
+        init();
+    }
+
+    public void setIni(Ini ini) {
+        this.ini = ini;
+    }
+
+    public void setIniConfig(String config) {
+        this.iniConfig = config;
+    }
+
+    public void setIniResourcePath(String iniResourcePath) {
+        this.iniResourePath = iniResourcePath;
+    }
+
+    @Override
+    public void init() throws ShiroException {
+        //this.environment and this.securityManager are null.  Try Ini config:
+        Ini ini = this.ini;
+        if (ini != null) {
+            apply(ini);
+        }
+
+        if (this.objects.isEmpty() && this.iniConfig != null) {
+            ini = new Ini();
+            ini.load(this.iniConfig);
+            apply(ini);
+        }
+
+        if (this.objects.isEmpty() && this.iniResourePath != null) {
+            ini = new Ini();
+            ini.loadFromPath(this.iniResourePath);
+            apply(ini);
+        }
+
+        if (this.objects.isEmpty()) {
+            if (ResourceUtils.resourceExists("classpath:shiro.ini")) {
+                ini = new Ini();
+                ini.loadFromPath("classpath:shiro.ini");
+                apply(ini);
+            }
+        }
+
+        if (this.objects.isEmpty()) {
+            String msg = "Configuration error.  All heuristics for acquiring Shiro INI config " +
+                    "have been exhausted.  Ensure you configure one of the following properties: " +
+                    "1) ini 2) iniConfig 3) iniResourcePath and the Ini sections are not empty.";
+            throw new ConfigurationException(msg);
+        }
+
+        LifecycleUtils.init(this.objects.values());
+    }
+
+    protected void apply(Ini ini) {
+        if (ini != null && !ini.isEmpty()) {
+            Map<String, ?> objects = createObjects(ini);
+            this.ini = ini;
+            this.objects.clear();
+            this.objects.putAll(objects);
+        }
+    }
+
+    private Map<String, ?> createObjects(Ini ini) {
+        IniSecurityManagerFactory factory = new IniSecurityManagerFactory(ini) {
+
+            @Override
+            protected SecurityManager createDefaultInstance() {
+                return new DefaultActiveMqSecurityManager();
+            }
+
+            @Override
+            protected Realm createRealm(Ini ini) {
+                IniRealm realm = (IniRealm)super.createRealm(ini);
+                realm.setPermissionResolver(new ActiveMQPermissionResolver());
+                return realm;
+            }
+        };
+        factory.getInstance(); //trigger beans creation
+        return factory.getBeans();
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/mgt/DefaultActiveMqSecurityManager.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/mgt/DefaultActiveMqSecurityManager.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/mgt/DefaultActiveMqSecurityManager.java
new file mode 100644
index 0000000..e3d8e61
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/mgt/DefaultActiveMqSecurityManager.java
@@ -0,0 +1,41 @@
+/**
+ * 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.shiro.mgt;
+
+import org.apache.activemq.shiro.session.mgt.DisabledSessionManager;
+import org.apache.shiro.mgt.DefaultSecurityManager;
+import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
+import org.apache.shiro.mgt.DefaultSubjectDAO;
+
+/**
+ * @since 5.10.0
+ */
+public class DefaultActiveMqSecurityManager extends DefaultSecurityManager {
+
+    public DefaultActiveMqSecurityManager() {
+        super();
+
+        //disable sessions entirely:
+        setSessionManager(new DisabledSessionManager());
+
+        //also prevent the SecurityManager impl from using the Session as a storage medium (i.e. after authc):
+        DefaultSubjectDAO subjectDao = (DefaultSubjectDAO)getSubjectDAO();
+        DefaultSessionStorageEvaluator sessionStorageEvaluator =
+                (DefaultSessionStorageEvaluator)subjectDao.getSessionStorageEvaluator();
+        sessionStorageEvaluator.setSessionStorageEnabled(false);
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/session/mgt/DisabledSessionManager.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/session/mgt/DisabledSessionManager.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/session/mgt/DisabledSessionManager.java
new file mode 100644
index 0000000..5234b1b
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/session/mgt/DisabledSessionManager.java
@@ -0,0 +1,39 @@
+/**
+ * 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.shiro.session.mgt;
+
+import org.apache.shiro.session.Session;
+import org.apache.shiro.session.SessionException;
+import org.apache.shiro.session.mgt.SessionContext;
+import org.apache.shiro.session.mgt.SessionKey;
+import org.apache.shiro.session.mgt.SessionManager;
+
+/**
+ * @since 5.10.0
+ */
+public class DisabledSessionManager implements SessionManager {
+
+    @Override
+    public Session start(SessionContext context) {
+        throw new UnsupportedOperationException("Sessions are disabled.");
+    }
+
+    @Override
+    public Session getSession(SessionKey key) throws SessionException {
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/ConnectionSubjectFactory.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/ConnectionSubjectFactory.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/ConnectionSubjectFactory.java
new file mode 100644
index 0000000..322f616
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/ConnectionSubjectFactory.java
@@ -0,0 +1,48 @@
+/**
+ * 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.shiro.subject;
+
+import org.apache.activemq.shiro.ConnectionReference;
+import org.apache.shiro.subject.Subject;
+
+/**
+ * A {@code ConnectionSubjectFactory} creates a {@code Subject} instance that represents the connection client's identity.
+ * <p/>
+ * Most implementations will simply use the {@link Subject.Builder Subject.Builder} to create an anonymous
+ * {@code Subject} instance and let a downstream {@link org.apache.activemq.shiro.authc.AuthenticationFilter} authenticate the {@code Subject} based on
+ * any credentials associated with the connection.  After authentication, the {@code Subject} will have an identity, and
+ * this is the expected flow for most connection clients.
+ * <p/>
+ * However, if there is some other data associated with the connection that can be inspected to create a
+ * {@code Subject} instance beyond what the {@link DefaultConnectionSubjectFactory} provides, this interface allows that
+ * logic to be plugged in as necessary.
+ *
+ * @see DefaultConnectionSubjectFactory
+ * @since 5.10.0
+ */
+public interface ConnectionSubjectFactory {
+
+    /**
+     * Creates a {@code Subject} instance representing the connection client.  It is common for {@code Subject} instances
+     * returned from this method to be anonymous until a downstream {@link org.apache.activemq.shiro.authc.AuthenticationFilter} authenticates the
+     * subject to associate an identity.
+     *
+     * @param ref a reference to the client's connection metadata
+     * @return a {@code Subject} instance representing the connection client.
+     */
+    Subject createSubject(ConnectionReference ref);
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/ConnectionSubjectResolver.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/ConnectionSubjectResolver.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/ConnectionSubjectResolver.java
new file mode 100644
index 0000000..f71fb05
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/ConnectionSubjectResolver.java
@@ -0,0 +1,67 @@
+/**
+ * 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.shiro.subject;
+
+import org.apache.activemq.broker.ConnectionContext;
+import org.apache.activemq.security.SecurityContext;
+import org.apache.activemq.shiro.ConnectionReference;
+import org.apache.shiro.subject.Subject;
+
+/**
+ * A {@code SubjectResolver} that acquires the current Subject from a {@link org.apache.activemq.shiro.ConnectionReference}.
+ *
+ * @since 5.10.0
+ */
+public class ConnectionSubjectResolver implements SubjectResolver {
+
+    private final SubjectSecurityContext securityContext;
+
+    public ConnectionSubjectResolver(ConnectionContext connCtx) {
+        if (connCtx == null) {
+            throw new IllegalArgumentException("ConnectionContext argument cannot be null.");
+        }
+        SecurityContext secCtx = connCtx.getSecurityContext();
+        if (secCtx == null) {
+            String msg = "There is no SecurityContext available on the ConnectionContext.  It " +
+                    "is expected that a previous broker in the chain will create the SecurityContext prior to this " +
+                    "resolver being invoked.  Ensure you have configured the SubjectPlugin and that it is " +
+                    "configured before all other Shiro-dependent broker filters.";
+            throw new IllegalArgumentException(msg);
+        }
+        if (!(secCtx instanceof SubjectSecurityContext)) {
+            String msg = "The specified SecurityContext is expected to be a " + SubjectSecurityContext.class.getName() +
+                    " instance.  The current instance's class: " + secCtx.getClass().getName();
+            throw new IllegalArgumentException(msg);
+        }
+        this.securityContext = (SubjectSecurityContext) secCtx;
+    }
+
+    public ConnectionSubjectResolver(ConnectionReference conn) {
+        this(conn.getConnectionContext());
+    }
+
+    @Override
+    public Subject getSubject() {
+        Subject subject = securityContext.getSubject();
+        if (subject != null) {
+            return subject;
+        }
+        String msg = "There is no Subject available in the SecurityContext.  Ensure " +
+                "that the SubjectPlugin is configured before all other Shiro-dependent broker filters.";
+        throw new IllegalStateException(msg);
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/DefaultConnectionSubjectFactory.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/DefaultConnectionSubjectFactory.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/DefaultConnectionSubjectFactory.java
new file mode 100644
index 0000000..853f701
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/DefaultConnectionSubjectFactory.java
@@ -0,0 +1,52 @@
+/**
+ * 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.shiro.subject;
+
+import org.apache.activemq.shiro.ConnectionReference;
+import org.apache.activemq.shiro.authc.AuthenticationPolicy;
+import org.apache.activemq.shiro.authc.DefaultAuthenticationPolicy;
+import org.apache.shiro.subject.Subject;
+
+/**
+ * @since 5.10.0
+ */
+public class DefaultConnectionSubjectFactory implements ConnectionSubjectFactory {
+
+    private AuthenticationPolicy authenticationPolicy;
+
+    public DefaultConnectionSubjectFactory() {
+        this.authenticationPolicy = new DefaultAuthenticationPolicy();
+    }
+
+    public AuthenticationPolicy getAuthenticationPolicy() {
+        return authenticationPolicy;
+    }
+
+    public void setAuthenticationPolicy(AuthenticationPolicy authenticationPolicy) {
+        this.authenticationPolicy = authenticationPolicy;
+    }
+
+    @Override
+    public Subject createSubject(ConnectionReference conn) {
+
+        Subject.Builder builder = new Subject.Builder(conn.getEnvironment().getSecurityManager());
+
+        authenticationPolicy.customizeSubject(builder, conn);
+
+        return builder.buildSubject();
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/SubjectConnectionReference.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/SubjectConnectionReference.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/SubjectConnectionReference.java
new file mode 100644
index 0000000..dd415cb
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/SubjectConnectionReference.java
@@ -0,0 +1,46 @@
+/**
+ * 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.shiro.subject;
+
+import org.apache.activemq.broker.ConnectionContext;
+import org.apache.activemq.command.ConnectionInfo;
+import org.apache.activemq.shiro.ConnectionReference;
+import org.apache.shiro.env.Environment;
+import org.apache.shiro.subject.Subject;
+
+/**
+ * {@link org.apache.activemq.shiro.ConnectionReference} that further provides access to the connection's Subject instance.
+ *
+ * @since 5.10.0
+ */
+public class SubjectConnectionReference extends ConnectionReference {
+
+    private final Subject subject;
+
+    public SubjectConnectionReference(ConnectionContext connCtx, ConnectionInfo connInfo,
+                                      Environment environment, Subject subject) {
+        super(connCtx, connInfo, environment);
+        if (subject == null) {
+            throw new IllegalArgumentException("Subject argument cannot be null.");
+        }
+        this.subject = subject;
+    }
+
+    public Subject getSubject() {
+        return subject;
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/SubjectFilter.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/SubjectFilter.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/SubjectFilter.java
new file mode 100644
index 0000000..962cd8e
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/SubjectFilter.java
@@ -0,0 +1,119 @@
+/**
+ * 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.shiro.subject;
+
+import org.apache.activemq.broker.ConnectionContext;
+import org.apache.activemq.command.ConnectionInfo;
+import org.apache.activemq.security.SecurityContext;
+import org.apache.activemq.shiro.ConnectionReference;
+import org.apache.activemq.shiro.DefaultSecurityContextFactory;
+import org.apache.activemq.shiro.SecurityContextFactory;
+import org.apache.activemq.shiro.env.EnvironmentFilter;
+import org.apache.shiro.subject.Subject;
+
+/**
+ * The {@code SubjectFilter} ensures a Shiro {@link Subject} representing the client's identity is associated with
+ * every connection to the ActiveMQ Broker.  The {@code Subject} is made available to downstream broker filters so
+ * they may perform security checks as necessary.
+ * <p/>
+ * This implementation does not perform any security checks/assertions itself.  It is expected that other broker filters
+ * will be configured after this one and those will perform any security behavior or checks as necessary.
+ *
+ * @since 5.10.0
+ */
+public class SubjectFilter extends EnvironmentFilter {
+
+    private ConnectionSubjectFactory connectionSubjectFactory;
+    private SecurityContextFactory securityContextFactory;
+
+    public SubjectFilter() {
+        this.connectionSubjectFactory = new DefaultConnectionSubjectFactory();
+        this.securityContextFactory = new DefaultSecurityContextFactory();
+    }
+
+    public ConnectionSubjectFactory getConnectionSubjectFactory() {
+        return connectionSubjectFactory;
+    }
+
+    public void setConnectionSubjectFactory(ConnectionSubjectFactory connectionSubjectFactory) {
+        if (connectionSubjectFactory == null) {
+            throw new IllegalArgumentException("ConnectionSubjectFactory argument cannot be null.");
+        }
+        this.connectionSubjectFactory = connectionSubjectFactory;
+    }
+
+    public SecurityContextFactory getSecurityContextFactory() {
+        return this.securityContextFactory;
+    }
+
+    public void setSecurityContextFactory(SecurityContextFactory securityContextFactory) {
+        if (securityContextFactory == null) {
+            throw new IllegalArgumentException("SecurityContextFactory argument cannot be null.");
+        }
+        this.securityContextFactory = securityContextFactory;
+    }
+
+    protected Subject createSubject(ConnectionReference conn) {
+        return this.connectionSubjectFactory.createSubject(conn);
+    }
+
+    protected SecurityContext createSecurityContext(SubjectConnectionReference conn) {
+        return this.securityContextFactory.createSecurityContext(conn);
+    }
+
+    /**
+     * Creates a {@link Subject} instance reflecting the specified Connection.  The {@code Subject} is then stored in
+     * a {@link SecurityContext} instance which is set as the Connection's
+     * {@link ConnectionContext#setSecurityContext(org.apache.activemq.security.SecurityContext) securityContext}.
+     *
+     * @param context state associated with the client's connection
+     * @param info    info about the client's connection
+     * @throws Exception if there is a problem creating a Subject or {@code SecurityContext} instance.
+     */
+    @Override
+    public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
+
+        if (isEnabled()) {
+
+            SecurityContext secCtx = context.getSecurityContext();
+
+            if (secCtx == null) {
+                ConnectionReference conn = new ConnectionReference(context, info, getEnvironment());
+                Subject subject = createSubject(conn);
+                SubjectConnectionReference subjectConn = new SubjectConnectionReference(context, info, getEnvironment(), subject);
+                secCtx = createSecurityContext(subjectConn);
+                context.setSecurityContext(secCtx);
+            }
+        }
+
+        try {
+            super.addConnection(context, info);
+        } catch (Exception e) {
+            context.setSecurityContext(null);
+            throw e;
+        }
+    }
+
+    @Override
+    public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Exception {
+        try {
+            super.removeConnection(context, info, error);
+        } finally {
+            context.setSecurityContext(null);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/SubjectResolver.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/SubjectResolver.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/SubjectResolver.java
new file mode 100644
index 0000000..7273ff3
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/SubjectResolver.java
@@ -0,0 +1,32 @@
+/**
+ * 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.shiro.subject;
+
+import org.apache.shiro.subject.Subject;
+
+/**
+ * @since 5.10.0
+ */
+public interface SubjectResolver {
+
+    /**
+     * Resolves and returns a {@link Subject} instance.  If one cannot be found, a runtime {@code Exception} is thrown.
+     *
+     * @return a resolved {@code Subject} instance.
+     */
+    Subject getSubject() throws RuntimeException;
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/SubjectSecurityContext.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/SubjectSecurityContext.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/SubjectSecurityContext.java
new file mode 100644
index 0000000..1d752e2
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/subject/SubjectSecurityContext.java
@@ -0,0 +1,89 @@
+/**
+ * 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.shiro.subject;
+
+import org.apache.activemq.command.ActiveMQDestination;
+import org.apache.activemq.security.SecurityContext;
+import org.apache.shiro.subject.Subject;
+
+import java.security.Principal;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * ActiveMQ {@code SecurityContext} implementation that retains a Shiro {@code Subject} instance for use during
+ * security checks and other security-related operations.
+ *
+ * @since 5.10.0
+ */
+public class SubjectSecurityContext extends SecurityContext {
+
+    private final Subject subject;
+
+    public SubjectSecurityContext(SubjectConnectionReference conn) {
+        //The username might not be available at the time this object is instantiated (the Subject might be
+        //anonymous).  Instead we override the getUserName() method below and that will always delegate to the
+        //Subject to return the most accurate/freshest username available.
+        super(null);
+        this.subject = conn.getSubject();
+    }
+
+    public Subject getSubject() {
+        return subject;
+    }
+
+    private static String getUsername(Subject subject) {
+        if (subject != null) {
+            Object principal = subject.getPrincipal();
+            if (principal != null) {
+                return String.valueOf(principal);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String getUserName() {
+        return getUsername(this.subject);
+    }
+
+    private static UnsupportedOperationException notAllowed(String methodName) {
+        String msg = "Do not invoke the '" + methodName + "' method or use a broker filter that invokes it.  Use one " +
+                "of the Shiro-based security filters instead.";
+        return new UnsupportedOperationException(msg);
+    }
+
+    @Override
+    public boolean isInOneOf(Set<?> allowedPrincipals) {
+        throw notAllowed("isInOneOf");
+    }
+
+    @Override
+    public ConcurrentHashMap<ActiveMQDestination, ActiveMQDestination> getAuthorizedReadDests() {
+        throw notAllowed("getAuthorizedReadDests");
+    }
+
+    @Override
+    public ConcurrentHashMap<ActiveMQDestination, ActiveMQDestination> getAuthorizedWriteDests() {
+        throw notAllowed("getAuthorizedWriteDests");
+    }
+
+    @Override
+    public Set<Principal> getPrincipals() {
+        throw notAllowed("getPrincipals");
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/java/org/apache/activemq/shiro/ConnectionReferenceTest.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/java/org/apache/activemq/shiro/ConnectionReferenceTest.java b/activemq-shiro/src/test/java/org/apache/activemq/shiro/ConnectionReferenceTest.java
new file mode 100644
index 0000000..9290afd
--- /dev/null
+++ b/activemq-shiro/src/test/java/org/apache/activemq/shiro/ConnectionReferenceTest.java
@@ -0,0 +1,43 @@
+/**
+ * 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.shiro;
+
+import org.apache.activemq.broker.ConnectionContext;
+import org.apache.activemq.command.ConnectionInfo;
+import org.apache.shiro.env.DefaultEnvironment;
+import org.junit.Test;
+
+/**
+ * @since 5.10.0
+ */
+public class ConnectionReferenceTest {
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testNoConnectionContext() {
+        new ConnectionReference(null, new ConnectionInfo(), new DefaultEnvironment());
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testNoConnectionInfo() {
+        new ConnectionReference(new ConnectionContext(), null, new DefaultEnvironment());
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testNoEnvironment() {
+        new ConnectionReference(new ConnectionContext(), new ConnectionInfo(), null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/java/org/apache/activemq/shiro/SecurityFilterTest.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/java/org/apache/activemq/shiro/SecurityFilterTest.java b/activemq-shiro/src/test/java/org/apache/activemq/shiro/SecurityFilterTest.java
new file mode 100644
index 0000000..ca64205
--- /dev/null
+++ b/activemq-shiro/src/test/java/org/apache/activemq/shiro/SecurityFilterTest.java
@@ -0,0 +1,37 @@
+/**
+ * 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.shiro;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @since 5.10.0
+ */
+public class SecurityFilterTest {
+
+    @Test
+    public void testEnabled() {
+
+        SecurityFilter filter = new SecurityFilter() {};
+        assertTrue(filter.isEnabled()); //enabled by default
+
+        filter.setEnabled(false);
+        assertFalse(filter.isEnabled());
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/java/org/apache/activemq/shiro/ShiroPluginTest.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/java/org/apache/activemq/shiro/ShiroPluginTest.java b/activemq-shiro/src/test/java/org/apache/activemq/shiro/ShiroPluginTest.java
new file mode 100644
index 0000000..67e9d61
--- /dev/null
+++ b/activemq-shiro/src/test/java/org/apache/activemq/shiro/ShiroPluginTest.java
@@ -0,0 +1,403 @@
+/**
+ * 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.shiro;
+
+import org.apache.activemq.broker.BrokerPlugin;
+import org.apache.activemq.broker.BrokerService;
+import org.apache.activemq.broker.MutableBrokerFilter;
+import org.apache.activemq.shiro.authc.AuthenticationFilter;
+import org.apache.activemq.shiro.authc.DefaultAuthenticationPolicy;
+import org.apache.activemq.shiro.authz.AuthorizationFilter;
+import org.apache.activemq.shiro.env.IniEnvironment;
+import org.apache.activemq.shiro.subject.DefaultConnectionSubjectFactory;
+import org.apache.activemq.shiro.subject.SubjectFilter;
+import org.apache.activemq.test.JmsResourceProvider;
+import org.apache.activemq.test.TestSupport;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.config.Ini;
+import org.apache.shiro.env.DefaultEnvironment;
+import org.apache.shiro.env.Environment;
+import org.apache.shiro.mgt.DefaultSecurityManager;
+import org.apache.shiro.realm.text.IniRealm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+
+/**
+ * @since 5.10.0
+ */
+public class ShiroPluginTest extends TestSupport {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ShiroPluginTest.class);
+
+    protected BrokerService broker;
+    protected SecureJmsResourceProvider resourceProvider;
+    protected ConnectionFactory connectionFactory;
+    protected Connection connection;
+    protected Session session;
+    protected Destination destination;
+    protected MessageConsumer consumer;
+    protected MessageProducer producer;
+
+    @Override
+    protected void setUp() throws Exception {
+        resourceProvider = new SecureJmsResourceProvider();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        LOG.info("Shutting down broker...");
+
+        if (session != null) {
+            session.close();
+        }
+        session = null;
+
+        if (connection != null) {
+            connection.close();
+        }
+        connection = null;
+
+        if (broker != null) {
+            broker.stop();
+            broker.waitUntilStopped();
+        }
+        broker = null;
+
+        LOG.info("Broker shut down.");
+    }
+
+    protected void reconnect() throws Exception {
+        reconnect(null, null);
+    }
+
+    protected void reconnect(String username, String password) throws Exception {
+        if (connection != null) {
+            // Close the prev connection.
+            connection.close();
+        }
+        session = null;
+        if (username == null && password == null) {
+            connection = resourceProvider.createConnection(connectionFactory);
+        } else {
+            connection = resourceProvider.createConnection(connectionFactory, username, password);
+        }
+        reconnectSession();
+        connection.start();
+    }
+
+    protected void reconnectSession() throws JMSException {
+        if (session != null) {
+            session.close();
+        }
+        session = resourceProvider.createSession(connection);
+        destination = resourceProvider.createDestination(session, getSubject());
+        producer = resourceProvider.createProducer(session, destination);
+        consumer = resourceProvider.createConsumer(session, destination);
+    }
+
+    protected ConnectionFactory newConnectionFactory() throws Exception {
+        return resourceProvider.createConnectionFactory();
+    }
+
+    protected void start() throws Exception {
+        startBroker();
+        topic = resourceProvider.isTopic();
+        connectionFactory = newConnectionFactory();
+    }
+
+    protected void startBroker() throws Exception {
+        broker.start();
+        broker.waitUntilStarted();
+    }
+
+    protected BrokerService createBroker(BrokerPlugin... plugins) throws Exception {
+        return createBroker(plugins, resourceProvider.getServerUri());
+    }
+
+    protected BrokerService createBroker(BrokerPlugin[] plugins, String... connectorUris) throws Exception {
+        BrokerService brokerService = new BrokerService();
+        if (plugins != null && plugins.length > 0) {
+            brokerService.setPlugins(plugins);
+        }
+        if (connectorUris != null) {
+            for (String uri : connectorUris) {
+                brokerService.addConnector(uri);
+            }
+        }
+        return brokerService;
+    }
+
+    protected ShiroPlugin createPlugin(String iniPath) {
+        Ini ini = Ini.fromResourcePath(iniPath);
+        Environment env = new IniEnvironment(ini);
+        ShiroPlugin plugin = new ShiroPlugin();
+        plugin.setEnvironment(env);
+        return plugin;
+    }
+
+    public void testNoEnvironmentOrSecurityManager() throws Exception {
+        //should build IniEnvironment from shiro.ini in the classpath at the least:
+        ShiroPlugin plugin = new ShiroPlugin();
+        plugin.installPlugin(new MutableBrokerFilter(null));
+
+        Ini ini = Ini.fromResourcePath("classpath:shiro.ini");
+        IniRealm realm = (IniRealm) ((DefaultSecurityManager) plugin.getEnvironment().getSecurityManager()).getRealms().iterator().next();
+        assertEquals(ini, realm.getIni());
+    }
+
+    public void testSetIni() throws Exception {
+        ShiroPlugin plugin = new ShiroPlugin();
+        Ini ini = Ini.fromResourcePath("classpath:minimal.shiro.ini");
+        plugin.setIni(ini);
+        plugin.installPlugin(new MutableBrokerFilter(null));
+
+        IniRealm realm = (IniRealm) ((DefaultSecurityManager) plugin.getEnvironment().getSecurityManager()).getRealms().iterator().next();
+        assertSame(ini, realm.getIni());
+    }
+
+    public void testSetIniString() throws Exception {
+        ShiroPlugin plugin = new ShiroPlugin();
+        plugin.setIniConfig(
+                "[users]\n" +
+                "system = manager, system\n" +
+                "[roles]\n" +
+                "system = *");
+        plugin.installPlugin(new MutableBrokerFilter(null));
+
+        IniRealm realm = (IniRealm) ((DefaultSecurityManager) plugin.getEnvironment().getSecurityManager()).getRealms().iterator().next();
+        Ini ini = realm.getIni();
+        assertEquals(1, ini.getSection("users").size());
+        assertEquals("manager, system", ini.getSection("users").get("system"));
+        assertEquals(1, ini.getSection("roles").size());
+        assertEquals("*", ini.getSection("roles").get("system"));
+    }
+
+    public void testSetIniResourcePath() throws Exception {
+        ShiroPlugin plugin = new ShiroPlugin();
+
+        String path = "classpath:minimal.shiro.ini";
+
+        plugin.setIniResourcePath(path);
+        plugin.installPlugin(new MutableBrokerFilter(null));
+
+        Ini ini = Ini.fromResourcePath(path);
+
+        IniRealm realm = (IniRealm) ((DefaultSecurityManager) plugin.getEnvironment().getSecurityManager()).getRealms().iterator().next();
+        assertEquals(ini, realm.getIni());
+    }
+
+    public void testSetSubjectFilter() {
+        ShiroPlugin plugin = new ShiroPlugin();
+        SubjectFilter filter = new SubjectFilter();
+        plugin.setSubjectFilter(filter);
+        assertSame(filter, plugin.getSubjectFilter());
+        //assert that the AuthenticationFilter is always the next filter in the chain after the SubjectFilter:
+        assertSame(plugin.getAuthenticationFilter(), filter.getNext());
+    }
+
+    public void testSetAuthenticationFilter() {
+        ShiroPlugin plugin = new ShiroPlugin();
+        AuthenticationFilter filter = new AuthenticationFilter();
+        plugin.setAuthenticationFilter(filter);
+        assertSame(filter, plugin.getAuthenticationFilter());
+        //assert that the AuthenticationFilter is always the next filter in the chain after the SubjectFilter:
+        assertSame(plugin.getSubjectFilter().getNext(), filter);
+    }
+
+    public void testSetAuthorizationFilter() {
+        ShiroPlugin plugin = new ShiroPlugin();
+        AuthorizationFilter filter = new AuthorizationFilter();
+        plugin.setAuthorizationFilter(filter);
+        assertSame(filter, plugin.getAuthorizationFilter());
+        //assert that the AuthenticationFilter is always the next filter in the chain after the AuthenticationFilter:
+        assertSame(plugin.getAuthenticationFilter().getNext(), filter);
+    }
+
+    public void testSetEnvironment() {
+        ShiroPlugin plugin = new ShiroPlugin();
+        Environment env = new DefaultEnvironment();
+        plugin.setEnvironment(env);
+        assertSame(env, plugin.getEnvironment());
+    }
+
+    public void testSetSecurityManager() {
+        ShiroPlugin plugin = new ShiroPlugin();
+        org.apache.shiro.mgt.SecurityManager securityManager = new DefaultSecurityManager();
+        plugin.setSecurityManager(securityManager);
+        assertSame(securityManager, plugin.getSecurityManager());
+    }
+
+    public void testSecurityManagerWhenInstalled() throws Exception {
+        ShiroPlugin plugin = new ShiroPlugin();
+        org.apache.shiro.mgt.SecurityManager securityManager = new DefaultSecurityManager();
+        plugin.setSecurityManager(securityManager);
+
+        assertNull(plugin.getEnvironment()); //we will auto-create one when only a sm is provided
+
+        plugin.installPlugin(new MutableBrokerFilter(null));
+
+        assertSame(securityManager, plugin.getSecurityManager());
+        assertNotNull(plugin.getEnvironment());
+        assertSame(securityManager, plugin.getEnvironment().getSecurityManager());
+    }
+
+    public void testEnabledWhenNotInstalled() {
+        ShiroPlugin plugin = new ShiroPlugin();
+        assertTrue(plugin.isEnabled()); //enabled by default
+
+        plugin.setEnabled(false);
+        assertFalse(plugin.isEnabled());
+
+        plugin.setEnabled(true);
+        assertTrue(plugin.isEnabled());
+    }
+
+    public void testEnabledWhenInstalled() throws Exception {
+        ShiroPlugin plugin = createPlugin("classpath:minimal.shiro.ini");
+        this.broker = createBroker(plugin);
+        start();
+        assertTrue(plugin.isEnabled());
+
+        plugin.setEnabled(false);
+        assertFalse(plugin.isEnabled());
+
+        plugin.setEnabled(true);
+        assertTrue(plugin.isEnabled());
+    }
+
+    public void testAuthenticationEnabledWhenNotInstalled() {
+        ShiroPlugin plugin = new ShiroPlugin();
+        assertTrue(plugin.isAuthenticationEnabled());
+
+        plugin.setAuthenticationEnabled(false);
+        assertFalse(plugin.isAuthenticationEnabled());
+
+        plugin.setAuthenticationEnabled(true);
+        assertTrue(plugin.isAuthenticationEnabled());
+    }
+
+    public void testAuthenticationEnabledWhenInstalled() throws Exception {
+        ShiroPlugin plugin = new ShiroPlugin();
+        plugin.setEnvironment(new DefaultEnvironment());
+        plugin.installPlugin(new MutableBrokerFilter(null));
+
+        assertTrue(plugin.isAuthenticationEnabled());
+
+        plugin.setAuthenticationEnabled(false);
+        assertFalse(plugin.isAuthenticationEnabled());
+
+        plugin.setAuthenticationEnabled(true);
+        assertTrue(plugin.isAuthenticationEnabled());
+    }
+
+    public void testSetAuthenticationPolicy() {
+        ShiroPlugin plugin = new ShiroPlugin();
+        DefaultAuthenticationPolicy policy = new DefaultAuthenticationPolicy();
+        plugin.setAuthenticationPolicy(policy);
+        assertSame(policy, plugin.getAuthenticationPolicy());
+        assertSame(policy, plugin.getAuthenticationFilter().getAuthenticationPolicy());
+        assertSame(policy, ((DefaultConnectionSubjectFactory) plugin.getSubjectFilter().getConnectionSubjectFactory()).getAuthenticationPolicy());
+    }
+
+    public void testAuthorizationEnabledWhenNotInstalled() {
+        ShiroPlugin plugin = new ShiroPlugin();
+        assertTrue(plugin.isAuthorizationEnabled());
+
+        plugin.setAuthorizationEnabled(false);
+        assertFalse(plugin.isAuthorizationEnabled());
+
+        plugin.setAuthorizationEnabled(true);
+        assertTrue(plugin.isAuthorizationEnabled());
+    }
+
+    public void testAuthorizationEnabledWhenInstalled() throws Exception {
+        ShiroPlugin plugin = new ShiroPlugin();
+        plugin.setEnvironment(new DefaultEnvironment());
+        plugin.installPlugin(new MutableBrokerFilter(null));
+
+        assertTrue(plugin.isAuthorizationEnabled());
+
+        plugin.setAuthorizationEnabled(false);
+        assertFalse(plugin.isAuthorizationEnabled());
+
+        plugin.setAuthorizationEnabled(true);
+        assertTrue(plugin.isAuthorizationEnabled());
+    }
+
+
+    public void testSimple() throws Exception {
+        ShiroPlugin plugin = createPlugin("classpath:minimal.shiro.ini");
+        this.broker = createBroker(plugin);
+        start();
+        reconnect();
+    }
+
+    public void testDisabled() throws Exception {
+        ShiroPlugin plugin = createPlugin("classpath:nosystem.shiro.ini");
+        plugin.setEnabled(false);
+        this.broker = createBroker(plugin);
+        start();
+    }
+
+    public void testRuntimeDisableEnableChanges() throws Exception {
+        ShiroPlugin plugin = createPlugin("classpath:nosystem.shiro.ini");
+        ((DefaultAuthenticationPolicy) plugin.getAuthenticationPolicy()).setVmConnectionAuthenticationRequired(true);
+        plugin.setEnabled(false);
+        this.broker = createBroker(plugin);
+        start();
+
+        //connection has no credentials.  When disabled, this should succeed:
+        reconnect();
+
+        //now enable the plugin and assert that credentials are required:
+        plugin.setEnabled(true);
+
+        try {
+            reconnect();
+            fail("Connections without passwords in this configuration should fail.");
+        } catch (JMSException expected) {
+            assertTrue(expected.getCause() instanceof AuthenticationException);
+        }
+
+        //this should work now that we're authenticating:
+        reconnect("foo", "bar");
+    }
+
+    static class SecureJmsResourceProvider extends JmsResourceProvider {
+
+        /**
+         * Creates a connection, authenticating with the specified username and password.
+         *
+         * @see org.apache.activemq.test.JmsResourceProvider#createConnection(javax.jms.ConnectionFactory)
+         */
+        public Connection createConnection(ConnectionFactory cf, String username, String password) throws JMSException {
+            Connection connection = cf.createConnection(username, password);
+            if (getClientID() != null) {
+                connection.setClientID(getClientID());
+            }
+            return connection;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/java/org/apache/activemq/shiro/authc/AuthenticationFilterTest.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/java/org/apache/activemq/shiro/authc/AuthenticationFilterTest.java b/activemq-shiro/src/test/java/org/apache/activemq/shiro/authc/AuthenticationFilterTest.java
new file mode 100644
index 0000000..f2ded62
--- /dev/null
+++ b/activemq-shiro/src/test/java/org/apache/activemq/shiro/authc/AuthenticationFilterTest.java
@@ -0,0 +1,86 @@
+/**
+ * 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.shiro.authc;
+
+import org.apache.activemq.broker.Broker;
+import org.apache.activemq.broker.BrokerPluginSupport;
+import org.apache.activemq.broker.ConnectionContext;
+import org.apache.activemq.command.ConnectionInfo;
+import org.apache.activemq.shiro.subject.SubjectAdapter;
+import org.apache.activemq.shiro.subject.SubjectConnectionReference;
+import org.apache.activemq.shiro.subject.SubjectSecurityContext;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.env.DefaultEnvironment;
+import org.apache.shiro.subject.Subject;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @since 5.10.0
+ */
+public class AuthenticationFilterTest {
+
+    AuthenticationFilter filter = new AuthenticationFilter();
+
+    @Test
+    public void testSetAuthenticationTokenFactory() {
+        AuthenticationTokenFactory factory = new AuthenticationTokenFactory() {
+            @Override
+            public AuthenticationToken getAuthenticationToken(SubjectConnectionReference ref) throws Exception {
+                return null;
+            }
+        };
+        filter.setAuthenticationTokenFactory(factory);
+        assertSame(factory, filter.getAuthenticationTokenFactory());
+    }
+
+    @Test
+    public void testRemoveAuthenticationWithLogoutThrowable() throws Exception {
+
+        final boolean[] invoked = new boolean[1];
+
+        Broker broker = new BrokerPluginSupport() {
+            @Override
+            public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Exception {
+                invoked[0] = true;
+            }
+        };
+
+        DefaultEnvironment env = new DefaultEnvironment();
+
+        filter.setNext(broker);
+        filter.setEnvironment(env);
+
+        Subject subject = new SubjectAdapter() {
+            @Override
+            public void logout() {
+                throw new RuntimeException("Simulated failure.");
+            }
+        };
+
+        ConnectionContext ctx = new ConnectionContext();
+        ConnectionInfo info = new ConnectionInfo();
+        SubjectConnectionReference conn = new SubjectConnectionReference(ctx, info, env, subject);
+        SubjectSecurityContext ssc = new SubjectSecurityContext(conn);
+        ctx.setSecurityContext(ssc);
+
+        filter.removeConnection(ctx, info, null);
+
+        assertTrue(invoked[0]);
+    }
+}


[2/4] Fix for https://issues.apache.org/jira/browse/AMQ-3621 - Integrate Apache Shiro with ActiveMQ as "security solution" - many thanks to Les Hazlewood (lhazlewood) for the patch!

Posted by ce...@apache.org.
http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/java/org/apache/activemq/shiro/authc/DefaultAuthenticationPolicyTest.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/java/org/apache/activemq/shiro/authc/DefaultAuthenticationPolicyTest.java b/activemq-shiro/src/test/java/org/apache/activemq/shiro/authc/DefaultAuthenticationPolicyTest.java
new file mode 100644
index 0000000..0d39061
--- /dev/null
+++ b/activemq-shiro/src/test/java/org/apache/activemq/shiro/authc/DefaultAuthenticationPolicyTest.java
@@ -0,0 +1,339 @@
+/**
+ * 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.shiro.authc;
+
+import org.apache.activemq.broker.Connection;
+import org.apache.activemq.broker.ConnectionContext;
+import org.apache.activemq.broker.Connector;
+import org.apache.activemq.broker.region.ConnectionStatistics;
+import org.apache.activemq.command.Command;
+import org.apache.activemq.command.ConnectionControl;
+import org.apache.activemq.command.ConnectionInfo;
+import org.apache.activemq.command.Response;
+import org.apache.activemq.shiro.subject.SubjectAdapter;
+import org.apache.activemq.shiro.subject.SubjectConnectionReference;
+import org.apache.shiro.env.DefaultEnvironment;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.apache.shiro.subject.SimplePrincipalCollection;
+import org.apache.shiro.subject.Subject;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.junit.Assert.*;
+
+/**
+ * @since 5.10.0
+ */
+public class DefaultAuthenticationPolicyTest {
+
+    private DefaultAuthenticationPolicy policy;
+
+    @Before
+    public void setUp() {
+        this.policy = new DefaultAuthenticationPolicy();
+    }
+
+    @Test
+    public void testVmConnectionAuthenticationRequired() {
+        boolean required = true;
+        policy.setVmConnectionAuthenticationRequired(required);
+        assertEquals(required, policy.isVmConnectionAuthenticationRequired());
+    }
+
+    @Test
+    public void testSystemAccountUsername() {
+        String name = "foo";
+        policy.setSystemAccountUsername(name);
+        assertEquals(name, policy.getSystemAccountUsername());
+    }
+
+    @Test
+    public void testSystemAccountRealmName() {
+        String name = "fooRealm";
+        policy.setSystemAccountRealmName(name);
+        assertEquals(name, policy.getSystemAccountRealmName());
+    }
+
+    @Test
+    public void testAnonymousAllowed() {
+        boolean allowed = true;
+        policy.setAnonymousAccessAllowed(allowed);
+        assertEquals(allowed, policy.isAnonymousAccessAllowed());
+    }
+
+    @Test
+    public void testAnonymousAccountUsername() {
+        String name = "blah";
+        policy.setAnonymousAccountUsername(name);
+        assertEquals(name, policy.getAnonymousAccountUsername());
+    }
+
+    @Test
+    public void testAnonymousAccountRealmName() {
+        String name = "blahRealm";
+        policy.setAnonymousAccountRealmName(name);
+        assertEquals(name, policy.getAnonymousAccountRealmName());
+    }
+
+    @Test
+    public void testIsAnonymousAccount() {
+        Subject subject = new SubjectAdapter() {
+            @Override
+            public PrincipalCollection getPrincipals() {
+                return new SimplePrincipalCollection("anonymous", "iniRealm");
+            }
+        };
+        assertTrue(policy.isAnonymousAccount(subject));
+    }
+
+    @Test
+    public void testIsAnonymousAccountWithNullPrincipals() {
+        assertFalse(policy.isAnonymousAccount(new SubjectAdapter()));
+    }
+
+    @Test
+    public void testIsSystemAccountWithNullPrincipals() {
+        assertFalse(policy.isSystemAccount(new SubjectAdapter()));
+    }
+
+    @Test
+    public void testIsAuthenticationRequiredWhenAlreadyRequired() {
+        Subject subject = new SubjectAdapter() {
+            @Override
+            public boolean isAuthenticated() {
+                return true;
+            }
+        };
+        SubjectConnectionReference sc = new SubjectConnectionReference(new ConnectionContext(), new ConnectionInfo(),
+                new DefaultEnvironment(), subject);
+
+        assertFalse(policy.isAuthenticationRequired(sc));
+    }
+
+    @Test
+    public void testIsAuthenticationRequiredWhenAnonymousAllowedAnonymousSubject() {
+
+        policy.setAnonymousAccessAllowed(true);
+
+        Subject subject = new SubjectAdapter() {
+            @Override
+            public PrincipalCollection getPrincipals() {
+                return new SimplePrincipalCollection("anonymous", "iniRealm");
+            }
+        };
+        SubjectConnectionReference sc = new SubjectConnectionReference(new ConnectionContext(), new ConnectionInfo(),
+                new DefaultEnvironment(), subject);
+
+        assertFalse(policy.isAuthenticationRequired(sc));
+    }
+
+    @Test
+    public void testIsAuthenticationRequiredWhenAnonymousAllowedAndNotAnonymousSubject() {
+
+        policy.setAnonymousAccessAllowed(true);
+
+        Subject subject = new SubjectAdapter() {
+            @Override
+            public PrincipalCollection getPrincipals() {
+                return new SimplePrincipalCollection("system", "iniRealm");
+            }
+        };
+        SubjectConnectionReference sc = new SubjectConnectionReference(new ConnectionContext(), new ConnectionInfo(),
+                new DefaultEnvironment(), subject);
+
+        assertFalse(policy.isAuthenticationRequired(sc));
+    }
+
+    @Test
+    public void testIsAuthenticationRequiredWhenSystemConnectionAndSystemSubject() {
+
+        Subject subject = new SubjectAdapter() {
+            @Override
+            public PrincipalCollection getPrincipals() {
+                return new SimplePrincipalCollection("system", "iniRealm");
+            }
+        };
+        SubjectConnectionReference sc = new SubjectConnectionReference(new ConnectionContext(), new ConnectionInfo(),
+                new DefaultEnvironment(), subject);
+
+        assertFalse(policy.isAuthenticationRequired(sc));
+    }
+
+    @Test
+    public void testIsAuthenticationRequiredWhenSystemConnectionRequiresAuthentication() {
+
+        policy.setVmConnectionAuthenticationRequired(true);
+
+        Subject subject = new SubjectAdapter() {
+            @Override
+            public PrincipalCollection getPrincipals() {
+                return new SimplePrincipalCollection("system", "iniRealm");
+            }
+        };
+        SubjectConnectionReference sc = new SubjectConnectionReference(new ConnectionContext(), new ConnectionInfo(),
+                new DefaultEnvironment(), subject);
+
+        assertTrue(policy.isAuthenticationRequired(sc));
+    }
+
+    @Test
+    public void testIsAuthenticationRequiredWhenSystemConnectionDoesNotRequireAuthenticationAndNotSystemAccount() {
+
+        Subject subject = new SubjectAdapter() {
+            @Override
+            public PrincipalCollection getPrincipals() {
+                return new SimplePrincipalCollection("foo", "iniRealm");
+            }
+        };
+        SubjectConnectionReference sc = new SubjectConnectionReference(new ConnectionContext(), new ConnectionInfo(),
+                new DefaultEnvironment(), subject);
+
+        assertTrue(policy.isAuthenticationRequired(sc));
+    }
+
+    @Test
+    public void testIsAssumeIdentity() {
+        policy.setAnonymousAccessAllowed(true);
+        assertTrue(policy.isAssumeIdentity(null));
+    }
+
+    @Test
+    public void testIsAssumeIdentityWithSystemConnection() {
+
+        ConnectionContext ctx = new ConnectionContext();
+        Connection connection = new Connection() {
+            @Override
+            public Connector getConnector() {
+                return null;  //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public void dispatchSync(Command message) {
+                //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public void dispatchAsync(Command command) {
+                //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public Response service(Command command) {
+                return null;  //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public void serviceException(Throwable error) {
+                //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public boolean isSlow() {
+                return false;  //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public boolean isBlocked() {
+                return false;  //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public boolean isConnected() {
+                return false;  //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public boolean isActive() {
+                return false;  //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public int getDispatchQueueSize() {
+                return 0;  //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public ConnectionStatistics getStatistics() {
+                return null;  //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public boolean isManageable() {
+                return false;  //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public String getRemoteAddress() {
+                return "vm://localhost";
+            }
+
+            @Override
+            public void serviceExceptionAsync(IOException e) {
+                //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public String getConnectionId() {
+                return null;  //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public boolean isNetworkConnection() {
+                return false;  //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public boolean isFaultTolerantConnection() {
+                return false;  //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public void updateClient(ConnectionControl control) {
+                //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public void start() throws Exception {
+                //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public void stop() throws Exception {
+                //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public int getActiveTransactionCount() {
+                return 0;  //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public Long getOldestActiveTransactionDuration() {
+                return null;  //To change body of implemented methods use File | Settings | File Templates.
+            }
+        };
+
+        ctx.setConnection(connection);
+
+        SubjectConnectionReference sc = new SubjectConnectionReference(ctx, new ConnectionInfo(),
+                new DefaultEnvironment(), new SubjectAdapter());
+
+        assertTrue(policy.isAssumeIdentity(sc));
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/java/org/apache/activemq/shiro/authz/ActiveMQPermissionResolverTest.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/java/org/apache/activemq/shiro/authz/ActiveMQPermissionResolverTest.java b/activemq-shiro/src/test/java/org/apache/activemq/shiro/authz/ActiveMQPermissionResolverTest.java
new file mode 100644
index 0000000..363f60b
--- /dev/null
+++ b/activemq-shiro/src/test/java/org/apache/activemq/shiro/authz/ActiveMQPermissionResolverTest.java
@@ -0,0 +1,53 @@
+/**
+ * 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.shiro.authz;
+
+import org.apache.shiro.authz.Permission;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @since 5.10.0
+ */
+public class ActiveMQPermissionResolverTest {
+
+    @Test
+    public void testDefault() {
+        ActiveMQPermissionResolver resolver = new ActiveMQPermissionResolver();
+        assertTrue(resolver.isCaseSensitive());
+        Permission p = resolver.resolvePermission("Foo:Bar");
+        assertNotNull(p);
+        assertTrue(p instanceof ActiveMQWildcardPermission);
+        assertTrue(p.implies(new ActiveMQWildcardPermission("Foo:Bar")));
+        assertFalse(p.implies(new ActiveMQWildcardPermission("foo:bar")));
+    }
+
+    @Test
+    public void testCaseInsensitive() {
+        ActiveMQPermissionResolver resolver = new ActiveMQPermissionResolver();
+        resolver.setCaseSensitive(false);
+        assertFalse(resolver.isCaseSensitive());
+        Permission p = resolver.resolvePermission("Foo:Bar");
+        assertNotNull(p);
+        assertTrue(p instanceof ActiveMQWildcardPermission);
+        assertTrue(p.implies(new ActiveMQWildcardPermission("foo:bar")));
+        assertTrue(p.implies(new ActiveMQWildcardPermission("Foo:Bar", true)));
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/java/org/apache/activemq/shiro/authz/ActiveMQWildcardPermissionTest.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/java/org/apache/activemq/shiro/authz/ActiveMQWildcardPermissionTest.java b/activemq-shiro/src/test/java/org/apache/activemq/shiro/authz/ActiveMQWildcardPermissionTest.java
new file mode 100644
index 0000000..515aeeb
--- /dev/null
+++ b/activemq-shiro/src/test/java/org/apache/activemq/shiro/authz/ActiveMQWildcardPermissionTest.java
@@ -0,0 +1,158 @@
+/**
+ * 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.shiro.authz;
+
+import org.apache.shiro.authz.Permission;
+import org.apache.shiro.authz.permission.WildcardPermission;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.Assert.*;
+
+/**
+ * @since 5.10.0
+ */
+public class ActiveMQWildcardPermissionTest {
+
+    @Test
+    public void testNotWildcardPermission() {
+        ActiveMQWildcardPermission perm = new ActiveMQWildcardPermission("topic:TEST:*");
+        Permission dummy = new Permission() {
+            @Override
+            public boolean implies(Permission p) {
+                return false;
+            }
+        };
+       assertFalse(perm.implies(dummy));
+    }
+
+    @Test
+    public void testIntrapartWildcard() {
+        ActiveMQWildcardPermission superset = new ActiveMQWildcardPermission("topic:ActiveMQ.Advisory.*:read");
+        ActiveMQWildcardPermission subset = new ActiveMQWildcardPermission("topic:ActiveMQ.Advisory.Topic:read");
+
+        assertTrue(superset.implies(subset));
+        assertFalse(subset.implies(superset));
+    }
+
+    @Test
+    public void testMatches() {
+        assertMatch("x", "x");
+        assertNoMatch("x", "y");
+
+        assertMatch("xx", "xx");
+        assertNoMatch("xy", "xz");
+
+        assertMatch("?", "x");
+        assertMatch("x?", "xy");
+        assertMatch("?y", "xy");
+        assertMatch("x?z", "xyz");
+
+        assertMatch("*", "x");
+        assertMatch("x*", "x");
+        assertMatch("x*", "xy");
+        assertMatch("xy*", "xy");
+        assertMatch("xy*", "xyz");
+
+        assertMatch("*x", "x");
+        assertNoMatch("*x", "y");
+
+        assertMatch("*x", "wx");
+        assertNoMatch("*x", "wz");
+        assertMatch("*x", "vwx");
+
+        assertMatch("x*z", "xz");
+        assertMatch("x*z", "xyz");
+        assertMatch("x*z", "xyyz");
+
+        assertNoMatch("ab*t?z", "abz");
+        assertNoMatch("ab*d*yz", "abcdz");
+
+        assertMatch("ab**cd**ef*yz", "abcdefyz");
+        assertMatch("a*c?*z", "abcxyz");
+        assertMatch("a*cd*z", "abcdxyz");
+
+        assertMatch("*", "x:x");
+        assertMatch("*", "x:x:x");
+        assertMatch("x", "x:y");
+        assertMatch("x", "x:y:z");
+
+        assertMatch("foo?armat*", "foobarmatches");
+        assertMatch("f*", "f");
+        assertNoMatch("foo", "f");
+        assertMatch("fo*b", "foob");
+        assertNoMatch("fo*b*r", "fooba");
+        assertNoMatch("foo*", "f");
+
+        assertMatch("t*k?ou", "thankyou");
+        assertMatch("he*l*world", "helloworld");
+        assertNoMatch("foo", "foob");
+
+        assertMatch("*:ActiveMQ.Advisory", "foo:ActiveMQ.Advisory");
+        assertNoMatch("*:ActiveMQ.Advisory", "foo:ActiveMQ.Advisory.");
+        assertMatch("*:ActiveMQ.Advisory*", "foo:ActiveMQ.Advisory");
+        assertMatch("*:ActiveMQ.Advisory*", "foo:ActiveMQ.Advisory.");
+        assertMatch("*:ActiveMQ.Advisory.*", "foo:ActiveMQ.Advisory.Connection");
+        assertMatch("*:ActiveMQ.Advisory*:read", "foo:ActiveMQ.Advisory.Connection:read");
+        assertNoMatch("*:ActiveMQ.Advisory*:read", "foo:ActiveMQ.Advisory.Connection:write");
+        assertMatch("*:ActiveMQ.Advisory*:*", "foo:ActiveMQ.Advisory.Connection:read");
+        assertMatch("*:ActiveMQ.Advisory*:*", "foo:ActiveMQ.Advisory.");
+        assertMatch("topic", "topic:TEST:*");
+        assertNoMatch("*:ActiveMQ*", "topic:TEST:*");
+        assertMatch("topic:ActiveMQ.Advisory*", "topic:ActiveMQ.Advisory.Connection:create");
+        assertMatch("foo?ar", "foobar");
+    }
+
+    protected static void assertMatch(String pattern, String value) {
+        assertTrue(matches(pattern, value));
+    }
+
+    protected static void assertNoMatch(String pattern, String value) {
+        assertFalse(matches(pattern, value));
+    }
+
+    protected static boolean matches(String pattern, String value) {
+        ActiveMQWildcardPermission patternPerm = new ActiveMQWildcardPermission(pattern);
+        WildcardPermission valuePerm = new WildcardPermission(value, true);
+        return patternPerm.implies(valuePerm);
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void testGetPartsByReflectionThrowingException() {
+
+        ActiveMQWildcardPermission perm = new ActiveMQWildcardPermission("foo:bar") {
+            @Override
+            protected List<Set<String>> doGetPartsByReflection(WildcardPermission wp) throws Exception {
+                throw new RuntimeException("Testing failure");
+            }
+        };
+
+        WildcardPermission otherPerm = new WildcardPermission("foo:bar:baz");
+
+        perm.implies(otherPerm);
+    }
+
+    @Test
+    public void testImpliesWithExtraParts() {
+        ActiveMQWildcardPermission perm1 = new ActiveMQWildcardPermission("foo:bar:baz");
+        ActiveMQWildcardPermission perm2 = new ActiveMQWildcardPermission("foo:bar");
+        assertFalse(perm1.implies(perm2));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/java/org/apache/activemq/shiro/authz/AuthorizationFilterTest.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/java/org/apache/activemq/shiro/authz/AuthorizationFilterTest.java b/activemq-shiro/src/test/java/org/apache/activemq/shiro/authz/AuthorizationFilterTest.java
new file mode 100644
index 0000000..bc63f30
--- /dev/null
+++ b/activemq-shiro/src/test/java/org/apache/activemq/shiro/authz/AuthorizationFilterTest.java
@@ -0,0 +1,364 @@
+/**
+ * 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.shiro.authz;
+
+import org.apache.activemq.broker.ConnectionContext;
+import org.apache.activemq.broker.ProducerBrokerExchange;
+import org.apache.activemq.broker.StubBroker;
+import org.apache.activemq.command.ActiveMQDestination;
+import org.apache.activemq.command.ActiveMQTextMessage;
+import org.apache.activemq.command.ActiveMQTopic;
+import org.apache.activemq.command.ConnectionInfo;
+import org.apache.activemq.command.ConsumerInfo;
+import org.apache.activemq.command.DestinationInfo;
+import org.apache.activemq.command.ProducerInfo;
+import org.apache.activemq.shiro.subject.SubjectAdapter;
+import org.apache.activemq.shiro.subject.SubjectConnectionReference;
+import org.apache.activemq.shiro.subject.SubjectSecurityContext;
+import org.apache.shiro.authz.Permission;
+import org.apache.shiro.authz.UnauthorizedException;
+import org.apache.shiro.env.Environment;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.apache.shiro.subject.SimplePrincipalCollection;
+import org.apache.shiro.subject.Subject;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Collection;
+
+import static org.junit.Assert.*;
+
+/**
+ * @since 5.10.0
+ */
+public class AuthorizationFilterTest {
+
+    private AuthorizationFilter filter;
+    private StubBroker nextBroker;
+
+    @Before
+    public void setUp() {
+        filter = new AuthorizationFilter();
+        nextBroker = new StubBroker();
+        filter.setNext(nextBroker);
+    }
+
+    @Test
+    public void testDefaults() {
+        ActionPermissionResolver resolver = filter.getActionPermissionResolver();
+        assertNotNull(resolver);
+        assertTrue(resolver instanceof DestinationActionPermissionResolver);
+    }
+
+    @Test
+    public void testSetActionPermissionResolver() {
+        ActionPermissionResolver resolver = new DestinationActionPermissionResolver();
+        filter.setActionPermissionResolver(resolver);
+        assertSame(resolver, filter.getActionPermissionResolver());
+    }
+
+    private ConnectionContext createContext(Subject subject) {
+        ConnectionContext ctx = new ConnectionContext();
+        ConnectionInfo info = new ConnectionInfo();
+        Environment environment = new Environment() {
+            @Override
+            public org.apache.shiro.mgt.SecurityManager getSecurityManager() {
+                return null; //not needed in this test.
+            }
+        };
+        SubjectConnectionReference ref = new SubjectConnectionReference(ctx, info, environment, subject);
+        SubjectSecurityContext secCtx = new SubjectSecurityContext(ref);
+        ctx.setSecurityContext(secCtx);
+        return ctx;
+    }
+
+    @Test
+    public void testSubjectToString() {
+        Subject subject = new PermsSubject() {
+            @Override
+            public PrincipalCollection getPrincipals() {
+                return null;
+            }
+        };
+        String string = filter.toString(subject);
+        assertEquals("", string);
+    }
+
+    @Test(expected=UnauthorizedException.class)
+    public void testAddDestinationInfoNotAuthorized() throws Exception {
+        String name = "myTopic";
+        ActiveMQDestination dest = new ActiveMQTopic(name);
+        DestinationInfo info = new DestinationInfo(null, DestinationInfo.ADD_OPERATION_TYPE, dest);
+
+        Subject subject = new PermsSubject();
+        ConnectionContext context = createContext(subject);
+
+        filter.addDestinationInfo(context, info);
+    }
+
+    @Test
+    public void testAddDestinationInfoAuthorized() throws Exception {
+
+        String name = "myTopic";
+        ActiveMQDestination dest = new ActiveMQTopic(name);
+        DestinationInfo info = new DestinationInfo(null, DestinationInfo.ADD_OPERATION_TYPE, dest);
+
+        Subject subject = new PermsSubject() {
+            @Override
+            public boolean isPermitted(Permission toCheck) {
+                Permission assigned = createPerm("topic:myTopic:create");
+                assertEquals(assigned.toString(), toCheck.toString());
+                return assigned.implies(toCheck);
+            }
+        };
+
+        ConnectionContext context = createContext(subject);
+
+        filter.addDestinationInfo(context, info);
+    }
+
+    @Test(expected=UnauthorizedException.class)
+    public void testAddDestinationNotAuthorized() throws Exception {
+        String name = "myTopic";
+        ActiveMQDestination dest = new ActiveMQTopic(name);
+
+        Subject subject = new PermsSubject();
+        ConnectionContext context = createContext(subject);
+
+        filter.addDestination(context, dest, true);
+    }
+
+    @Test
+    public void testAddDestinationAuthorized() throws Exception {
+        String name = "myTopic";
+        ActiveMQDestination dest = new ActiveMQTopic(name);
+
+        Subject subject = new PermsSubject() {
+            @Override
+            public boolean isPermitted(Permission toCheck) {
+                Permission assigned = createPerm("topic:myTopic:create");
+                assertEquals(assigned.toString(), toCheck.toString());
+                return assigned.implies(toCheck);
+            }
+        };
+
+        ConnectionContext context = createContext(subject);
+
+        filter.addDestination(context, dest, true);
+    }
+
+    @Test(expected=UnauthorizedException.class)
+    public void testRemoveDestinationInfoNotAuthorized() throws Exception {
+        String name = "myTopic";
+        ActiveMQDestination dest = new ActiveMQTopic(name);
+        DestinationInfo info = new DestinationInfo(null, DestinationInfo.REMOVE_OPERATION_TYPE, dest);
+
+        Subject subject = new PermsSubject();
+        ConnectionContext context = createContext(subject);
+
+        filter.removeDestinationInfo(context, info);
+    }
+
+    @Test
+    public void testRemoveDestinationInfoAuthorized() throws Exception {
+
+        String name = "myTopic";
+        ActiveMQDestination dest = new ActiveMQTopic(name);
+        DestinationInfo info = new DestinationInfo(null, DestinationInfo.REMOVE_OPERATION_TYPE, dest);
+
+        Subject subject = new PermsSubject() {
+            @Override
+            public boolean isPermitted(Permission toCheck) {
+                Permission assigned = createPerm("topic:myTopic:remove");
+                assertEquals(assigned.toString(), toCheck.toString());
+                return assigned.implies(toCheck);
+            }
+        };
+
+        ConnectionContext context = createContext(subject);
+
+        filter.removeDestinationInfo(context, info);
+    }
+
+    @Test(expected=UnauthorizedException.class)
+    public void testRemoveDestinationNotAuthorized() throws Exception {
+        String name = "myTopic";
+        ActiveMQDestination dest = new ActiveMQTopic(name);
+
+        Subject subject = new PermsSubject();
+        ConnectionContext context = createContext(subject);
+
+        filter.removeDestination(context, dest, 1000);
+    }
+
+    @Test
+    public void testRemoveDestinationAuthorized() throws Exception {
+        String name = "myTopic";
+        ActiveMQDestination dest = new ActiveMQTopic(name);
+
+        Subject subject = new PermsSubject() {
+            @Override
+            public boolean isPermitted(Permission toCheck) {
+                Permission assigned = createPerm("topic:myTopic:remove");
+                assertEquals(assigned.toString(), toCheck.toString());
+                return assigned.implies(toCheck);
+            }
+        };
+
+        ConnectionContext context = createContext(subject);
+
+        filter.removeDestination(context, dest, 1000);
+    }
+
+    @Test(expected=UnauthorizedException.class)
+    public void testAddConsumerNotAuthorized() throws Exception {
+        String name = "myTopic";
+        ActiveMQDestination dest = new ActiveMQTopic(name);
+
+        Subject subject = new PermsSubject();
+        ConnectionContext context = createContext(subject);
+        ConsumerInfo info = new ConsumerInfo(null);
+        info.setDestination(dest);
+
+        filter.addConsumer(context, info);
+    }
+
+    @Test
+    public void testAddConsumerAuthorized() throws Exception {
+        String name = "myTopic";
+        ActiveMQDestination dest = new ActiveMQTopic(name);
+
+        Subject subject = new PermsSubject() {
+            @Override
+            public boolean isPermitted(Permission toCheck) {
+                Permission assigned = createPerm("topic:myTopic:read");
+                assertEquals(assigned.toString(), toCheck.toString());
+                return assigned.implies(toCheck);
+            }
+        };
+
+        ConnectionContext context = createContext(subject);
+        ConsumerInfo info = new ConsumerInfo(null);
+        info.setDestination(dest);
+
+        filter.addConsumer(context, info);
+    }
+
+    @Test
+    public void testAddProducerWithoutDestination() throws Exception {
+        Subject subject = new PermsSubject();
+        ConnectionContext context = createContext(subject);
+        ProducerInfo info = new ProducerInfo(null);
+        filter.addProducer(context, info);
+    }
+
+    @Test(expected=UnauthorizedException.class)
+    public void testAddProducerNotAuthorized() throws Exception {
+        String name = "myTopic";
+        ActiveMQDestination dest = new ActiveMQTopic(name);
+
+        Subject subject = new PermsSubject();
+        ConnectionContext context = createContext(subject);
+        ProducerInfo info = new ProducerInfo(null);
+        info.setDestination(dest);
+
+        filter.addProducer(context, info);
+    }
+
+    @Test
+    public void testAddProducerAuthorized() throws Exception {
+        String name = "myTopic";
+        ActiveMQDestination dest = new ActiveMQTopic(name);
+
+        Subject subject = new PermsSubject() {
+            @Override
+            public boolean isPermitted(Permission toCheck) {
+                Permission assigned = createPerm("topic:myTopic:write");
+                assertEquals(assigned.toString(), toCheck.toString());
+                return assigned.implies(toCheck);
+            }
+        };
+
+        ConnectionContext context = createContext(subject);
+        ProducerInfo info = new ProducerInfo(null);
+        info.setDestination(dest);
+
+        filter.addProducer(context, info);
+    }
+
+    @Test(expected=UnauthorizedException.class)
+    public void testBrokerExchangeSendNotAuthorized() throws Exception {
+        String name = "myTopic";
+
+        ActiveMQDestination dest = new ActiveMQTopic(name);
+        ActiveMQTextMessage message = new ActiveMQTextMessage();
+        message.setDestination(dest);
+        message.setText("Hello, world!");
+
+        Subject subject = new PermsSubject();
+        ConnectionContext context = createContext(subject);
+        ProducerBrokerExchange exchange = new ProducerBrokerExchange();
+        exchange.setConnectionContext(context);
+
+        filter.send(exchange, message);
+    }
+
+    @Test
+    public void testBrokerExchangeSendAuthorized() throws Exception {
+        String name = "myTopic";
+        ActiveMQDestination dest = new ActiveMQTopic(name);
+        ActiveMQTextMessage message = new ActiveMQTextMessage();
+        message.setDestination(dest);
+        message.setText("Hello, world!");
+
+        Subject subject = new PermsSubject() {
+            @Override
+            public boolean isPermitted(Permission toCheck) {
+                Permission assigned = createPerm("topic:myTopic:write");
+                assertEquals(assigned.toString(), toCheck.toString());
+                return assigned.implies(toCheck);
+            }
+        };
+
+        ConnectionContext context = createContext(subject);
+        ProducerBrokerExchange exchange = new ProducerBrokerExchange();
+        exchange.setConnectionContext(context);
+
+        filter.send(exchange, message);
+    }
+
+
+    protected Permission createPerm(String perm) {
+        return new DestinationActionPermissionResolver().createPermission(perm);
+    }
+
+
+    private static class PermsSubject extends SubjectAdapter {
+
+        @Override
+        public PrincipalCollection getPrincipals() {
+            return new SimplePrincipalCollection("foo", "someRealm");
+        }
+
+        @Override
+        public boolean isPermittedAll(Collection<Permission> permissions) {
+            assertNotNull(permissions);
+            assertEquals(1, permissions.size());
+            return isPermitted(permissions.iterator().next());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/java/org/apache/activemq/shiro/authz/DestinationActionPermissionResolverTest.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/java/org/apache/activemq/shiro/authz/DestinationActionPermissionResolverTest.java b/activemq-shiro/src/test/java/org/apache/activemq/shiro/authz/DestinationActionPermissionResolverTest.java
new file mode 100644
index 0000000..329979f
--- /dev/null
+++ b/activemq-shiro/src/test/java/org/apache/activemq/shiro/authz/DestinationActionPermissionResolverTest.java
@@ -0,0 +1,153 @@
+/**
+ * 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.shiro.authz;
+
+import org.apache.activemq.broker.ConnectionContext;
+import org.apache.activemq.command.ActiveMQDestination;
+import org.apache.activemq.command.ActiveMQQueue;
+import org.apache.activemq.command.ActiveMQTempQueue;
+import org.apache.activemq.command.ActiveMQTempTopic;
+import org.apache.activemq.command.ActiveMQTopic;
+import org.apache.activemq.filter.AnyDestination;
+import org.apache.shiro.authz.Permission;
+import org.apache.shiro.authz.permission.WildcardPermission;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Collection;
+
+import static org.junit.Assert.*;
+
+/**
+ * @since 5.10.0
+ */
+public class DestinationActionPermissionResolverTest {
+
+    private DestinationActionPermissionResolver resolver;
+
+    @Before
+    public void setUp() {
+        this.resolver = new DestinationActionPermissionResolver();
+    }
+
+    @Test
+    public void testDefaults() {
+        assertNull(resolver.getPermissionStringPrefix());
+        //default is true to reflect ActiveMQ's case-sensitive destination names:
+        assertTrue(resolver.isPermissionStringCaseSensitive());
+    }
+
+    @Test
+    public void testPermissionStringPrefixProp() {
+        String prefix = "foo";
+        resolver.setPermissionStringPrefix(prefix);
+        assertEquals(prefix, resolver.getPermissionStringPrefix());
+    }
+
+    @Test
+    public void testCaseSensitiveProp() {
+        resolver.setPermissionStringCaseSensitive(true);
+        assertTrue(resolver.isPermissionStringCaseSensitive());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetPermissionsWithNonDestinationActionInstance() {
+        resolver.getPermissions(new Action() {
+            @Override
+            public String toString() {
+                return "foo";
+            }
+        });
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetPermissionsWithNullArgument() {
+        resolver.getPermissions((Action)null);
+    }
+
+    void assertPermString(String perm, Collection<Permission> perms) {
+        assertEquals(1, perms.size());
+        assertEquals(perm, perms.iterator().next().toString());
+    }
+
+    @Test
+    public void testGetPermissionsWithTopic() {
+        ActiveMQTopic topic = new ActiveMQTopic("myTopic");
+        DestinationAction action = new DestinationAction(new ConnectionContext(), topic, "create");
+        Collection<Permission> perms = resolver.getPermissions(action);
+        assertPermString("topic:myTopic:create", perms);
+    }
+
+    @Test
+    public void testGetPermissionsWithTemporaryTopic() {
+        ActiveMQTempTopic topic = new ActiveMQTempTopic("myTempTopic");
+        DestinationAction action = new DestinationAction(new ConnectionContext(), topic, "remove");
+        Collection<Permission> perms = resolver.getPermissions(action);
+        assertPermString("temp-topic:myTempTopic:remove", perms);
+    }
+
+    @Test
+    public void testGetPermissionsWithQueue() {
+        ActiveMQQueue queue = new ActiveMQQueue("myQueue");
+        DestinationAction action = new DestinationAction(new ConnectionContext(), queue, "write");
+        Collection<Permission> perms = resolver.getPermissions(action);
+        assertPermString("queue:myQueue:write", perms);
+    }
+
+    @Test
+    public void testGetPermissionsWithTemporaryQueue() {
+        ActiveMQTempQueue queue = new ActiveMQTempQueue("myTempQueue");
+        DestinationAction action = new DestinationAction(new ConnectionContext(), queue, "read");
+        Collection<Permission> perms = resolver.getPermissions(action);
+        assertPermString("temp-queue:myTempQueue:read", perms);
+    }
+
+    @Test
+    public void testPermissionWithPrefix() {
+        resolver.setPermissionStringPrefix("activeMQ");
+        ActiveMQTopic topic = new ActiveMQTopic("myTopic");
+        DestinationAction action = new DestinationAction(new ConnectionContext(), topic, "create");
+        Collection<Permission> perms = resolver.getPermissions(action);
+        assertPermString("activeMQ:topic:myTopic:create", perms);
+    }
+
+    //Ensures if they explicitly set a prefix with a colon suffix that we don't add another one
+    @Test
+    public void testPermissionWithPrefixAndExplicitColon() {
+        resolver.setPermissionStringPrefix("activeMQ:");
+        ActiveMQTopic topic = new ActiveMQTopic("myTopic");
+        DestinationAction action = new DestinationAction(new ConnectionContext(), topic, "create");
+        Collection<Permission> perms = resolver.getPermissions(action);
+        assertPermString("activeMQ:topic:myTopic:create", perms);
+    }
+
+    @Test
+    public void testAlternateWildcardPermissionToStringWithMultipleActions() {
+        Permission perm = resolver.createPermission("foo:bar:action1,action2");
+        assertTrue(perm instanceof WildcardPermission);
+        assertEquals("foo:bar:action1,action2", perm.toString());
+
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreatePermissionStringWithCompositeDestination() {
+        ActiveMQTopic topicA = new ActiveMQTopic("A");
+        ActiveMQTopic topicB = new ActiveMQTopic("B");
+        ActiveMQDestination composite = new AnyDestination(new ActiveMQDestination[]{topicA, topicB});
+        resolver.createPermissionString(composite, "read");
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/java/org/apache/activemq/shiro/authz/DestinationActionTest.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/java/org/apache/activemq/shiro/authz/DestinationActionTest.java b/activemq-shiro/src/test/java/org/apache/activemq/shiro/authz/DestinationActionTest.java
new file mode 100644
index 0000000..a5a9158
--- /dev/null
+++ b/activemq-shiro/src/test/java/org/apache/activemq/shiro/authz/DestinationActionTest.java
@@ -0,0 +1,58 @@
+/**
+ * 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.shiro.authz;
+
+import org.apache.activemq.broker.ConnectionContext;
+import org.apache.activemq.command.ActiveMQQueue;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @since 5.10.0
+ */
+public class DestinationActionTest {
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNullConnectionContext() {
+        new DestinationAction(null, new ActiveMQQueue("foo"), "create");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNullDestination() {
+        new DestinationAction(new ConnectionContext(), null, "create");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNullVerb() {
+        new DestinationAction(new ConnectionContext(), new ActiveMQQueue("foo"), null);
+    }
+
+    @Test
+    public void testDefault() {
+        ConnectionContext ctx = new ConnectionContext();
+        ActiveMQQueue queue = new ActiveMQQueue("foo");
+        String verb = "create";
+
+        DestinationAction action = new DestinationAction(ctx, queue, verb);
+        assertSame(ctx, action.getConnectionContext());
+        assertSame(queue, action.getDestination());
+        assertEquals(verb, action.getVerb());
+        assertEquals("create destination: queue://foo", action.toString());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/java/org/apache/activemq/shiro/env/EnvironmentFilterTest.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/java/org/apache/activemq/shiro/env/EnvironmentFilterTest.java b/activemq-shiro/src/test/java/org/apache/activemq/shiro/env/EnvironmentFilterTest.java
new file mode 100644
index 0000000..b95c9fb
--- /dev/null
+++ b/activemq-shiro/src/test/java/org/apache/activemq/shiro/env/EnvironmentFilterTest.java
@@ -0,0 +1,30 @@
+/**
+ * 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.shiro.env;
+
+import org.junit.Test;
+
+/**
+ * @since 5.10.0
+ */
+public class EnvironmentFilterTest {
+
+    @Test(expected=IllegalStateException.class)
+    public void testNullEnvironment() {
+        new EnvironmentFilter(){}.getEnvironment();
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/java/org/apache/activemq/shiro/env/IniEnvironmentTest.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/java/org/apache/activemq/shiro/env/IniEnvironmentTest.java b/activemq-shiro/src/test/java/org/apache/activemq/shiro/env/IniEnvironmentTest.java
new file mode 100644
index 0000000..0d7d654
--- /dev/null
+++ b/activemq-shiro/src/test/java/org/apache/activemq/shiro/env/IniEnvironmentTest.java
@@ -0,0 +1,121 @@
+/**
+ * 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.shiro.env;
+
+import org.apache.shiro.authc.UsernamePasswordToken;
+import org.apache.shiro.config.ConfigurationException;
+import org.apache.shiro.config.Ini;
+import org.apache.shiro.subject.Subject;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @since 5.10.0
+ */
+public class IniEnvironmentTest {
+
+    IniEnvironment env;
+
+    @Before
+    public void setUp() {
+        env = new IniEnvironment();
+    }
+
+    protected void authenticate() {
+        authenticate("foo", "bar");
+    }
+
+    protected void authenticate(String username, String password) {
+        Subject subject = new Subject.Builder(env.getSecurityManager()).buildSubject();
+        subject.login(new UsernamePasswordToken(username, password));
+    }
+
+    @Test
+    public void testIniInstanceConstructorArg() {
+        Ini ini = new Ini();
+        ini.addSection("users").put("foo", "bar");
+        env = new IniEnvironment(ini);
+        authenticate();
+    }
+
+    @Test
+    public void testStringConstructorArg() {
+        String config =
+                "[users]\n" +
+                "foo = bar";
+
+        env = new IniEnvironment(config);
+        authenticate();
+    }
+
+    @Test
+    public void testSetIni() {
+        Ini ini = new Ini();
+        ini.addSection("users").put("foo", "bar");
+
+        env = new IniEnvironment();
+        env.setIni(ini);
+        env.init();
+
+        authenticate();
+    }
+
+    @Test
+    public void testSetIniString() {
+        String config =
+                "[users]\n" +
+                "foo = bar";
+
+        env = new IniEnvironment();
+        env.setIniConfig(config);
+        env.init();
+
+        authenticate();
+    }
+
+    @Test
+    public void testSetIniResourcePath() {
+        env = new IniEnvironment();
+        env.setIniResourcePath("classpath:minimal.shiro.ini");
+        env.init();
+
+        authenticate("system", "manager");
+    }
+
+    @Test
+    public void testDefaultClasspathIni() {
+        env = new IniEnvironment();
+        env.init();
+
+        authenticate("system", "manager");
+    }
+
+    @Test(expected = ConfigurationException.class)
+    public void testNoDefaultClasspathIni() {
+        env = new IniEnvironment() {
+            @Override
+            protected void apply(Ini ini) {
+                super.apply(ini);
+                //clear out the objects to simulate as if the ini file wasn't found:
+                this.objects.clear();
+            }
+        };
+        env.init();
+        authenticate("system", "manager");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/java/org/apache/activemq/shiro/session/mgt/DisabledSessionManagerTest.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/java/org/apache/activemq/shiro/session/mgt/DisabledSessionManagerTest.java b/activemq-shiro/src/test/java/org/apache/activemq/shiro/session/mgt/DisabledSessionManagerTest.java
new file mode 100644
index 0000000..df02ee0
--- /dev/null
+++ b/activemq-shiro/src/test/java/org/apache/activemq/shiro/session/mgt/DisabledSessionManagerTest.java
@@ -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.
+ */
+package org.apache.activemq.shiro.session.mgt;
+
+import org.apache.shiro.session.mgt.DefaultSessionKey;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @since 5.10.0
+ */
+public class DisabledSessionManagerTest {
+
+    private DisabledSessionManager mgr;
+
+    @Before
+    public void setUp() {
+        this.mgr = new DisabledSessionManager();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testStart() {
+        mgr.start(null);
+    }
+
+    @Test
+    public void testGetSession() {
+        assertNull(mgr.getSession(null));
+        assertNull(mgr.getSession(new DefaultSessionKey("foo")));
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/ConnectionSubjectResolverTest.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/ConnectionSubjectResolverTest.java b/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/ConnectionSubjectResolverTest.java
new file mode 100644
index 0000000..1404b71
--- /dev/null
+++ b/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/ConnectionSubjectResolverTest.java
@@ -0,0 +1,80 @@
+/**
+ * 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.shiro.subject;
+
+import org.apache.activemq.broker.ConnectionContext;
+import org.apache.activemq.command.ConnectionInfo;
+import org.apache.activemq.security.SecurityContext;
+import org.apache.shiro.env.DefaultEnvironment;
+import org.apache.shiro.subject.Subject;
+import org.junit.Test;
+
+import java.security.Principal;
+import java.util.Set;
+
+/**
+ * @since 5.10.0
+ */
+public class ConnectionSubjectResolverTest {
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNullConstructorArg() {
+        new ConnectionSubjectResolver((ConnectionContext)null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNullSecurityContext() {
+        SubjectConnectionReference reference =
+                new SubjectConnectionReference(new ConnectionContext(), new ConnectionInfo(),
+                        new DefaultEnvironment(), new SubjectAdapter());
+
+        new ConnectionSubjectResolver(reference);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNonSubjectSecurityContext() {
+        SubjectConnectionReference reference =
+                new SubjectConnectionReference(new ConnectionContext(), new ConnectionInfo(),
+                        new DefaultEnvironment(), new SubjectAdapter());
+        reference.getConnectionContext().setSecurityContext(new SecurityContext("") {
+            @Override
+            public Set<Principal> getPrincipals() {
+                return null;
+            }
+        });
+
+        new ConnectionSubjectResolver(reference);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testNullSubject() {
+
+        SubjectConnectionReference reference =
+                new SubjectConnectionReference(new ConnectionContext(), new ConnectionInfo(),
+                        new DefaultEnvironment(), new SubjectAdapter());
+        reference.getConnectionContext().setSecurityContext(new SubjectSecurityContext(reference) {
+            @Override
+            public Subject getSubject() {
+                return null;
+            }
+        });
+
+        ConnectionSubjectResolver resolver = new ConnectionSubjectResolver(reference);
+        resolver.getSubject();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/DefaultConnectionSubjectFactoryTest.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/DefaultConnectionSubjectFactoryTest.java b/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/DefaultConnectionSubjectFactoryTest.java
new file mode 100644
index 0000000..1774f50
--- /dev/null
+++ b/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/DefaultConnectionSubjectFactoryTest.java
@@ -0,0 +1,55 @@
+/**
+ * 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.shiro.subject;
+
+import org.apache.activemq.shiro.ConnectionReference;
+import org.apache.activemq.shiro.authc.AuthenticationPolicy;
+import org.apache.shiro.subject.Subject;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @since 5.10.0
+ */
+public class DefaultConnectionSubjectFactoryTest {
+
+    private DefaultConnectionSubjectFactory factory;
+
+    @Before
+    public void setUp() {
+        this.factory = new DefaultConnectionSubjectFactory();
+    }
+
+    @Test
+    public void testSetAuthenticationPolicy() {
+        AuthenticationPolicy policy = new AuthenticationPolicy() {
+            @Override
+            public void customizeSubject(Subject.Builder subjectBuilder, ConnectionReference ref) {
+                //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public boolean isAuthenticationRequired(SubjectConnectionReference ref) {
+                return false;  //To change body of implemented methods use File | Settings | File Templates.
+            }
+        };
+        factory.setAuthenticationPolicy(policy);
+        assertSame(policy, factory.getAuthenticationPolicy());
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/SubjectAdapter.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/SubjectAdapter.java b/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/SubjectAdapter.java
new file mode 100644
index 0000000..58f25e5
--- /dev/null
+++ b/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/SubjectAdapter.java
@@ -0,0 +1,185 @@
+/**
+ * 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.shiro.subject;
+
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authz.AuthorizationException;
+import org.apache.shiro.authz.Permission;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.subject.ExecutionException;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.apache.shiro.subject.Subject;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+/**
+ * @since 5.10.0
+ */
+public class SubjectAdapter implements Subject {
+
+    @Override
+    public Object getPrincipal() {
+        return null;
+    }
+
+    @Override
+    public PrincipalCollection getPrincipals() {
+        return null;
+    }
+
+    @Override
+    public boolean isPermitted(String permission) {
+        return false;
+    }
+
+    @Override
+    public boolean isPermitted(Permission permission) {
+        return false;
+    }
+
+    @Override
+    public boolean[] isPermitted(String... permissions) {
+        return new boolean[0];
+    }
+
+    @Override
+    public boolean[] isPermitted(List<Permission> permissions) {
+        return new boolean[0];
+    }
+
+    @Override
+    public boolean isPermittedAll(String... permissions) {
+        return false;
+    }
+
+    @Override
+    public boolean isPermittedAll(Collection<Permission> permissions) {
+        return false;
+    }
+
+    @Override
+    public void checkPermission(String permission) throws AuthorizationException {
+    }
+
+    @Override
+    public void checkPermission(Permission permission) throws AuthorizationException {
+    }
+
+    @Override
+    public void checkPermissions(String... permissions) throws AuthorizationException {
+    }
+
+    @Override
+    public void checkPermissions(Collection<Permission> permissions) throws AuthorizationException {
+    }
+
+    @Override
+    public boolean hasRole(String roleIdentifier) {
+        return false;
+    }
+
+    @Override
+    public boolean[] hasRoles(List<String> roleIdentifiers) {
+        return new boolean[0];  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public boolean hasAllRoles(Collection<String> roleIdentifiers) {
+        return false;
+    }
+
+    @Override
+    public void checkRole(String roleIdentifier) throws AuthorizationException {
+    }
+
+    @Override
+    public void checkRoles(Collection<String> roleIdentifiers) throws AuthorizationException {
+    }
+
+    @Override
+    public void checkRoles(String... roleIdentifiers) throws AuthorizationException {
+    }
+
+    @Override
+    public void login(AuthenticationToken token) throws AuthenticationException {
+    }
+
+    @Override
+    public boolean isAuthenticated() {
+        return false;
+    }
+
+    @Override
+    public boolean isRemembered() {
+        return false;
+    }
+
+    @Override
+    public Session getSession() {
+        return null;
+    }
+
+    @Override
+    public Session getSession(boolean create) {
+        return null;
+    }
+
+    @Override
+    public void logout() {
+    }
+
+    @Override
+    public <V> V execute(Callable<V> callable) throws ExecutionException {
+        return null;
+    }
+
+    @Override
+    public void execute(Runnable runnable) {
+    }
+
+    @Override
+    public <V> Callable<V> associateWith(Callable<V> callable) {
+        return null;
+    }
+
+    @Override
+    public Runnable associateWith(Runnable runnable) {
+        return runnable;
+    }
+
+    @Override
+    public void runAs(PrincipalCollection principals) throws NullPointerException, IllegalStateException {
+    }
+
+    @Override
+    public boolean isRunAs() {
+        return false;
+    }
+
+    @Override
+    public PrincipalCollection getPreviousPrincipals() {
+        return null;
+    }
+
+    @Override
+    public PrincipalCollection releaseRunAs() {
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/SubjectConnectionReferenceTest.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/SubjectConnectionReferenceTest.java b/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/SubjectConnectionReferenceTest.java
new file mode 100644
index 0000000..82b187b
--- /dev/null
+++ b/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/SubjectConnectionReferenceTest.java
@@ -0,0 +1,33 @@
+/**
+ * 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.shiro.subject;
+
+import org.apache.activemq.broker.ConnectionContext;
+import org.apache.activemq.command.ConnectionInfo;
+import org.apache.shiro.env.DefaultEnvironment;
+import org.junit.Test;
+
+/**
+ * @since 5.10.0
+ */
+public class SubjectConnectionReferenceTest {
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testNullSubject() {
+        new SubjectConnectionReference(new ConnectionContext(), new ConnectionInfo(), new DefaultEnvironment(), null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/SubjectFilterTest.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/SubjectFilterTest.java b/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/SubjectFilterTest.java
new file mode 100644
index 0000000..e2cbec7
--- /dev/null
+++ b/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/SubjectFilterTest.java
@@ -0,0 +1,59 @@
+/**
+ * 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.shiro.subject;
+
+import org.apache.activemq.security.SecurityContext;
+import org.apache.activemq.shiro.SecurityContextFactory;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @since 5.10.0
+ */
+public class SubjectFilterTest {
+
+    private SubjectFilter filter;
+
+    @Before
+    public void setUp() {
+        filter = new SubjectFilter();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void setNullSubjectConnectionFactory() {
+        filter.setConnectionSubjectFactory(null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void setNullSecurityContextFactory() {
+        filter.setSecurityContextFactory(null);
+    }
+
+    @Test
+    public void testSetSecurityContextFactory() {
+        SecurityContextFactory factory = new SecurityContextFactory() {
+            @Override
+            public SecurityContext createSecurityContext(SubjectConnectionReference ref) {
+                return null;
+            }
+        };
+        filter.setSecurityContextFactory(factory);
+        assertSame(factory, filter.getSecurityContextFactory());
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/SubjectSecurityContextTest.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/SubjectSecurityContextTest.java b/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/SubjectSecurityContextTest.java
new file mode 100644
index 0000000..49d70ed
--- /dev/null
+++ b/activemq-shiro/src/test/java/org/apache/activemq/shiro/subject/SubjectSecurityContextTest.java
@@ -0,0 +1,58 @@
+/**
+ * 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.shiro.subject;
+
+import org.apache.activemq.broker.ConnectionContext;
+import org.apache.activemq.command.ConnectionInfo;
+import org.apache.shiro.env.DefaultEnvironment;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @since 5.10.0
+ */
+public class SubjectSecurityContextTest {
+
+    SubjectSecurityContext ctx;
+
+    @Before
+    public void setUp() {
+        SubjectConnectionReference conn = new SubjectConnectionReference(new ConnectionContext(), new ConnectionInfo(),
+                new DefaultEnvironment(), new SubjectAdapter());
+        ctx = new SubjectSecurityContext(conn);
+    }
+
+    @Test(expected=UnsupportedOperationException.class)
+    public void testInOneOf() {
+        ctx.isInOneOf(null);
+    }
+
+    @Test(expected=UnsupportedOperationException.class)
+    public void testGetAuthorizedReadDests() {
+        ctx.getAuthorizedReadDests();
+    }
+
+    @Test(expected=UnsupportedOperationException.class)
+    public void testGetAuthorizedWriteDests() {
+        ctx.getAuthorizedWriteDests();
+    }
+
+    @Test(expected=UnsupportedOperationException.class)
+    public void testGetPrincipals() {
+        ctx.getPrincipals();
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/resources/empty.shiro.ini
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/resources/empty.shiro.ini b/activemq-shiro/src/test/resources/empty.shiro.ini
new file mode 100644
index 0000000..abe7ae5
--- /dev/null
+++ b/activemq-shiro/src/test/resources/empty.shiro.ini
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+
+# This empty file will cause an error at Shiro startup (as expected).  Used for testing purposes only.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/resources/minimal.shiro.ini
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/resources/minimal.shiro.ini b/activemq-shiro/src/test/resources/minimal.shiro.ini
new file mode 100644
index 0000000..0ccfc86
--- /dev/null
+++ b/activemq-shiro/src/test/resources/minimal.shiro.ini
@@ -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.
+#
+[main]
+
+# Shiro object graph configuration here if desired/necessary
+
+[users]
+
+# users section format:
+#
+# username = password [, assignedRole1, assignedRole2, ..., assignedRoleN]
+#
+# for example:
+#
+# scott = tiger, users, administrators, advisory
+#
+# Roles and permissions assigned to roles are defined in the [roles] section
+# below. By transitive association, any user assigned a role is granted the
+# role's permissions.
+
+# ActiveMQ System User
+# needed for in-VM/local connections when authentication is enabled:
+system = manager, system
+
+[roles]
+
+# roles section format:
+#
+# roleName = wildcardPermission1, wildcardPermission2, ..., wildcardPermissionN
+
+# The 'system' role is assigned all permissions (*).  Be careful when assigning
+# this to actual users other than then system user!
+system = *
+
+# Full access rights should generally be given to the ActiveMQ.Advisory.*
+# destinations because by default an ActiveMQConnection uses advisory topics to
+# get early knowledge of temp destination creation and deletion. For more info:
+#
+#   http://activemq.apache.org/security.html
+#
+# So we create an 'advisory' role here with a wildcard/catch-all permissions
+# for all advisory topics.  To make your life easy, ensure you assign this to
+# any/all users, e.g.
+#
+#   jsmith = password, advisory, ...
+
+advisory = topic:ActiveMQ.Advisory*
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/resources/nosystem.shiro.ini
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/resources/nosystem.shiro.ini b/activemq-shiro/src/test/resources/nosystem.shiro.ini
new file mode 100644
index 0000000..6409c7f
--- /dev/null
+++ b/activemq-shiro/src/test/resources/nosystem.shiro.ini
@@ -0,0 +1,38 @@
+#
+# 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.
+#
+[main]
+
+# THIS IS USED FOR TESTING PURPOSES ONLY.  DO NOT BASE YOUR CONFIGURATION OFF OF THIS FILE.
+
+# Shiro object graph configuration here if desired/necessary
+
+[users]
+# username is purposefully different than the system username:
+foo = bar, foo, advisory
+
+[roles]
+# Full access rights should generally be given to the ActiveMQ.Advisory.* destinations because by default an
+# ActiveMQConnection uses advisory topics to get early knowledge of temp destination creation and deletion.
+# See http://activemq.apache.org/security.html for more.
+#
+# So we create an 'advisory' role here with a wildcard/catch-all permissions for all advisory topics:
+advisory = topic:ActiveMQ.Advisory*
+
+# test specific roles/perms:
+foo = queue:QUEUE.org.apache.activemq.shiro.ShiroPluginTest.testRuntimeDisableEnableChanges:*

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/resources/org/apache/activemq/shiro/embedded-ini-config.xml
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/resources/org/apache/activemq/shiro/embedded-ini-config.xml b/activemq-shiro/src/test/resources/org/apache/activemq/shiro/embedded-ini-config.xml
new file mode 100644
index 0000000..dfe8e68
--- /dev/null
+++ b/activemq-shiro/src/test/resources/org/apache/activemq/shiro/embedded-ini-config.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<!-- this file can only be parsed using the xbean-spring library -->
+<!-- START SNIPPET: example -->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:amq="http://activemq.apache.org/schema/core"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
+       http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">
+
+    <broker xmlns="http://activemq.apache.org/schema/core"
+            useJmx="false" persistent="false" populateJMSXUserID="true">
+
+        <destinations>
+            <queue physicalName="TEST.Q"/>
+        </destinations>
+
+        <transportConnectors>
+            <transportConnector name="default" uri="tcp://0.0.0.0:61616"/>
+        </transportConnectors>
+
+        <plugins>
+            <bean id="shiroPlugin"
+                  class="org.apache.activemq.shiro.ShiroPlugin"
+                  xmlns="http://www.springframework.org/schema/beans">
+                <property name="iniConfig">
+                    <value>
+                        [main]
+
+                        # Shiro object graph configuration here if desired/necessary
+
+                        [users]
+
+                        # users section format:
+                        #
+                        # username = password [, assignedRole1, assignedRole2, ..., assignedRoleN]
+                        #
+                        # for example:
+                        #
+                        # scott = tiger, users, administrators, advisory
+                        #
+                        # Roles and permissions assigned to roles are defined in the [roles] section
+                        # below. By transitive association, any user assigned a role is granted the
+                        # role's permissions.
+
+                        # ActiveMQ System User
+                        # needed for in-VM/local connections when authentication is enabled:
+                        system = manager, system
+
+                        [roles]
+
+                        # roles section format:
+                        #
+                        # roleName = wildcardPermission1, wildcardPermission2, ..., wildcardPermissionN
+
+                        # The 'system' role is assigned all permissions (*). Be careful when assigning
+                        # this to actual users other than then system user:
+                        system = *
+
+                        # Full access rights should generally be given to the ActiveMQ.Advisory.*
+                        # destinations because by default an ActiveMQConnection uses advisory topics to
+                        # get early knowledge of temp destination creation and deletion. For more info:
+                        #
+                        # http://activemq.apache.org/security.html
+                        #
+                        # So we create an 'advisory' role here with a wildcard/catch-all permissions
+                        # for all advisory topics. To make your life easy, ensure you assign this to
+                        # any/all users, e.g.
+                        #
+                        # jsmith = password, advisory, ...
+
+                        advisory = topic:ActiveMQ.Advisory*
+                    </value>
+                </property>
+            </bean>
+        </plugins>
+    </broker>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/test/resources/org/apache/activemq/shiro/external-ini-config.xml
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/test/resources/org/apache/activemq/shiro/external-ini-config.xml b/activemq-shiro/src/test/resources/org/apache/activemq/shiro/external-ini-config.xml
new file mode 100644
index 0000000..b4102d6
--- /dev/null
+++ b/activemq-shiro/src/test/resources/org/apache/activemq/shiro/external-ini-config.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+
+<!-- this file can only be parsed using the xbean-spring library -->
+<!-- START SNIPPET: example -->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:amq="http://activemq.apache.org/schema/core"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
+       http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">
+
+    <broker xmlns="http://activemq.apache.org/schema/core"
+            useJmx="false" persistent="false" populateJMSXUserID="true">
+
+        <destinations>
+            <queue physicalName="TEST.Q"/>
+        </destinations>
+
+        <transportConnectors>
+            <transportConnector name="default" uri="tcp://0.0.0.0:61616"/>
+        </transportConnectors>
+
+        <plugins>
+            <bean id="shiroPlugin" class="org.apache.activemq.shiro.ShiroPlugin"
+                  xmlns="http://www.springframework.org/schema/beans">
+
+                <!-- Reference Shiro's ini config from an external path, e.g. classpath: -->
+                <property name="iniResourcePath" value="classpath:org/apache/activemq/shiro/external-ini-config.xml"/>
+
+                <!-- or it could be in another location, such as a URL or file:
+                <property name="iniResourcePath" value="url:http://config.somehost.com/mybroker/shiro.ini"/>
+                <property name="iniResourcePath" value="file:/usr/local/somewhere/shiro.ini"/> -->
+            </bean>
+        </plugins>
+    </broker>
+
+</beans>


[4/4] git commit: Fix for https://issues.apache.org/jira/browse/AMQ-3621 - Integrate Apache Shiro with ActiveMQ as "security solution" - many thanks to Les Hazlewood (lhazlewood) for the patch!

Posted by ce...@apache.org.
Fix for https://issues.apache.org/jira/browse/AMQ-3621 - Integrate Apache Shiro with ActiveMQ as "security solution" - many thanks to Les Hazlewood (lhazlewood) for the patch!

Commit dev changes to branch.

Signed-off-by: Christian Posta <ch...@gmail.com>

Added shiro support to 5.9.x

Signed-off-by: Christian Posta <ch...@gmail.com>

Added Shiro deps to dist zip/tarball and 'all' .jar

Signed-off-by: Christian Posta <ch...@gmail.com>

updated version number to reflect correctly packaging Shiro features

Signed-off-by: Christian Posta <ch...@gmail.com>

Handled case when adding a producer might not have a specified destination.

Signed-off-by: Christian Posta <ch...@gmail.com>

Merged latest Connection API changes from trunk.

Signed-off-by: Christian Posta <ch...@gmail.com>

removed accidental addition of Stormpath's m2 repo

Signed-off-by: Christian Posta <ch...@gmail.com>

reverted accidental deletion of <distributionManagement>

Signed-off-by: Christian Posta <ch...@gmail.com>


Project: http://git-wip-us.apache.org/repos/asf/activemq/repo
Commit: http://git-wip-us.apache.org/repos/asf/activemq/commit/f9451e56
Tree: http://git-wip-us.apache.org/repos/asf/activemq/tree/f9451e56
Diff: http://git-wip-us.apache.org/repos/asf/activemq/diff/f9451e56

Branch: refs/heads/trunk
Commit: f9451e56e20f8d1712ed09c579d493feee514898
Parents: e7e317d
Author: Les Hazlewood <le...@hazlewood.com>
Authored: Wed Dec 11 18:42:27 2013 -0800
Committer: Christian Posta <ch...@gmail.com>
Committed: Mon Feb 24 08:17:32 2014 -0700

----------------------------------------------------------------------
 activemq-all/pom.xml                            |   8 +
 activemq-shiro/pom.xml                          | 178 ++++++++
 .../activemq/shiro/ConnectionReference.java     |  66 +++
 .../shiro/DefaultSecurityContextFactory.java    |  45 +++
 .../activemq/shiro/SecurityContextFactory.java  |  56 +++
 .../apache/activemq/shiro/SecurityFilter.java   |  41 ++
 .../org/apache/activemq/shiro/ShiroPlugin.java  | 246 +++++++++++
 .../shiro/authc/AuthenticationFilter.java       | 137 +++++++
 .../shiro/authc/AuthenticationPolicy.java       |  58 +++
 .../shiro/authc/AuthenticationTokenFactory.java |  46 +++
 .../authc/DefaultAuthenticationPolicy.java      | 212 ++++++++++
 .../DefaultAuthenticationTokenFactory.java      |  59 +++
 .../org/apache/activemq/shiro/authz/Action.java |  35 ++
 .../shiro/authz/ActionPermissionResolver.java   |  50 +++
 .../shiro/authz/ActiveMQPermissionResolver.java |  57 +++
 .../shiro/authz/ActiveMQWildcardPermission.java | 275 +++++++++++++
 .../shiro/authz/AuthorizationFilter.java        | 226 +++++++++++
 .../activemq/shiro/authz/DestinationAction.java |  90 +++++
 .../DestinationActionPermissionResolver.java    | 272 +++++++++++++
 .../activemq/shiro/env/EnvironmentFilter.java   |  45 +++
 .../activemq/shiro/env/IniEnvironment.java      | 136 +++++++
 .../mgt/DefaultActiveMqSecurityManager.java     |  41 ++
 .../session/mgt/DisabledSessionManager.java     |  39 ++
 .../shiro/subject/ConnectionSubjectFactory.java |  48 +++
 .../subject/ConnectionSubjectResolver.java      |  67 +++
 .../DefaultConnectionSubjectFactory.java        |  52 +++
 .../subject/SubjectConnectionReference.java     |  46 +++
 .../activemq/shiro/subject/SubjectFilter.java   | 119 ++++++
 .../activemq/shiro/subject/SubjectResolver.java |  32 ++
 .../shiro/subject/SubjectSecurityContext.java   |  89 ++++
 .../activemq/shiro/ConnectionReferenceTest.java |  43 ++
 .../activemq/shiro/SecurityFilterTest.java      |  37 ++
 .../apache/activemq/shiro/ShiroPluginTest.java  | 403 +++++++++++++++++++
 .../shiro/authc/AuthenticationFilterTest.java   |  86 ++++
 .../authc/DefaultAuthenticationPolicyTest.java  | 339 ++++++++++++++++
 .../authz/ActiveMQPermissionResolverTest.java   |  53 +++
 .../authz/ActiveMQWildcardPermissionTest.java   | 158 ++++++++
 .../shiro/authz/AuthorizationFilterTest.java    | 364 +++++++++++++++++
 ...DestinationActionPermissionResolverTest.java | 153 +++++++
 .../shiro/authz/DestinationActionTest.java      |  58 +++
 .../shiro/env/EnvironmentFilterTest.java        |  30 ++
 .../activemq/shiro/env/IniEnvironmentTest.java  | 121 ++++++
 .../session/mgt/DisabledSessionManagerTest.java |  47 +++
 .../subject/ConnectionSubjectResolverTest.java  |  80 ++++
 .../DefaultConnectionSubjectFactoryTest.java    |  55 +++
 .../activemq/shiro/subject/SubjectAdapter.java  | 185 +++++++++
 .../subject/SubjectConnectionReferenceTest.java |  33 ++
 .../shiro/subject/SubjectFilterTest.java        |  59 +++
 .../subject/SubjectSecurityContextTest.java     |  58 +++
 .../src/test/resources/empty.shiro.ini          |  20 +
 .../src/test/resources/minimal.shiro.ini        |  63 +++
 .../src/test/resources/nosystem.shiro.ini       |  38 ++
 .../activemq/shiro/embedded-ini-config.xml      |  95 +++++
 .../activemq/shiro/external-ini-config.xml      |  53 +++
 .../org/apache/activemq/shiro/no-ini-config.xml |  78 ++++
 activemq-shiro/src/test/resources/shiro.ini     |  64 +++
 assembly/pom.xml                                |  12 +
 assembly/src/main/descriptors/common-bin.xml    |   7 +-
 pom.xml                                         |  36 ++
 59 files changed, 5698 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-all/pom.xml
----------------------------------------------------------------------
diff --git a/activemq-all/pom.xml b/activemq-all/pom.xml
index 3007874..1eb5430 100644
--- a/activemq-all/pom.xml
+++ b/activemq-all/pom.xml
@@ -106,6 +106,7 @@
                   <include>${project.groupId}:activemq-jaas</include>
                   <include>${project.groupId}:activemq-broker</include>
                   <include>${project.groupId}:activemq-console</include>
+                  <include>${project.groupId}:activemq-shiro</include>
                   <include>${project.groupId}:activemq-spring</include>
                   <include>${project.groupId}:activemq-pool</include>
                   <include>${project.groupId}:activemq-jms-pool</include>
@@ -231,6 +232,13 @@
             </dependency>
             <dependency>
               <groupId>${project.groupId}</groupId>
+              <artifactId>activemq-shiro</artifactId>
+              <version>${project.version}</version>
+              <classifier>sources</classifier>
+              <optional>true</optional>
+            </dependency>
+            <dependency>
+              <groupId>${project.groupId}</groupId>
               <artifactId>activemq-spring</artifactId>
               <version>${project.version}</version>
               <classifier>sources</classifier>

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/pom.xml
----------------------------------------------------------------------
diff --git a/activemq-shiro/pom.xml b/activemq-shiro/pom.xml
new file mode 100644
index 0000000..c273b9e
--- /dev/null
+++ b/activemq-shiro/pom.xml
@@ -0,0 +1,178 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.activemq</groupId>
+        <artifactId>activemq-parent</artifactId>
+        <version>5.10-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>activemq-shiro</artifactId>
+    <packaging>bundle</packaging>
+    <name>ActiveMQ :: Shiro</name>
+    <description>ActiveMQ secured by Apache Shiro</description>
+
+    <dependencies>
+
+        <!-- =============================== -->
+        <!-- Required Dependencies -->
+        <!-- =============================== -->
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>activemq-broker</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.shiro</groupId>
+            <artifactId>shiro-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.shiro</groupId>
+            <artifactId>shiro-spring</artifactId>
+        </dependency>
+
+        <!-- =============================== -->
+        <!-- Optional Dependencies           -->
+        <!-- =============================== -->
+        <!-- <dependency>
+            <groupId>com.thoughtworks.xstream</groupId>
+            <artifactId>xstream</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.jettison</groupId>
+            <artifactId>jettison</artifactId>
+            <optional>true</optional>
+        </dependency> -->
+
+        <!-- =============================== -->
+        <!-- Testing Dependencies            -->
+        <!-- =============================== -->
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>activemq-kahadb-store</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>activemq-broker</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>activemq-unit-tests</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <!-- <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>activemq-jaas</artifactId>
+            <scope>test</scope>
+        </dependency> -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>org.apache.activemq.shiro</Bundle-SymbolicName>
+                        <Export-Package>org.apache.activemq.shiro*;version=${project.version};-noimport:=true;-split-package:=merge-first</Export-Package>
+                        <Import-Package>
+                            org.apache.activemq*;version=${project.version};resolution:=optional,
+                            org.apache.shiro*;version="[1.2,2]",
+                            *
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <!-- <build>
+        <resources>
+            <resource>
+                <directory>${project.basedir}/src/main/resources</directory>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>${project.basedir}/src/main/filtered-resources</directory>
+                <filtering>true</filtering>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+            </resource>
+        </resources>
+
+        <plugins>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <forkMode>always</forkMode>
+                    <argLine>${surefire.argLine}</argLine>
+                    <runOrder>alphabetical</runOrder>
+                    <failIfNoTests>false</failIfNoTests>
+                    <systemProperties>
+                        <property>
+                            <name>org.apache.activemq.default.directory.prefix</name>
+                            <value>target/</value>
+                        </property>
+                        <!- - Uncomment the following if you want to configure custom logging (using src/test/resources/log4j.properties)
+                             while running mvn:test
+                             Note: if you want to see log messages on the console window remove
+                                   "redirectTestOutputToFile" from the parent pom
+                        - ->
+                        <!- -
+                        <property>
+                          <name>log4j.configuration</name>
+                          <value>file:target/test-classes/log4j.properties</value>
+                        </property>
+                        - ->
+                    </systemProperties>
+                    <includes>
+                        <include>**/*Test.*</include>
+                    </includes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build> -->
+</project>

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/ConnectionReference.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/ConnectionReference.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/ConnectionReference.java
new file mode 100644
index 0000000..06c277a
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/ConnectionReference.java
@@ -0,0 +1,66 @@
+/**
+ * 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.shiro;
+
+import org.apache.activemq.broker.ConnectionContext;
+import org.apache.activemq.command.ConnectionInfo;
+import org.apache.shiro.env.Environment;
+
+/**
+ * A reference (handle) to a client's {@link ConnectionContext} and {@link ConnectionInfo} as well as the Shiro
+ * {@link Environment}.
+ * <p/>
+ * This implementation primarily exists as a <a href="http://sourcemaking.com/refactoring/introduce-parameter-object">
+ * Parameter Object Design Pattern</a> implementation to eliminate long parameter lists, but provides additional
+ * benefits, such as immutability and non-null guarantees, and possibility for future data without forcing method
+ * signature changes.
+ *
+ * @since 5.10.0
+ */
+public class ConnectionReference {
+
+    private final ConnectionContext connectionContext;
+    private final ConnectionInfo connectionInfo;
+    private final Environment environment;
+
+    public ConnectionReference(ConnectionContext connCtx, ConnectionInfo connInfo, Environment environment) {
+        if (connCtx == null) {
+            throw new IllegalArgumentException("ConnectionContext argument cannot be null.");
+        }
+        if (connInfo == null) {
+            throw new IllegalArgumentException("ConnectionInfo argument cannot be null.");
+        }
+        if (environment == null) {
+            throw new IllegalArgumentException("Environment argument cannot be null.");
+        }
+        this.connectionContext = connCtx;
+        this.connectionInfo = connInfo;
+        this.environment = environment;
+    }
+
+    public ConnectionContext getConnectionContext() {
+        return connectionContext;
+    }
+
+    public ConnectionInfo getConnectionInfo() {
+        return connectionInfo;
+    }
+
+    public Environment getEnvironment() {
+        return environment;
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/DefaultSecurityContextFactory.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/DefaultSecurityContextFactory.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/DefaultSecurityContextFactory.java
new file mode 100644
index 0000000..2ac65bd
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/DefaultSecurityContextFactory.java
@@ -0,0 +1,45 @@
+/**
+ * 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.shiro;
+
+import org.apache.activemq.security.SecurityContext;
+import org.apache.activemq.shiro.subject.SubjectConnectionReference;
+import org.apache.activemq.shiro.subject.SubjectSecurityContext;
+import org.apache.shiro.env.Environment;
+
+/**
+ * Default {@code SecurityContextFactory} implementation that creates
+ * {@link org.apache.activemq.shiro.subject.SubjectSecurityContext} instances, allowing the connection's {@code Subject} and the Shiro
+ * {@link Environment} to be available to downstream security broker filters.
+ *
+ * @since 5.10.0
+ */
+public class DefaultSecurityContextFactory implements SecurityContextFactory {
+
+    /**
+     * Returns a new {@link org.apache.activemq.shiro.subject.SubjectSecurityContext} instance, allowing the connection's {@code Subject} and the Shiro
+     * {@link Environment} to be available to downstream security broker filters.
+     *
+     * @param conn the subject's connection
+     * @return a new {@link org.apache.activemq.shiro.subject.SubjectSecurityContext} instance, allowing the connection's {@code Subject} and the Shiro
+     *         {@link Environment} to be available to downstream security broker filters.
+     */
+    @Override
+    public SecurityContext createSecurityContext(SubjectConnectionReference conn) {
+        return new SubjectSecurityContext(conn);
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/SecurityContextFactory.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/SecurityContextFactory.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/SecurityContextFactory.java
new file mode 100644
index 0000000..044fb03
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/SecurityContextFactory.java
@@ -0,0 +1,56 @@
+/**
+ * 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.shiro;
+
+import org.apache.activemq.security.SecurityContext;
+import org.apache.activemq.shiro.subject.SubjectConnectionReference;
+import org.apache.shiro.subject.Subject;
+
+/**
+ * A {@code SecurityContextFactory} returns a {@link SecurityContext} instance that retains a client
+ * connection's {@link Subject} instance.
+ * <p/>
+ * It should be noted that at the time a {@code SecurityContextFactory} is invoked, a {@link Subject} is already
+ * associated with the client connection.  A {@code SecurityContextFactory} is merely responsible for creating
+ * a Shiro-specific {@link org.apache.activemq.security.SecurityContext SecurityContext} instance.
+ * <p/>
+ * The returned {@code SecurityContext} instance will then be made available to any downstream Broker Filters via
+ * {@code connectionContext.}{@link org.apache.activemq.broker.ConnectionContext#getSecurityContext() getSecurityContext()}
+ * to ensure it may be used for Shiro-based security checks.
+ *
+ * @see org.apache.activemq.shiro.subject.SubjectSecurityContext
+ * @since 5.10.0
+ */
+public interface SecurityContextFactory {
+
+    /**
+     * Creates a new {@link SecurityContext} retaining the client connection's {@link Subject} instance.
+     * <p/>
+     * It should be noted that at the time a {@code SecurityContextFactory} is invoked, a {@code Subject} is already
+     * associated with the client connection.  A {@code SecurityContextFactory} is merely responsible for creating
+     * a Shiro-specific {@link org.apache.activemq.security.SecurityContext SecurityContext} instance.
+     * <p/>
+     * The returned {@code SecurityContext} instance will then be made available to any downstream Broker Filters via
+     * {@code connectionContext.}{@link org.apache.activemq.broker.ConnectionContext#getSecurityContext() getSecurityContext()}
+     * to ensure it may be used for Shiro-based security checks.
+     *
+     * @param ref the client's connection and subject
+     * @return a new {@link SecurityContext} retaining the client connection's {@link Subject} instance.
+     * @see org.apache.activemq.shiro.subject.SubjectSecurityContext
+     */
+    SecurityContext createSecurityContext(SubjectConnectionReference ref);
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/SecurityFilter.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/SecurityFilter.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/SecurityFilter.java
new file mode 100644
index 0000000..8fbd9de
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/SecurityFilter.java
@@ -0,0 +1,41 @@
+/**
+ * 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.shiro;
+
+import org.apache.activemq.broker.MutableBrokerFilter;
+
+/**
+ * @since 5.10.0
+ */
+public abstract class SecurityFilter extends MutableBrokerFilter {
+
+    private volatile boolean enabled;
+
+    public SecurityFilter() {
+        super(null);
+        this.enabled = true;
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/ShiroPlugin.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/ShiroPlugin.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/ShiroPlugin.java
new file mode 100644
index 0000000..95e69b1
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/ShiroPlugin.java
@@ -0,0 +1,246 @@
+/**
+ * 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.shiro;
+
+import org.apache.activemq.ConfigurationException;
+import org.apache.activemq.broker.Broker;
+import org.apache.activemq.broker.BrokerPluginSupport;
+import org.apache.activemq.shiro.authc.AuthenticationFilter;
+import org.apache.activemq.shiro.authc.AuthenticationPolicy;
+import org.apache.activemq.shiro.authc.DefaultAuthenticationPolicy;
+import org.apache.activemq.shiro.authz.AuthorizationFilter;
+import org.apache.activemq.shiro.env.IniEnvironment;
+import org.apache.activemq.shiro.subject.ConnectionSubjectFactory;
+import org.apache.activemq.shiro.subject.DefaultConnectionSubjectFactory;
+import org.apache.activemq.shiro.subject.SubjectFilter;
+import org.apache.shiro.config.Ini;
+import org.apache.shiro.env.Environment;
+import org.apache.shiro.mgt.SecurityManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @since 5.10.0
+ */
+public class ShiroPlugin extends BrokerPluginSupport {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ShiroPlugin.class);
+
+    private volatile boolean enabled = true;
+
+    private Broker broker; //the downstream broker after any/all Shiro-specific broker filters
+
+    private SecurityManager securityManager;
+    private Environment environment;
+    private IniEnvironment iniEnvironment; //only used if the above environment instance is not explicitly configured
+
+    private SubjectFilter subjectFilter;
+
+    private AuthenticationFilter authenticationFilter;
+
+    private AuthorizationFilter authorizationFilter;
+
+    public ShiroPlugin() {
+
+        //Default if this.environment is not configured. See the ensureEnvironment() method below.
+        iniEnvironment = new IniEnvironment();
+
+        authorizationFilter = new AuthorizationFilter();
+
+        // we want to share one AuthenticationPolicy instance across both the AuthenticationFilter and the
+        // ConnectionSubjectFactory:
+        AuthenticationPolicy authcPolicy = new DefaultAuthenticationPolicy();
+
+        authenticationFilter = new AuthenticationFilter();
+        authenticationFilter.setAuthenticationPolicy(authcPolicy);
+        authenticationFilter.setNext(authorizationFilter);
+
+        subjectFilter = new SubjectFilter();
+        DefaultConnectionSubjectFactory subjectFactory = new DefaultConnectionSubjectFactory();
+        subjectFactory.setAuthenticationPolicy(authcPolicy);
+        subjectFilter.setConnectionSubjectFactory(subjectFactory);
+        subjectFilter.setNext(authenticationFilter);
+    }
+
+    public SubjectFilter getSubjectFilter() {
+        return subjectFilter;
+    }
+
+    public void setSubjectFilter(SubjectFilter subjectFilter) {
+        this.subjectFilter = subjectFilter;
+        this.subjectFilter.setNext(this.authenticationFilter);
+    }
+
+    public AuthenticationFilter getAuthenticationFilter() {
+        return authenticationFilter;
+    }
+
+    public void setAuthenticationFilter(AuthenticationFilter authenticationFilter) {
+        this.authenticationFilter = authenticationFilter;
+        this.authenticationFilter.setNext(this.authorizationFilter);
+        this.subjectFilter.setNext(authenticationFilter);
+    }
+
+    public AuthorizationFilter getAuthorizationFilter() {
+        return authorizationFilter;
+    }
+
+    public void setAuthorizationFilter(AuthorizationFilter authorizationFilter) {
+        this.authorizationFilter = authorizationFilter;
+        this.authorizationFilter.setNext(this.broker);
+        this.authenticationFilter.setNext(authorizationFilter);
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+        if (isInstalled()) {
+            //we're running, so apply the changes now:
+            applyEnabled(enabled);
+        }
+    }
+
+    public boolean isEnabled() {
+        if (isInstalled()) {
+            return getNext() == this.subjectFilter;
+        }
+        return enabled;
+    }
+
+    private void applyEnabled(boolean enabled) {
+        if (enabled) {
+            //ensure the SubjectFilter and downstream filters are used:
+            super.setNext(this.subjectFilter);
+        } else {
+            //Shiro is not enabled, restore the original downstream broker:
+            super.setNext(this.broker);
+        }
+    }
+
+    public Environment getEnvironment() {
+        return environment;
+    }
+
+    public void setEnvironment(Environment environment) {
+        this.environment = environment;
+    }
+
+    public SecurityManager getSecurityManager() {
+        return securityManager;
+    }
+
+    public void setSecurityManager(SecurityManager securityManager) {
+        this.securityManager = securityManager;
+    }
+
+    public void setIni(Ini ini) {
+        this.iniEnvironment.setIni(ini);
+    }
+
+    public void setIniConfig(String iniConfig) {
+        this.iniEnvironment.setIniConfig(iniConfig);
+    }
+
+    public void setIniResourcePath(String resourcePath) {
+        this.iniEnvironment.setIniResourcePath(resourcePath);
+    }
+
+    // ===============================================================
+    // Authentication Configuration
+    // ===============================================================
+    public void setAuthenticationEnabled(boolean authenticationEnabled) {
+        this.authenticationFilter.setEnabled(authenticationEnabled);
+    }
+
+    public boolean isAuthenticationEnabled() {
+        return this.authenticationFilter.isEnabled();
+    }
+
+    public AuthenticationPolicy getAuthenticationPolicy() {
+        return authenticationFilter.getAuthenticationPolicy();
+    }
+
+    public void setAuthenticationPolicy(AuthenticationPolicy authenticationPolicy) {
+        authenticationFilter.setAuthenticationPolicy(authenticationPolicy);
+        //also set it on the ConnectionSubjectFactory:
+        ConnectionSubjectFactory factory = subjectFilter.getConnectionSubjectFactory();
+        if (factory instanceof DefaultConnectionSubjectFactory) {
+            ((DefaultConnectionSubjectFactory) factory).setAuthenticationPolicy(authenticationPolicy);
+        }
+    }
+
+    // ===============================================================
+    // Authorization Configuration
+    // ===============================================================
+    public void setAuthorizationEnabled(boolean authorizationEnabled) {
+        this.authorizationFilter.setEnabled(authorizationEnabled);
+    }
+
+    public boolean isAuthorizationEnabled() {
+        return this.authorizationFilter.isEnabled();
+    }
+
+    private Environment ensureEnvironment() throws ConfigurationException {
+        if (this.environment != null) {
+            return this.environment;
+        }
+
+        //this.environment is null - set it:
+        if (this.securityManager != null) {
+            this.environment = new Environment() {
+                @Override
+                public SecurityManager getSecurityManager() {
+                    return ShiroPlugin.this.securityManager;
+                }
+            };
+            return this.environment;
+        }
+
+        this.iniEnvironment.init(); //will automatically catch any config errors and throw.
+
+        this.environment = iniEnvironment;
+
+        return this.iniEnvironment;
+    }
+
+    @Override
+    public Broker installPlugin(Broker broker) throws Exception {
+
+        Environment environment = ensureEnvironment();
+
+        this.authorizationFilter.setEnvironment(environment);
+        this.authenticationFilter.setEnvironment(environment);
+        this.subjectFilter.setEnvironment(environment);
+
+        this.broker = broker;
+        this.authorizationFilter.setNext(broker);
+        this.authenticationFilter.setNext(this.authorizationFilter);
+        this.subjectFilter.setNext(this.authenticationFilter);
+
+        Broker next = this.subjectFilter;
+        if (!this.enabled) {
+            //not enabled at startup - default to the original broker:
+            next = broker;
+        }
+
+        setNext(next);
+        return this;
+    }
+
+    private boolean isInstalled() {
+        return getNext() != null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/authc/AuthenticationFilter.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/authc/AuthenticationFilter.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authc/AuthenticationFilter.java
new file mode 100644
index 0000000..de68820
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authc/AuthenticationFilter.java
@@ -0,0 +1,137 @@
+/**
+ * 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.shiro.authc;
+
+import org.apache.activemq.broker.ConnectionContext;
+import org.apache.activemq.command.ConnectionInfo;
+import org.apache.activemq.security.SecurityContext;
+import org.apache.activemq.shiro.ConnectionReference;
+import org.apache.activemq.shiro.env.EnvironmentFilter;
+import org.apache.activemq.shiro.subject.ConnectionSubjectResolver;
+import org.apache.activemq.shiro.subject.SubjectConnectionReference;
+import org.apache.activemq.shiro.subject.SubjectSecurityContext;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.subject.Subject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@code AuthenticationFilter} enforces if authentication is required before allowing the broker filter chain
+ * to continue.
+ * <p/>
+ * This implementation performs a connection-level authentication assertion:  If the {@link Subject} associated with the
+ * connection<b>*</b> is not authenticated, and the
+ * {@link AuthenticationPolicy AuthenticationPolicy} requires the {@code Subject} to be authenticated, it will attempt
+ * to {@link Subject#login(org.apache.shiro.authc.AuthenticationToken) login} the Subject automatically.  The
+ * {@link AuthenticationToken} used to login is created by the
+ * {@link #getAuthenticationTokenFactory() authenticationTokenFactory}, typically by acquiring any credentials
+ * associated with the connection.
+ * <p/>
+ * Once the connection's {@code Subject} is authenticated as necessary, the broker filter chain will continue
+ * as expected.
+ * <p/>
+ * <b>*</b>: The upstream {@link org.apache.activemq.shiro.subject.SubjectFilter} is expected to execute before this one, ensuring a Subject instance
+ * is already associated with the connection.
+ *
+ * @since 5.10.0
+ */
+public class AuthenticationFilter extends EnvironmentFilter {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AuthenticationFilter.class);
+
+    private AuthenticationPolicy authenticationPolicy;
+    private AuthenticationTokenFactory authenticationTokenFactory;
+
+    public AuthenticationFilter() {
+        this.authenticationPolicy = new DefaultAuthenticationPolicy();
+        this.authenticationTokenFactory = new DefaultAuthenticationTokenFactory();
+    }
+
+    public AuthenticationPolicy getAuthenticationPolicy() {
+        return authenticationPolicy;
+    }
+
+    public void setAuthenticationPolicy(AuthenticationPolicy authenticationPolicy) {
+        this.authenticationPolicy = authenticationPolicy;
+    }
+
+    public AuthenticationTokenFactory getAuthenticationTokenFactory() {
+        return authenticationTokenFactory;
+    }
+
+    public void setAuthenticationTokenFactory(AuthenticationTokenFactory authenticationTokenFactory) {
+        this.authenticationTokenFactory = authenticationTokenFactory;
+    }
+
+    protected Subject getSubject(ConnectionReference conn) {
+        return new ConnectionSubjectResolver(conn).getSubject();
+    }
+
+    @Override
+    public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
+
+        if (isEnabled()) { //disabled means don't enforce authentication (i.e. allow anonymous access):
+
+            Subject subject = getSubject(new ConnectionReference(context, info, getEnvironment()));
+
+            if (!subject.isAuthenticated()) {
+
+                SubjectConnectionReference connection = new SubjectConnectionReference(context, info, getEnvironment(), subject);
+
+                if (this.authenticationPolicy.isAuthenticationRequired(connection)) {
+                    AuthenticationToken token = this.authenticationTokenFactory.getAuthenticationToken(connection);
+                    if (token == null) {
+                        String msg = "Unable to obtain authentication credentials for newly established connection.  " +
+                                "Authentication is required.";
+                        throw new AuthenticationException(msg);
+                    }
+                    //token is not null - login the current subject:
+                    subject.login(token);
+                }
+            }
+        }
+
+        super.addConnection(context, info);
+    }
+
+    @Override
+    public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Exception {
+        try {
+            super.removeConnection(context, info, error);
+        } finally {
+            SecurityContext secCtx = context.getSecurityContext();
+
+            if (secCtx instanceof SubjectSecurityContext) {
+
+                SubjectSecurityContext subjectSecurityContext = (SubjectSecurityContext) secCtx;
+                Subject subject = subjectSecurityContext.getSubject();
+
+                if (subject != null) {
+                    try {
+                        subject.logout();
+                    } catch (Throwable t) {
+                        String msg = "Unable to cleanly logout connection Subject during connection removal.  This is " +
+                                "unexpected but not critical: it can be safely ignored because the " +
+                                "connection will no longer be used.";
+                        LOG.info(msg, t);
+                    }
+                }
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/authc/AuthenticationPolicy.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/authc/AuthenticationPolicy.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authc/AuthenticationPolicy.java
new file mode 100644
index 0000000..76c8464
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authc/AuthenticationPolicy.java
@@ -0,0 +1,58 @@
+/**
+ * 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.shiro.authc;
+
+import org.apache.activemq.shiro.ConnectionReference;
+import org.apache.activemq.shiro.subject.SubjectConnectionReference;
+import org.apache.shiro.subject.Subject;
+
+/**
+ * An {@code AuthenticationPolicy} customizes the behavior of the {@link AuthenticationFilter}, such as whether or not
+ * authentication is required or how to represent trusted/known {@code Subject} identities.
+ * <p/>
+ * Most will find customizing properties on the {@link DefaultAuthenticationPolicy} easier than implementing this
+ * interface directly.
+ *
+ * @see DefaultAuthenticationPolicy
+ * @since 5.10.0
+ */
+public interface AuthenticationPolicy {
+
+    /**
+     * Allows customization of the {@code Subject} being built for the specified client
+     * connection.  This allows for any pre-existing connection-specific identity or state to be applied to the
+     * {@link Subject.Builder} before the {@code Subject} instance is actually created.
+     * <p/>
+     * <b>NOTE:</b> This method is called by the {@link org.apache.activemq.shiro.subject.SubjectFilter SubjectFilter} <em>before</em> the filter chain
+     * is executed (and before an authentication attempt occurs).  Implementations <b><em>MUST NOT</em></b>
+     * attempt to actually {@link org.apache.shiro.subject.Subject.Builder#buildSubject() build} the subject or perform
+     * an authentication attempt in this method.
+     *
+     * @param subjectBuilder the builder for the Subject that will be created representing the associated client connection
+     * @param ref            a reference to the client's connection metadata
+     * @see org.apache.activemq.shiro.subject.SubjectFilter
+     */
+    void customizeSubject(Subject.Builder subjectBuilder, ConnectionReference ref);
+
+    /**
+     * Returns {@code true} if the connection's {@code Subject} instance should be authenticated, {@code false} otherwise.
+     *
+     * @param ref the subject's connection
+     * @return {@code true} if the connection's {@code Subject} instance should be authenticated, {@code false} otherwise.
+     */
+    boolean isAuthenticationRequired(SubjectConnectionReference ref);
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/authc/AuthenticationTokenFactory.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/authc/AuthenticationTokenFactory.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authc/AuthenticationTokenFactory.java
new file mode 100644
index 0000000..2134929
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authc/AuthenticationTokenFactory.java
@@ -0,0 +1,46 @@
+/**
+ * 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.shiro.authc;
+
+import org.apache.activemq.shiro.subject.SubjectConnectionReference;
+import org.apache.shiro.authc.AuthenticationToken;
+
+/**
+ * A {@code AuthenticationTokenFactory} inspects a newly-added ActiveMQ connection and returns a Shiro
+ * {@link AuthenticationToken} instance representing credentials associated with the connection.  These credentials can
+ * be used to {@link org.apache.shiro.subject.Subject#login(org.apache.shiro.authc.AuthenticationToken) authenticate}
+ * the connection, allowing for later identity and authorization (access control) checks.
+ *
+ * @see AuthenticationFilter#addConnection(org.apache.activemq.broker.ConnectionContext, org.apache.activemq.command.ConnectionInfo)
+ * @since 5.10.0
+ */
+public interface AuthenticationTokenFactory {
+
+    /**
+     * Returns a Shiro {@code AuthenticationToken} instance that should be used to authenticate the connection's
+     * {@link org.apache.shiro.subject.Subject}, or {@code null} if no authentication information can be obtained.
+     * <p/>
+     * If no {@code AuthenticationToken} can be obtained, the connection's Subject will be considered anonymous and any
+     * downstream security checks that enforce authentication or authorization will fail (as would be expected).
+     *
+     * @param ref the subject's connection
+     * @return a Shiro {@code AuthenticationToken} instance that should be used to authenticate the connection's
+     *         {@link org.apache.shiro.subject.Subject}, or {@code null} if no authentication information can be obtained.
+     * @throws Exception if there is a problem acquiring/creating an expected {@code AuthenticationToken}.
+     */
+    AuthenticationToken getAuthenticationToken(SubjectConnectionReference ref) throws Exception;
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/authc/DefaultAuthenticationPolicy.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/authc/DefaultAuthenticationPolicy.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authc/DefaultAuthenticationPolicy.java
new file mode 100644
index 0000000..654c5e5
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authc/DefaultAuthenticationPolicy.java
@@ -0,0 +1,212 @@
+/**
+ * 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.shiro.authc;
+
+import org.apache.activemq.shiro.ConnectionReference;
+import org.apache.activemq.shiro.subject.SubjectConnectionReference;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.apache.shiro.subject.SimplePrincipalCollection;
+import org.apache.shiro.subject.Subject;
+
+import java.util.Collection;
+
+/**
+ * @since 5.10.0
+ */
+public class DefaultAuthenticationPolicy implements AuthenticationPolicy {
+
+    private boolean vmConnectionAuthenticationRequired = false;
+    private String systemAccountUsername = "system";
+    private String systemAccountRealmName = "iniRealm";
+
+    private boolean anonymousAccessAllowed = false;
+    private String anonymousAccountUsername = "anonymous";
+    private String anonymousAccountRealmName = "iniRealm";
+
+    public boolean isVmConnectionAuthenticationRequired() {
+        return vmConnectionAuthenticationRequired;
+    }
+
+    public void setVmConnectionAuthenticationRequired(boolean vmConnectionAuthenticationRequired) {
+        this.vmConnectionAuthenticationRequired = vmConnectionAuthenticationRequired;
+    }
+
+    public String getSystemAccountUsername() {
+        return systemAccountUsername;
+    }
+
+    public void setSystemAccountUsername(String systemAccountUsername) {
+        this.systemAccountUsername = systemAccountUsername;
+    }
+
+    public String getSystemAccountRealmName() {
+        return systemAccountRealmName;
+    }
+
+    public void setSystemAccountRealmName(String systemAccountRealmName) {
+        this.systemAccountRealmName = systemAccountRealmName;
+    }
+
+    public boolean isAnonymousAccessAllowed() {
+        return anonymousAccessAllowed;
+    }
+
+    public void setAnonymousAccessAllowed(boolean anonymousAccessAllowed) {
+        this.anonymousAccessAllowed = anonymousAccessAllowed;
+    }
+
+    public String getAnonymousAccountUsername() {
+        return anonymousAccountUsername;
+    }
+
+    public void setAnonymousAccountUsername(String anonymousAccountUsername) {
+        this.anonymousAccountUsername = anonymousAccountUsername;
+    }
+
+    public String getAnonymousAccountRealmName() {
+        return anonymousAccountRealmName;
+    }
+
+    public void setAnonymousAccountRealmName(String anonymousAccountRealmName) {
+        this.anonymousAccountRealmName = anonymousAccountRealmName;
+    }
+
+    /**
+     * Returns {@code true} if the client connection has supplied credentials to authenticate itself, {@code false}
+     * otherwise.
+     *
+     * @param conn the client's connection context
+     * @return {@code true} if the client connection has supplied credentials to authenticate itself, {@code false}
+     *         otherwise.
+     */
+    protected boolean credentialsAvailable(ConnectionReference conn) {
+        return conn.getConnectionInfo().getUserName() != null || conn.getConnectionInfo().getPassword() != null;
+    }
+
+    @Override
+    public boolean isAuthenticationRequired(SubjectConnectionReference conn) {
+        Subject subject = conn.getSubject();
+
+        if (subject.isAuthenticated()) {
+            //already authenticated:
+            return false;
+        }
+        //subject is not authenticated.  Authentication is required by default for all accounts other than
+        //the anonymous user (if enabled) or the vm account (if enabled)
+        if (isAnonymousAccessAllowed()) {
+            if (isAnonymousAccount(subject)) {
+                return false;
+            }
+        }
+
+        if (!isVmConnectionAuthenticationRequired()) {
+            if (isSystemAccount(subject)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    protected boolean isAnonymousAccount(Subject subject) {
+        PrincipalCollection pc = subject.getPrincipals();
+        return pc != null && matches(pc, anonymousAccountUsername, anonymousAccountRealmName);
+    }
+
+    protected boolean isSystemAccount(Subject subject) {
+        PrincipalCollection pc = subject.getPrincipals();
+        return pc != null && matches(pc, systemAccountUsername, systemAccountRealmName);
+    }
+
+    protected boolean matches(PrincipalCollection principals, String username, String realmName) {
+        Collection realmPrincipals = principals.fromRealm(realmName);
+        if (realmPrincipals != null && !realmPrincipals.isEmpty()) {
+            if (realmPrincipals.iterator().next().equals(username)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    protected boolean isSystemConnection(ConnectionReference conn) {
+        String remoteAddress = conn.getConnectionContext().getConnection().getRemoteAddress();
+        return remoteAddress.startsWith("vm:");
+    }
+
+    @Override
+    public void customizeSubject(Subject.Builder subjectBuilder, ConnectionReference conn) {
+        // We only need to specify a custom identity or authentication state if a normal authentication will not occur.
+        // If the client supplied connection credentials, the AuthenticationFilter will perform a normal authentication,
+        // so we should exit immediately:
+        if (credentialsAvailable(conn)) {
+            return;
+        }
+
+        //The connection cannot be authenticated, potentially implying a system or anonymous connection.  Check if so:
+        if (isAssumeIdentity(conn)) {
+            PrincipalCollection assumedIdentity = createAssumedIdentity(conn);
+            subjectBuilder.principals(assumedIdentity);
+        }
+    }
+
+    /**
+     * Returns {@code true} if an unauthenticated connection should still assume a specific identity, {@code false}
+     * otherwise.  This method will <em>only</em> be called if there are no connection
+     * {@link #credentialsAvailable(ConnectionReference) credentialsAvailable}.
+     * If a client supplies connection credentials, they will always be used to authenticate the client with that
+     * identity.
+     * <p/>
+     * If {@code true} is returned, the assumed identity will be returned by
+     * {@link #createAssumedIdentity(ConnectionReference) createAssumedIdentity}.
+     * <h3>Warning</h3>
+     * This method exists primarily to support the system and anonymous accounts - it is probably unsafe to return
+     * {@code true} in most other scenarios.
+     *
+     * @param conn a reference to the client's connection
+     * @return {@code true} if an unauthenticated connection should still assume a specific identity, {@code false}
+     *         otherwise.
+     */
+    protected boolean isAssumeIdentity(ConnectionReference conn) {
+        return isAnonymousAccessAllowed() ||
+                (isSystemConnection(conn) && !isVmConnectionAuthenticationRequired());
+    }
+
+    /**
+     * Returns a Shiro {@code PrincipalCollection} representing the identity to assume (without true authentication) for
+     * the specified Connection.
+     * <p/>
+     * This method is <em>only</em> called if {@link #isAssumeIdentity(ConnectionReference)} is {@code true}.
+     *
+     * @param conn a reference to the client's connection
+     * @return a Shiro {@code PrincipalCollection} representing the identity to assume (without true authentication) for
+     *         the specified Connection.
+     */
+    protected PrincipalCollection createAssumedIdentity(ConnectionReference conn) {
+
+        //anonymous by default:
+        String username = anonymousAccountUsername;
+        String realmName = anonymousAccountRealmName;
+
+        //vm connections are special and should assume the system account:
+        if (isSystemConnection(conn)) {
+            username = systemAccountUsername;
+            realmName = systemAccountRealmName;
+        }
+
+        return new SimplePrincipalCollection(username, realmName);
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/authc/DefaultAuthenticationTokenFactory.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/authc/DefaultAuthenticationTokenFactory.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authc/DefaultAuthenticationTokenFactory.java
new file mode 100644
index 0000000..1c3a0d3
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authc/DefaultAuthenticationTokenFactory.java
@@ -0,0 +1,59 @@
+/**
+ * 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.shiro.authc;
+
+import org.apache.activemq.command.ConnectionInfo;
+import org.apache.activemq.shiro.subject.SubjectConnectionReference;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.UsernamePasswordToken;
+
+/**
+ * Default implementation of the {@link AuthenticationTokenFactory} interface that returns
+ * {@link org.apache.shiro.authc.UsernamePasswordToken UsernamePasswordToken} instances based on inspecting the
+ * {@link ConnectionInfo}.
+ *
+ * @since 5.10.0
+ */
+public class DefaultAuthenticationTokenFactory implements AuthenticationTokenFactory {
+
+    public DefaultAuthenticationTokenFactory() {
+    }
+
+    /**
+     * Returns a new {@link UsernamePasswordToken} instance populated based on the ConnectionInfo's
+     * {@link org.apache.activemq.command.ConnectionInfo#getUserName() userName} and
+     * {@link org.apache.activemq.command.ConnectionInfo#getPassword() password} properties.
+     *
+     * @param conn the subject's connection
+     * @return a new {@link UsernamePasswordToken} instance populated based on the ConnectionInfo's
+     *         ConnectionInfo's {@link org.apache.activemq.command.ConnectionInfo#getUserName() userName} and
+     *         {@link org.apache.activemq.command.ConnectionInfo#getPassword() password} properties.
+     */
+    @Override
+    public AuthenticationToken getAuthenticationToken(SubjectConnectionReference conn) {
+
+        String username = conn.getConnectionInfo().getUserName();
+        String password = conn.getConnectionInfo().getPassword();
+
+        if (username == null && password == null) {
+            //no identity or credentials provided by the client for the connection - return null to reflect this
+            return null;
+        }
+
+        return new UsernamePasswordToken(username, password);
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/Action.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/Action.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/Action.java
new file mode 100644
index 0000000..dfde09f
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/Action.java
@@ -0,0 +1,35 @@
+/**
+ * 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.shiro.authz;
+
+/**
+ * An {@code Action} represents an attempt to perform some behavior, typically on a particular resource.
+ *
+ * @see DestinationAction
+ * @since 5.10.0
+ */
+public interface Action {
+
+    /**
+     * Returns a human readable string that indicates what this action is, for example "open doors" or
+     * "delete file /usr/local/foo"
+     *
+     * @return a human readable string that indicates what this action is
+     */
+    String toString();
+
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/ActionPermissionResolver.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/ActionPermissionResolver.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/ActionPermissionResolver.java
new file mode 100644
index 0000000..a379d21
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/ActionPermissionResolver.java
@@ -0,0 +1,50 @@
+/**
+ * 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.shiro.authz;
+
+import org.apache.shiro.authz.Permission;
+
+import java.util.Collection;
+
+/**
+ * An {@code ActionPermissionResolver} will inspect an {@link Action} and return
+ * {@link Permission}s that must be granted to a {@link org.apache.shiro.subject.Subject Subject} in order for the
+ * {@code Subject} to execute the action.
+ * <p/>
+ * If a {@code Subject} is not granted all of the returned permissions, the {@code Action} will not be executed.
+ *
+ * @since 5.10.0
+ */
+public interface ActionPermissionResolver {
+
+    /**
+     * Returns all {@link Permission}s that must be granted to a
+     * {@link org.apache.shiro.subject.Subject Subject} in order for the {@code Subject} to execute the action, or
+     * an empty collection if no permissions are required.
+     * <p/>
+     * Most implementations will probably return a single Permission, but multiple permissions are possible, especially
+     * if the Action represents behavior attempted on a
+     * <a href="http://activemq.apache.org/composite-destinations.html">Composite Destination</a>.
+     *
+     * @param action the action attempted
+     * @return all {@link Permission}s that must be granted to a
+     *         {@link org.apache.shiro.subject.Subject Subject} in order for the {@code Subject} to execute the action,
+     *         or an empty collection if no permissions are required.
+     */
+    Collection<Permission> getPermissions(Action action);
+
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/ActiveMQPermissionResolver.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/ActiveMQPermissionResolver.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/ActiveMQPermissionResolver.java
new file mode 100644
index 0000000..620957b
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/ActiveMQPermissionResolver.java
@@ -0,0 +1,57 @@
+/**
+ * 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.shiro.authz;
+
+import org.apache.shiro.authz.Permission;
+import org.apache.shiro.authz.permission.WildcardPermission;
+import org.apache.shiro.authz.permission.WildcardPermissionResolver;
+
+/**
+ * {@link WildcardPermissionResolver} that can create case-sensitive (or case-insensitive)
+ * {@link WildcardPermission} instances as expected for ActiveMQ.
+ *
+ * @since 5.10.0
+ */
+public class ActiveMQPermissionResolver extends WildcardPermissionResolver {
+
+    private boolean caseSensitive;
+
+    public ActiveMQPermissionResolver() {
+        caseSensitive = true;
+    }
+
+    public boolean isCaseSensitive() {
+        return caseSensitive;
+    }
+
+    public void setCaseSensitive(boolean caseSensitive) {
+        this.caseSensitive = caseSensitive;
+    }
+
+    /**
+     * Creates a new {@link WildcardPermission} instance, with case-sensitivity determined by the
+     * {@link #isCaseSensitive() caseSensitive} setting.
+     *
+     * @param permissionString the wildcard permission-formatted string.
+     * @return a new {@link WildcardPermission} instance, with case-sensitivity determined by the
+     *         {@link #isCaseSensitive() caseSensitive} setting.
+     */
+    @Override
+    public Permission resolvePermission(String permissionString) {
+        return new ActiveMQWildcardPermission(permissionString, isCaseSensitive());
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq/blob/f9451e56/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/ActiveMQWildcardPermission.java
----------------------------------------------------------------------
diff --git a/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/ActiveMQWildcardPermission.java b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/ActiveMQWildcardPermission.java
new file mode 100644
index 0000000..38a514c
--- /dev/null
+++ b/activemq-shiro/src/main/java/org/apache/activemq/shiro/authz/ActiveMQWildcardPermission.java
@@ -0,0 +1,275 @@
+/**
+ * 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.shiro.authz;
+
+import org.apache.shiro.authz.Permission;
+import org.apache.shiro.authz.permission.WildcardPermission;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @since 5.10.0
+ */
+public class ActiveMQWildcardPermission extends WildcardPermission {
+
+    private final boolean caseSensitive;
+
+    public ActiveMQWildcardPermission(String wildcardString) {
+        this(wildcardString, true);
+    }
+
+    public ActiveMQWildcardPermission(String wildcardString, boolean caseSensitive) {
+        super(wildcardString, caseSensitive);
+        this.caseSensitive = caseSensitive;
+    }
+
+    @Override
+    public boolean implies(Permission p) {
+        // By default only supports comparisons with other WildcardPermissions
+        if (!(p instanceof WildcardPermission)) {
+            return false;
+        }
+
+        WildcardPermission wp = (WildcardPermission) p;
+
+        List<Set<String>> otherParts = getParts(wp);
+
+        int i = 0;
+        for (Set<String> otherPart : otherParts) {
+            // If this permission has less parts than the other permission, everything after the number of parts contained
+            // in this permission is automatically implied, so return true
+            if (getParts().size() - 1 < i) {
+                return true;
+            } else {
+                Set<String> thisPart = getParts().get(i);
+
+                for (String token : thisPart) {
+                    if (token.equals(WILDCARD_TOKEN)) {
+                        continue;
+                    }
+                    for (String otherToken : otherPart) {
+                        if (!caseSensitive) {
+                            otherToken = otherToken.toLowerCase();
+                        }
+                        if (!matches(token, otherToken)) {
+                            return false;
+                        }
+                    }
+                }
+                i++;
+            }
+        }
+
+        // If this permission has more parts than the other parts, only imply it if all of the other parts are wildcards
+        for (; i < getParts().size(); i++) {
+            Set<String> part = getParts().get(i);
+            if (!part.contains(WILDCARD_TOKEN)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Tests whether or not a string matches against a pattern.
+     * The pattern may contain two special characters:<br>
+     * '*' means zero or more characters<br>
+     * '?' means one and only one character
+     *
+     * @param pattern pattern to match against.
+     *                Must not be <code>null</code>.
+     * @param value   string which must be matched against the pattern.
+     *                Must not be <code>null</code>.
+     * @return <code>true</code> if the string matches against the
+     *         pattern, or <code>false</code> otherwise.
+     */
+    protected boolean matches(String pattern, String value) {
+
+        char[] patArr = pattern.toCharArray();
+        char[] valArr = value.toCharArray();
+        int patIndex = 0;
+        int patEndIndex = patArr.length - 1;
+        int valIndex = 0;
+        int valEndIndex = valArr.length - 1;
+        char ch;
+
+        boolean patternContainsStar = false;
+        for (char patternChar : patArr) {
+            if (patternChar == '*') {
+                patternContainsStar = true;
+                break;
+            }
+        }
+
+        if (!patternContainsStar) {
+            // No '*'s, so we make a shortcut
+            if (patEndIndex != valEndIndex) {
+                return false; // Pattern and string do not have the same size
+            }
+            for (int i = 0; i <= patEndIndex; i++) {
+                ch = patArr[i];
+                if (ch != '?') {
+                    if (ch != valArr[i]) {
+                        return false;// Character mismatch
+                    }
+                }
+            }
+            return true; // String matches against pattern
+        }
+
+
+        // Process characters before first star
+        while ((ch = patArr[patIndex]) != '*' && valIndex <= valEndIndex) {
+            if (ch != '?') {
+                if (ch != valArr[valIndex]) {
+                    return false;// Character mismatch
+                }
+            }
+            patIndex++;
+            valIndex++;
+        }
+        if (valIndex > valEndIndex) {
+            // All characters in the value are used. Check if only '*'s remain
+            // in the pattern. If so, we succeeded. Otherwise failure.
+            for (int i = patIndex; i <= patEndIndex; i++) {
+                if (patArr[i] != '*') {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        // Process characters after last star
+        while ((ch = patArr[patEndIndex]) != '*' && valIndex <= valEndIndex) {
+            if (ch != '?') {
+                if (ch != valArr[valEndIndex]) {
+                    return false;// Character mismatch
+                }
+            }
+            patEndIndex--;
+            valEndIndex--;
+        }
+        if (valIndex > valEndIndex) {
+            // All characters in the value are used. Check if only '*'s remain
+            // in the pattern. If so, we succeeded. Otherwise failure.
+            for (int i = patIndex; i <= patEndIndex; i++) {
+                if (patArr[i] != '*') {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        // process pattern between stars. patIndex and patEndIndex always point to a '*'.
+        while (patIndex != patEndIndex && valIndex <= valEndIndex) {
+            int innerPatternIndex = -1;
+            for (int i = patIndex + 1; i <= patEndIndex; i++) {
+                if (patArr[i] == '*') {
+                    innerPatternIndex = i;
+                    break;
+                }
+            }
+            if (innerPatternIndex == patIndex + 1) {
+                // Two stars next to each other, skip the first one.
+                patIndex++;
+                continue;
+            }
+            // Find the pattern between patIndex & innerPatternIndex in the value between
+            // valIndex and valEndIndex
+            int innerPatternLength = (innerPatternIndex - patIndex - 1);
+            int innerValueLength = (valEndIndex - valIndex + 1);
+            int foundIndex = -1;
+            innerValueLoop:
+            for (int i = 0; i <= innerValueLength - innerPatternLength; i++) {
+                for (int j = 0; j < innerPatternLength; j++) {
+                    ch = patArr[patIndex + j + 1];
+                    if (ch != '?') {
+                        if (ch != valArr[valIndex + i + j]) {
+                            continue innerValueLoop;
+                        }
+                    }
+                }
+
+                foundIndex = valIndex + i;
+                break;
+            }
+
+            if (foundIndex == -1) {
+                return false;
+            }
+
+            patIndex = innerPatternIndex;
+            valIndex = foundIndex + innerPatternLength;
+        }
+
+        // All characters in the string are used. Check if only '*'s are left
+        // in the pattern. If so, we succeeded. Otherwise failure.
+        for (int i = patIndex; i <= patEndIndex; i++) {
+            if (patArr[i] != '*') {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    protected List<Set<String>> getParts(WildcardPermission wp) {
+        if (wp instanceof ActiveMQWildcardPermission) {
+            return ((ActiveMQWildcardPermission) wp).getParts();
+        } else {
+            return getPartsByReflection(wp);
+        }
+    }
+
+    protected List<Set<String>> getPartsByReflection(WildcardPermission wp) {
+        try {
+            return doGetPartsByReflection(wp);
+        } catch (Exception e) {
+            String msg = "Unable to obtain WildcardPermission instance's 'parts' value.";
+            throw new IllegalStateException(msg, e);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    protected List<Set<String>> doGetPartsByReflection(WildcardPermission wp) throws Exception {
+        Method getParts = WildcardPermission.class.getDeclaredMethod("getParts");
+        getParts.setAccessible(true);
+        return (List<Set<String>>) getParts.invoke(wp);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder buffer = new StringBuilder();
+        for (Set<String> part : getParts()) {
+            if (buffer.length() > 0) {
+                buffer.append(":");
+            }
+            boolean first = true;
+            for (String token : part) {
+                if (!first) {
+                    buffer.append(",");
+                }
+                buffer.append(token);
+                first = false;
+            }
+        }
+        return buffer.toString();
+    }
+}