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 <christian.posta@gmail.com>

Added shiro support to 5.9.x

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

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

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

updated version number to reflect correctly packaging Shiro features

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

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

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

Merged latest Connection API changes from trunk.

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

removed accidental addition of Stormpath's m2 repo

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

reverted accidental deletion of <distributionManagement>

Signed-off-by: Christian Posta <christian.posta@gmail.com>
This commit is contained in:
Les Hazlewood 2013-12-11 18:42:27 -08:00 committed by Christian Posta
parent e7e317dc7e
commit f9451e56e2
59 changed files with 5698 additions and 1 deletions

View File

@ -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>
@ -229,6 +230,13 @@
<classifier>sources</classifier>
<optional>true</optional>
</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>

178
activemq-shiro/pom.xml Normal file
View File

@ -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>

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}
}
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}

View File

@ -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");
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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;
}
}
}

View File

@ -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]);
}
}

View File

@ -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));
}
}

View File

@ -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)));
}
}

View File

@ -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));
}
}

View File

@ -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());
}
}
}

View File

@ -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");
}
}

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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");
}
}

View File

@ -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")));
}
}

View File

@ -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();
}
}

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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.

View File

@ -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*

View File

@ -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:*

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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*

View File

@ -175,6 +175,10 @@
<artifactId>jaxb2-basics-runtime</artifactId>
<version>${jaxb-basics-version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>activemq-shiro</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>activemq-spring</artifactId>
@ -285,6 +289,14 @@
<groupId>org.apache.geronimo.specs</groupId>
<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>

View File

@ -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>

36
pom.xml
View File

@ -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>
@ -384,6 +387,11 @@
<artifactId>activemq-pool</artifactId>
<version>${project.version}</version>
</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>
@ -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>