001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.activemq.shiro.authz; 018 019import org.apache.activemq.command.ActiveMQDestination; 020import org.apache.shiro.authz.Permission; 021import org.apache.shiro.authz.permission.WildcardPermission; 022 023import java.util.Collection; 024import java.util.HashSet; 025import java.util.LinkedHashSet; 026import java.util.Set; 027 028/** 029 * A {@code DestinationActionPermissionResolver} inspects {@link DestinationAction}s and returns one or more 030 * {@link WildcardPermission}s that must be granted to a {@code Subject} in order for that {@code Subject} to 031 * perform the action being taken on an {@link ActiveMQDestination}. 032 * <p/> 033 * See the {@link #createPermissionString createPermissionString documentation} to see what the 034 * resulting {@link WildcardPermission} instances would look like. 035 * 036 * @see #createPermissionString(org.apache.activemq.command.ActiveMQDestination, String) ) 037 * @see #setPermissionStringPrefix(String) 038 * @since 5.10.0 039 */ 040public class DestinationActionPermissionResolver implements ActionPermissionResolver { 041 042 private String permissionStringPrefix; 043 private boolean permissionStringCaseSensitive = true; 044 045 /** 046 * Returns the String prefix that should be automatically prepended to a permission String before the 047 * String is converted to a {@link WildcardPermission} instance. This is convenient if you want to provide a 048 * 'scope' or 'namespace' for ActiveMQ Destinations to clearly distinguish ActiveMQ-specific permissions from any 049 * others you might assign to user accounts. The default value is {@code null}, indicating no prefix will be 050 * set by default. 051 * <p/> 052 * For example, the default settings might result in permissions Strings that look like this: 053 * <pre> 054 * topic:TEST:create 055 * temp-queue:MyQueue:remove 056 * topic:ActiveMQ.Advisory.*:read 057 * </pre> 058 * <p/> 059 * However, if your application has any application-specific permissions that start with the tokens {@code topic}, 060 * {@code temp-topic}, {@code queue}, or {@code temp-queue}, you wouldn't be able to distinguish between 061 * application-specific permissions and those specific to ActiveMQ. In this case you might set the 062 * {@code permissionStringPrefix}. For example, if you set: 063 * {@code resolver.setPermissionStringPrefix("jms");}, the above permission strings would look like this: 064 * <pre> 065 * jms:topic:TEST:create 066 * jms:temp-queue:MyQueue:remove 067 * jms:topic:ActiveMQ.Advisory.*:read 068 * </pre> 069 * <p/> 070 * Similarly, if the {@code permissionStringPrefix} was equal to {@code activeMQ}: 071 * <pre> 072 * activeMQ:topic:TEST:create 073 * activeMQ:temp-queue:MyQueue:remove 074 * activeMQ:topic:ActiveMQ.Advisory.*:read 075 * </pre> 076 * 077 * @return any String prefix that should be automatically prepended to a permission String before the 078 * String is converted to a {@link WildcardPermission} instance. Useful for namespacing permissions. 079 */ 080 public String getPermissionStringPrefix() { 081 return permissionStringPrefix; 082 } 083 084 /** 085 * Sets the String prefix that should be automatically prepended to a permission String before the 086 * String is converted to a {@link WildcardPermission} instance. This is convenient if you want to provide a 087 * 'scope' or 'namespace' for ActiveMQ Destinations to clearly distinguish ActiveMQ-specific permissions from any 088 * others you might assign to user accounts. The default value is {@code null}, indicating no prefix will be 089 * set by default. 090 * <p/> 091 * For example, the default settings might result in permissions Strings that look like this: 092 * <pre> 093 * topic:TEST:create 094 * temp-queue:MyQueue:remove 095 * topic:ActiveMQ.Advisory.*:read 096 * </pre> 097 * <p/> 098 * However, if your application has any application-specific permissions that start with the tokens {@code topic}, 099 * {@code temp-topic}, {@code queue}, or {@code temp-queue}, you wouldn't be able to distinguish between 100 * application-specific permissions and those specific to ActiveMQ. In this case you might set the 101 * {@code permissionStringPrefix}. For example, if you set: 102 * {@code resolver.setPermissionStringPrefix("jms");}, the above permission strings would look like this: 103 * <pre> 104 * jms:topic:TEST:create 105 * jms:temp-queue:MyQueue:remove 106 * jms:topic:ActiveMQ.Advisory.*:read 107 * </pre> 108 * <p/> 109 * Similarly, if the {@code permissionStringPrefix} was equal to {@code activeMQ}: 110 * <pre> 111 * activeMQ:topic:TEST:create 112 * activeMQ:temp-queue:MyQueue:remove 113 * activeMQ:topic:ActiveMQ.Advisory.*:read 114 * </pre> 115 * 116 * @param permissionStringPrefix any String prefix that should be automatically prepended to a permission String 117 * before the String is converted to a {@link WildcardPermission} instance. Useful 118 * for namespacing permissions. 119 */ 120 public void setPermissionStringPrefix(String permissionStringPrefix) { 121 this.permissionStringPrefix = permissionStringPrefix; 122 } 123 124 /** 125 * Returns {@code true} if returned {@link WildcardPermission} instances should be considered case-sensitive, 126 * {@code false} otherwise. The default value is {@code true}, which is <em>not</em> the normal 127 * {@link WildcardPermission} default setting. This default was chosen to reflect ActiveMQ's 128 * <a href="http://activemq.apache.org/are-destinations-case-sensitive.html">case-sensitive destination names</a>. 129 * 130 * @return {@code true} if returned {@link WildcardPermission} instances should be considered case-sensitive, 131 * {@code false} otherwise. 132 */ 133 public boolean isPermissionStringCaseSensitive() { 134 return permissionStringCaseSensitive; 135 } 136 137 /** 138 * Sets whether returned {@link WildcardPermission} instances should be considered case-sensitive. 139 * The default value is {@code true}, which is <em>not</em> the normal 140 * {@link WildcardPermission} default setting. This default was chosen to accurately reflect ActiveMQ's 141 * <a href="http://activemq.apache.org/are-destinations-case-sensitive.html">case-sensitive destination names</a>. 142 * 143 * @param permissionStringCaseSensitive whether returned {@link WildcardPermission} instances should be considered 144 * case-sensitive. 145 */ 146 public void setPermissionStringCaseSensitive(boolean permissionStringCaseSensitive) { 147 this.permissionStringCaseSensitive = permissionStringCaseSensitive; 148 } 149 150 @Override 151 public Collection<Permission> getPermissions(Action action) { 152 if (!(action instanceof DestinationAction)) { 153 throw new IllegalArgumentException("Action argument must be a " + DestinationAction.class.getName() + " instance."); 154 } 155 DestinationAction da = (DestinationAction) action; 156 return getPermissions(da); 157 } 158 159 protected Collection<Permission> getPermissions(DestinationAction da) { 160 ActiveMQDestination dest = da.getDestination(); 161 String verb = da.getVerb(); 162 return createPermissions(dest, verb); 163 } 164 165 protected Collection<Permission> createPermissions(ActiveMQDestination dest, String verb) { 166 167 Set<Permission> set; 168 169 if (dest.isComposite()) { 170 ActiveMQDestination[] composites = dest.getCompositeDestinations(); 171 set = new LinkedHashSet<Permission>(composites.length); 172 for(ActiveMQDestination d : composites) { 173 Collection<Permission> perms = createPermissions(d, verb); 174 set.addAll(perms); 175 } 176 } else { 177 set = new HashSet<Permission>(1); 178 String permString = createPermissionString(dest, verb); 179 Permission perm = createPermission(permString); 180 set.add(perm); 181 } 182 183 return set; 184 } 185 186 /** 187 * Inspects the specified {@code destination} and {@code verb} and returns a {@link WildcardPermission}-compatible 188 * String the represents the action. 189 * <h3>Format</h3> 190 * This implementation returns WildcardPermission strings with the following format: 191 * <pre> 192 * optionalPermissionStringPrefix + destinationType + ':' + destinationPhysicalName + ':' + actionVerb 193 * </pre> 194 * where: 195 * <ol> 196 * <li>{@code optionalPermissionStringPrefix} is the {@link #getPermissionStringPrefix() permissionStringPrefix} 197 * followed by a colon delimiter (':'). This is only present if the {@code permissionStringPrefix} has been 198 * specified and is non-null</li> 199 * <li>{@code destinationType} is one of the following four string tokens: 200 * <ul> 201 * <li>{@code topic}</li> 202 * <li>{@code temp-topic}</li> 203 * <li>{@code queue}</li> 204 * <li>{@code temp-queue}</li> 205 * </ul> 206 * based on whether the {@link DestinationAction#getDestination() destination} is 207 * a topic, temporary topic, queue, or temporary queue (respectively). 208 * </li> 209 * <li> 210 * {@code destinationPhysicalName} is 211 * {@link org.apache.activemq.command.ActiveMQDestination#getPhysicalName() destination.getPhysicalName()} 212 * </li> 213 * <li> 214 * {@code actionVerb} is {@link DestinationAction#getVerb() action.getVerb()} 215 * </li> 216 * </ol> 217 * <h3>Examples</h3> 218 * With the default settings (no {@link #getPermissionStringPrefix() permissionStringPrefix}), this might produce 219 * strings that look like the following: 220 * <pre> 221 * topic:TEST:create 222 * temp-queue:MyTempQueue:remove 223 * queue:ActiveMQ.Advisory.*:read 224 * </pre> 225 * If {@link #getPermissionStringPrefix() permissionStringPrefix} was set to {@code jms}, the above examples would 226 * look like this: 227 * <pre> 228 * jms:topic:TEST:create 229 * jms:temp-queue:MyTempQueue:remove 230 * jms:queue:ActiveMQ.Advisory.*:read 231 * </pre> 232 * 233 * @param dest the destination to inspect and convert to a {@link WildcardPermission} string. 234 * @param verb the behavior taken on the destination 235 * @return a {@link WildcardPermission} string that represents the specified {@code action}. 236 * @see #getPermissionStringPrefix() getPermissionStringPrefix() for more on why you might want to set this value 237 */ 238 protected String createPermissionString(ActiveMQDestination dest, String verb) { 239 if (dest.isComposite()) { 240 throw new IllegalArgumentException("Use createPermissionStrings for composite destinations."); 241 } 242 243 StringBuilder sb = new StringBuilder(); 244 245 if (permissionStringPrefix != null) { 246 sb.append(permissionStringPrefix); 247 if (!permissionStringPrefix.endsWith(":")) { 248 sb.append(":"); 249 } 250 } 251 252 if (dest.isTemporary()) { 253 sb.append("temp-"); 254 } 255 if (dest.isTopic()) { 256 sb.append("topic:"); 257 } else { 258 sb.append("queue:"); 259 } 260 261 sb.append(dest.getPhysicalName()); 262 sb.append(':'); 263 sb.append(verb); 264 265 return sb.toString(); 266 267 } 268 269 protected Permission createPermission(String permissionString) { 270 return new ActiveMQWildcardPermission(permissionString, isPermissionStringCaseSensitive()); 271 } 272}