| /* |
| * |
| * 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.qpid.server.security.access; |
| |
| import org.apache.qpid.framing.AMQShortString; |
| import org.apache.qpid.framing.QueueBindBody; |
| import org.apache.qpid.framing.QueueDeclareBody; |
| import org.apache.qpid.framing.ExchangeDeclareBody; |
| import org.apache.qpid.server.queue.AMQQueue; |
| import org.apache.qpid.server.exchange.Exchange; |
| |
| import java.util.*; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| public class PrincipalPermissions |
| { |
| |
| private static final Object CONSUME_QUEUES_KEY = new Object(); |
| private static final Object CONSUME_TEMPORARY_KEY = new Object(); |
| private static final Object CONSUME_OWN_QUEUES_ONLY_KEY = new Object(); |
| |
| private static final Object CREATE_QUEUES_KEY = new Object(); |
| private static final Object CREATE_EXCHANGES_KEY = new Object(); |
| |
| private static final Object CREATE_QUEUE_TEMPORARY_KEY = new Object(); |
| private static final Object CREATE_QUEUE_QUEUES_KEY = new Object(); |
| private static final Object CREATE_QUEUE_EXCHANGES_KEY = new Object(); |
| |
| private static final Object CREATE_QUEUE_EXCHANGES_TEMPORARY_KEY = new Object(); |
| private static final Object CREATE_QUEUE_EXCHANGES_ROUTINGKEYS_KEY = new Object(); |
| |
| private static final int PUBLISH_EXCHANGES_KEY = 0; |
| |
| private Map _permissions; |
| |
| private String _user; |
| |
| |
| public PrincipalPermissions(String user) |
| { |
| _user = user; |
| _permissions = new ConcurrentHashMap(); |
| } |
| |
| public void grant(Permission permission, Object... parameters) |
| { |
| switch (permission) |
| { |
| case ACCESS: |
| break; // This is a no-op as the existence of this PrincipalPermission object is scoped per VHost for ACCESS |
| case BIND: |
| break; // All the details are currently included in the create setup. |
| case CONSUME: // Parameters : AMQShortString queueName, Boolean Temporary, Boolean ownQueueOnly |
| Map consumeRights = (Map) _permissions.get(permission); |
| |
| if (consumeRights == null) |
| { |
| consumeRights = new ConcurrentHashMap(); |
| _permissions.put(permission, consumeRights); |
| } |
| |
| //if we have parametsre |
| if (parameters.length > 0) |
| { |
| AMQShortString queueName = (AMQShortString) parameters[0]; |
| Boolean temporary = (Boolean) parameters[1]; |
| Boolean ownQueueOnly = (Boolean) parameters[2]; |
| |
| if (temporary) |
| { |
| consumeRights.put(CONSUME_TEMPORARY_KEY, true); |
| } |
| else |
| { |
| consumeRights.put(CONSUME_TEMPORARY_KEY, false); |
| } |
| |
| if (ownQueueOnly) |
| { |
| consumeRights.put(CONSUME_OWN_QUEUES_ONLY_KEY, true); |
| } |
| else |
| { |
| consumeRights.put(CONSUME_OWN_QUEUES_ONLY_KEY, false); |
| } |
| |
| |
| LinkedList queues = (LinkedList) consumeRights.get(CONSUME_QUEUES_KEY); |
| if (queues == null) |
| { |
| queues = new LinkedList(); |
| consumeRights.put(CONSUME_QUEUES_KEY, queues); |
| } |
| |
| if (queueName != null) |
| { |
| queues.add(queueName); |
| } |
| } |
| |
| |
| break; |
| case CREATE: // Parameters : Boolean temporary, AMQShortString queueName |
| // , AMQShortString exchangeName , AMQShortString routingKey |
| // || AMQShortString exchangeName , AMQShortString Class |
| |
| Map createRights = (Map) _permissions.get(permission); |
| |
| if (createRights == null) |
| { |
| createRights = new ConcurrentHashMap(); |
| _permissions.put(permission, createRights); |
| |
| } |
| |
| //The existence of the empty map mean permission to all. |
| if (parameters.length == 0) |
| { |
| return; |
| } |
| |
| |
| if (parameters[0] instanceof Boolean) //Create Queue : |
| // Boolean temporary, [AMQShortString queueName, AMQShortString exchangeName , AMQShortString routingKey] |
| { |
| Boolean temporary = (Boolean) parameters[0]; |
| |
| AMQShortString queueName = parameters.length > 1 ? (AMQShortString) parameters[1] : null; |
| AMQShortString exchangeName = parameters.length > 2 ? (AMQShortString) parameters[2] : null; |
| //Set the routingkey to the specified value or the queueName if present |
| AMQShortString routingKey = parameters.length > 3 ? (AMQShortString) parameters[3] : queueName; |
| |
| // Get the queues map |
| Map create_queues = (Map) createRights.get(CREATE_QUEUES_KEY); |
| |
| if (create_queues == null) |
| { |
| create_queues = new ConcurrentHashMap(); |
| createRights.put(CREATE_QUEUES_KEY, create_queues); |
| } |
| |
| //Allow all temp queues to be created |
| create_queues.put(CREATE_QUEUE_TEMPORARY_KEY, temporary); |
| |
| //Create empty list of queues |
| Map create_queues_queues = (Map) create_queues.get(CREATE_QUEUE_QUEUES_KEY); |
| |
| if (create_queues_queues == null) |
| { |
| create_queues_queues = new ConcurrentHashMap(); |
| create_queues.put(CREATE_QUEUE_QUEUES_KEY, create_queues_queues); |
| } |
| |
| // We are granting CREATE rights to all temporary queues only |
| if (parameters.length == 1) |
| { |
| return; |
| } |
| |
| // if we have a queueName then we need to store any associated exchange / rk bindings |
| if (queueName != null) |
| { |
| Map queue = (Map) create_queues_queues.get(queueName); |
| if (queue == null) |
| { |
| queue = new ConcurrentHashMap(); |
| create_queues_queues.put(queueName, queue); |
| } |
| |
| if (exchangeName != null) |
| { |
| queue.put(exchangeName, routingKey); |
| } |
| |
| //If no exchange is specified then the presence of the queueName in the map says any exchange is ok |
| } |
| |
| // Store the exchange that we are being granted rights to. This will be used as part of binding |
| |
| //Lookup the list of exchanges |
| Map create_queues_exchanges = (Map) create_queues.get(CREATE_QUEUE_EXCHANGES_KEY); |
| |
| if (create_queues_exchanges == null) |
| { |
| create_queues_exchanges = new ConcurrentHashMap(); |
| create_queues.put(CREATE_QUEUE_EXCHANGES_KEY, create_queues_exchanges); |
| } |
| |
| //if we have an exchange |
| if (exchangeName != null) |
| { |
| //Retrieve the list of permitted exchanges. |
| Map exchanges = (Map) create_queues_exchanges.get(exchangeName); |
| |
| if (exchanges == null) |
| { |
| exchanges = new ConcurrentHashMap(); |
| create_queues_exchanges.put(exchangeName, exchanges); |
| } |
| |
| //Store the temporary setting CREATE_QUEUE_EXCHANGES_ROUTINGKEYS_KEY |
| exchanges.put(CREATE_QUEUE_EXCHANGES_TEMPORARY_KEY, temporary); |
| |
| //Store the binding details of queue/rk for this exchange. |
| if (queueName != null) |
| { |
| //Retrieve the list of permitted routingKeys. |
| Map rKeys = (Map) exchanges.get(exchangeName); |
| |
| if (rKeys == null) |
| { |
| rKeys = new ConcurrentHashMap(); |
| exchanges.put(CREATE_QUEUE_EXCHANGES_ROUTINGKEYS_KEY, rKeys); |
| } |
| |
| rKeys.put(queueName, routingKey); |
| } |
| } |
| } |
| else // Create Exchange : AMQShortString exchangeName , AMQShortString Class |
| { |
| Map create_exchanges = (Map) createRights.get(CREATE_EXCHANGES_KEY); |
| |
| if (create_exchanges == null) |
| { |
| create_exchanges = new ConcurrentHashMap(); |
| createRights.put(CREATE_EXCHANGES_KEY, create_exchanges); |
| } |
| |
| //Should perhaps error if parameters[0] is null; |
| AMQShortString exchangeName = parameters.length > 0 ? (AMQShortString) parameters[0] : null; |
| AMQShortString className = parameters.length > 1 ? (AMQShortString) parameters[1] : null; |
| |
| //Store the exchangeName / class mapping if the mapping is null |
| createRights.put(exchangeName, className); |
| } |
| break; |
| case DELETE: |
| break; |
| |
| case PUBLISH: // Parameters : Exchange exchange, AMQShortString routingKey |
| Map publishRights = (Map) _permissions.get(permission); |
| |
| if (publishRights == null) |
| { |
| publishRights = new ConcurrentHashMap(); |
| _permissions.put(permission, publishRights); |
| } |
| |
| if (parameters == null || parameters.length == 0) |
| { |
| //If we have no parameters then allow publish to all destinations |
| // this is signified by having a null value for publish_exchanges |
| } |
| else |
| { |
| Map publish_exchanges = (Map) publishRights.get(PUBLISH_EXCHANGES_KEY); |
| |
| if (publish_exchanges == null) |
| { |
| publish_exchanges = new ConcurrentHashMap(); |
| publishRights.put(PUBLISH_EXCHANGES_KEY, publish_exchanges); |
| } |
| |
| |
| HashSet routingKeys = (HashSet) publish_exchanges.get(parameters[0]); |
| |
| // Check to see if we have a routing key |
| if (parameters.length == 2) |
| { |
| if (routingKeys == null) |
| { |
| routingKeys = new HashSet<AMQShortString>(); |
| } |
| //Add routing key to permitted publish destinations |
| routingKeys.add(parameters[1]); |
| } |
| |
| // Add the updated routingkey list or null if all values allowed |
| publish_exchanges.put(parameters[0], routingKeys); |
| } |
| break; |
| case PURGE: |
| break; |
| case UNBIND: |
| break; |
| } |
| |
| } |
| |
| public boolean authorise(Permission permission, Object... parameters) |
| { |
| |
| switch (permission) |
| { |
| case ACCESS: |
| return true; // This is here for completeness but the SimpleXML ACLManager never calls it. |
| // The existence of this user specific PP can be validated in the map SimpleXML maintains. |
| case BIND: // Parameters : QueueBindMethod , Exchange , AMQQueue, AMQShortString routingKey |
| |
| Exchange exchange = (Exchange) parameters[1]; |
| |
| AMQQueue bind_queueName = (AMQQueue) parameters[2]; |
| AMQShortString routingKey = (AMQShortString) parameters[3]; |
| |
| //Get all Create Rights for this user |
| Map bindCreateRights = (Map) _permissions.get(Permission.CREATE); |
| |
| //Look up the Queue Creation Rights |
| Map bind_create_queues = (Map) bindCreateRights.get(CREATE_QUEUES_KEY); |
| |
| //Lookup the list of queues |
| Map bind_create_queues_queues = (Map) bindCreateRights.get(CREATE_QUEUE_QUEUES_KEY); |
| |
| // Check and see if we have a queue white list to check |
| if (bind_create_queues_queues != null) |
| { |
| //There a white list for queues |
| Map exchangeDetails = (Map) bind_create_queues_queues.get(bind_queueName); |
| |
| if (exchangeDetails == null) //Then all queue can be bound to all exchanges. |
| { |
| return true; |
| } |
| |
| // Check to see if we have a white list of routingkeys to check |
| Map rkeys = (Map) exchangeDetails.get(exchange.getName()); |
| |
| // if keys is null then any rkey is allowed on this exchange |
| if (rkeys == null) |
| { |
| // There is no routingkey white list |
| return true; |
| } |
| else |
| { |
| // We have routingKeys so a match must be found to allowed binding |
| Iterator keys = rkeys.keySet().iterator(); |
| |
| boolean matched = false; |
| while (keys.hasNext() && !matched) |
| { |
| AMQShortString rkey = (AMQShortString) keys.next(); |
| if (rkey.endsWith("*")) |
| { |
| matched = routingKey.startsWith(rkey.subSequence(0, rkey.length() - 1).toString()); |
| } |
| else |
| { |
| matched = routingKey.equals(rkey); |
| } |
| } |
| |
| |
| return matched; |
| } |
| |
| |
| } |
| else |
| { |
| //There a is no white list for queues |
| |
| // So can allow all queues to be bound |
| // but we should first check and see if we have a temp queue and validate that we are allowed |
| // to bind temp queues. |
| |
| //Check to see if we have a temporary queue |
| if (bind_queueName.isAutoDelete()) |
| { |
| // Check and see if we have an exchange white list. |
| Map bind_exchanges = (Map) bind_create_queues.get(CREATE_QUEUE_EXCHANGES_KEY); |
| |
| // If the exchange exists then we must check to see if temporary queues are allowed here |
| if (bind_exchanges != null) |
| { |
| // Check to see if the requested exchange is allowed. |
| Map exchangeDetails = (Map) bind_exchanges.get(exchange.getName()); |
| |
| return (Boolean) exchangeDetails.get(CREATE_QUEUE_EXCHANGES_TEMPORARY_KEY); |
| } |
| |
| //no white list so all allowed, drop through to return true below. |
| } |
| |
| // not a temporary queue and no white list so all allowed. |
| return true; |
| } |
| |
| case CREATE:// Paramters : QueueDeclareBody || ExchangeDeclareBody |
| |
| Map createRights = (Map) _permissions.get(permission); |
| |
| // If there are no create rights then deny request |
| if (createRights == null) |
| { |
| return false; |
| } |
| |
| if (parameters.length == 1) |
| { |
| if (parameters[0] instanceof QueueDeclareBody) |
| { |
| QueueDeclareBody body = (QueueDeclareBody) parameters[0]; |
| |
| //Look up the Queue Creation Rights |
| Map create_queues = (Map) createRights.get(CREATE_QUEUES_KEY); |
| |
| //Lookup the list of queues allowed to be created |
| Map create_queues_queues = (Map) create_queues.get(CREATE_QUEUE_QUEUES_KEY); |
| |
| |
| AMQShortString queueName = body.getQueue(); |
| |
| |
| if (body.getAutoDelete())// we have a temporary queue |
| { |
| return (Boolean) create_queues.get(CREATE_QUEUE_TEMPORARY_KEY); |
| } |
| else |
| { |
| // If there is a white list then check |
| return create_queues_queues == null || create_queues_queues.containsKey(queueName); |
| } |
| |
| } |
| else if (parameters[0] instanceof ExchangeDeclareBody) |
| { |
| ExchangeDeclareBody body = (ExchangeDeclareBody) parameters[0]; |
| |
| AMQShortString exchangeName = body.getExchange(); |
| |
| Map create_exchanges = (Map) createRights.get(CREATE_EXCHANGES_KEY); |
| |
| // If the exchange list is doesn't exist then all is allowed else check the valid exchanges |
| return create_exchanges == null || create_exchanges.containsKey(exchangeName); |
| } |
| } |
| break; |
| case CONSUME: // Parameters : AMQQueue |
| |
| if (parameters.length == 1 && parameters[0] instanceof AMQQueue) |
| { |
| AMQQueue queue = ((AMQQueue) parameters[0]); |
| Map queuePermissions = (Map) _permissions.get(permission); |
| |
| List queues = (List) queuePermissions.get(CONSUME_QUEUES_KEY); |
| |
| Boolean temporayQueues = (Boolean) queuePermissions.get(CONSUME_TEMPORARY_KEY); |
| Boolean ownQueuesOnly = (Boolean) queuePermissions.get(CONSUME_OWN_QUEUES_ONLY_KEY); |
| |
| // If user is allowed to publish to temporary queues and this is a temp queue then allow it. |
| if (temporayQueues) |
| { |
| if (queue.isAutoDelete()) |
| // This will allow consumption from any temporary queue including ones not owned by this user. |
| // Of course the exclusivity will not be broken. |
| { |
| // if not limited to ownQueuesOnly then ok else check queue Owner. |
| return !ownQueuesOnly || queue.getOwner().equals(_user); |
| } |
| else |
| { |
| return false; |
| } |
| } |
| |
| // if queues are white listed then ensure it is ok |
| if (queues != null) |
| { |
| // if no queues are listed then ALL are ok othereise it must be specified. |
| if (ownQueuesOnly) |
| { |
| if (queue.getOwner().equals(_user)) |
| { |
| return queues.size() == 0 || queues.contains(queue.getName()); |
| } |
| else |
| { |
| return false; |
| } |
| } |
| |
| // If we are |
| return queues.size() == 0 || queues.contains(queue.getName()); |
| } |
| } |
| |
| // Can't authenticate without the right parameters |
| return false; |
| case DELETE: |
| break; |
| |
| case PUBLISH: // Parameters : Exchange exchange, AMQShortString routingKey |
| Map publishRights = (Map) _permissions.get(permission); |
| |
| if (publishRights == null) |
| { |
| return false; |
| } |
| |
| Map exchanges = (Map) publishRights.get(PUBLISH_EXCHANGES_KEY); |
| |
| // Having no exchanges listed gives full publish rights to all exchanges |
| if (exchanges == null) |
| { |
| return true; |
| } |
| // Otherwise exchange must be listed in the white list |
| |
| // If the map doesn't have the exchange then it isn't allowed |
| if (!exchanges.containsKey(parameters[0])) |
| { |
| return false; |
| } |
| else |
| { |
| |
| // Get valid routing keys |
| HashSet routingKeys = (HashSet) exchanges.get(parameters[0]); |
| |
| // Having no routingKeys in the map then all are allowed. |
| if (routingKeys == null) |
| { |
| return true; |
| } |
| else |
| { |
| // We have routingKeys so a match must be found to allowed binding |
| Iterator keys = routingKeys.iterator(); |
| |
| |
| AMQShortString publishRKey = (AMQShortString)parameters[1]; |
| |
| boolean matched = false; |
| while (keys.hasNext() && !matched) |
| { |
| AMQShortString rkey = (AMQShortString) keys.next(); |
| |
| if (rkey.endsWith("*")) |
| { |
| matched = publishRKey.startsWith(rkey.subSequence(0, rkey.length() - 1)); |
| } |
| else |
| { |
| matched = publishRKey.equals(rkey); |
| } |
| } |
| return matched; |
| } |
| } |
| case PURGE: |
| break; |
| case UNBIND: |
| break; |
| |
| } |
| |
| return false; |
| } |
| } |