blob: 23073e061386d8e9917efd227f7f5472141ebc5c [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.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;
}
}