blob: 0947529d6c0bb474052da3eb6bb4c4c286814a25 [file] [log] [blame]
/**
* 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());
}
}