diff options
author | Aidan Skinner <aidan@apache.org> | 2009-02-09 17:03:57 +0000 |
---|---|---|
committer | Aidan Skinner <aidan@apache.org> | 2009-02-09 17:03:57 +0000 |
commit | d7ba7d6fdf756080b2862a48a892526ef40e163f (patch) | |
tree | 5d2f5b703c859ad772c4440c97a272ee82b1aee0 | |
parent | 08d3f7bb40f0c37a0d6494379f1fa4c525a78a05 (diff) | |
download | qpid-python-d7ba7d6fdf756080b2862a48a892526ef40e163f.tar.gz |
QPID-1626: Add per-virtualhost authorization plugins.
PluginManager: add support for getting ACLPluginFactories from OSGi and the ones we already know about.
*ApplicationRegistry*: return an ACLManager, not an ACLPlugin from getAccessManager.
ACLManager: use PluginManager to get all the available plugins. When being asked to authorize a particular request, hold a vote amongst all the plugins as to whether to allow or deny access.
ACLPlugin: return a ALLOWED/DENIED/ABSTAIN vote result. Fix typo in method name.
ACLPluginFactory: Factory class for ACLPlugins.
AccessResult: just use class SimpleName instead of getPluginName
PrincipalPermissions: return AuthzResult instead of boolean. Might want to maek use of Abstain for things it doesn't actually acare about instead of defaulting to Allowed.
AllowAll, DenyAll, BasicACLPlugin, SimpleXML: add Factory, return AuthzResult instead of boolean.
VirtualHost: get a new ACLManager and configure it with the virtualhost security section. Ensure that old config files which have the access_control_list outside of the main security.access section continue to work.
MockPluginManager: add mock class for tests
PluginTest: not having any plugins now returns an empty set, not null
MockAMQQueue: support name attribute
ACLManagerTest: tests for ACLManager class
ExchangeDenier, QueueDenier: new test classes for ACLManagerTest
PrincipalPermissionsTest: check for correct return result, not true/false anymore
Move plugin configuration to <security> section, not <security><access>
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@742626 13f79535-47bb-0310-9956-ffa450edef68
25 files changed, 807 insertions, 243 deletions
diff --git a/qpid/java/broker/etc/acl.config.xml b/qpid/java/broker/etc/acl.config.xml index 614ecf0a88..a2b723fc63 100644 --- a/qpid/java/broker/etc/acl.config.xml +++ b/qpid/java/broker/etc/acl.config.xml @@ -105,7 +105,6 @@ <access> <class>org.apache.qpid.server.security.access.plugins.SimpleXML</class> </access> - <access_control_list> <!-- This section grants pubish rights to an exchange + routing key pair --> <publish> diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java index 9191ecf6ed..1b7919e8b7 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java @@ -30,6 +30,11 @@ import org.apache.felix.framework.cache.BundleCache; import org.apache.felix.framework.util.FelixConstants; import org.apache.felix.framework.util.StringMap; import org.apache.qpid.server.exchange.ExchangeType; +import org.apache.qpid.server.security.access.ACLPlugin; +import org.apache.qpid.server.security.access.ACLPluginFactory; +import org.apache.qpid.server.security.access.plugins.AllowAll; +import org.apache.qpid.server.security.access.plugins.DenyAll; +import org.apache.qpid.server.security.access.plugins.SimpleXML; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleException; import org.osgi.util.tracker.ServiceTracker; @@ -46,8 +51,10 @@ public class PluginManager private Felix _felix = null; private ServiceTracker _exchangeTracker = null; + private ServiceTracker _securityTracker = null; private Activator _activator = null; private boolean _empty; + private Map<String, ACLPluginFactory> _securityPlugins; public PluginManager(String plugindir) throws Exception { @@ -115,8 +122,13 @@ public class PluginManager try { _felix.start(); + _exchangeTracker = new ServiceTracker(_activator.getContext(), ExchangeType.class.getName(), null); _exchangeTracker.open(); + + _securityTracker = new ServiceTracker(_activator.getContext(), ACLPlugin.class.getName(), null); + _exchangeTracker.open(); + } catch (BundleException e) { @@ -124,22 +136,37 @@ public class PluginManager } } - public Map<String, ExchangeType<?>> getExchanges() - { - if (_empty) - { - return null; - } - Map<String, ExchangeType<?>>exchanges = new HashMap<String, ExchangeType<?>>(); - for (Object service : _exchangeTracker.getServices()) + private <type> Map<String, type> getServices(ServiceTracker tracker) + { + Map<String, type>exchanges = new HashMap<String, type>(); + + if (tracker != null) { - if (service instanceof ExchangeType<?>) + for (Object service : tracker.getServices()) { - exchanges.put(service.getClass().getName(), (ExchangeType<?>) service); + exchanges.put(service.getClass().getName(), (type) service); } } return exchanges; } + + public Map<String, ExchangeType<?>> getExchanges() + { + return getServices(_exchangeTracker); + } + + public Map<String, ACLPluginFactory> getSecurityPlugins() + { + if (_securityPlugins == null) + { + _securityPlugins = getServices(_securityTracker); + // A little gross that we have to add them here, but not all the plugins are OSGIfied + _securityPlugins.put(SimpleXML.class.getName(), SimpleXML.FACTORY); + _securityPlugins.put(AllowAll.class.getName(), AllowAll.FACTORY); + _securityPlugins.put(DenyAll.class.getName(), DenyAll.FACTORY); + } + return _securityPlugins; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java index c9c3acf61b..02124a3737 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java @@ -67,7 +67,7 @@ public abstract class ApplicationRegistry implements IApplicationRegistry protected VirtualHostRegistry _virtualHostRegistry; - protected ACLPlugin _accessManager; + protected ACLManager _accessManager; protected PrincipalDatabaseManager _databaseManager; @@ -285,9 +285,9 @@ public abstract class ApplicationRegistry implements IApplicationRegistry return _virtualHostRegistry; } - public ACLPlugin getAccessManager() + public ACLManager getAccessManager() { - return _accessManager; + return new ACLManager(_configuration, _pluginManager); } public ManagedObjectRegistry getManagedObjectRegistry() diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java index a555b72dcf..c34c4bf80a 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java @@ -94,8 +94,10 @@ public class ConfigurationFileApplicationRegistry extends ApplicationRegistry _virtualHostRegistry = new VirtualHostRegistry(); - _accessManager = ACLManager.loadACLManager("default", _configuration); + _pluginManager = new PluginManager(_configuration.getString("plugin-directory")); + _accessManager = new ACLManager(_configuration, _pluginManager); + _databaseManager = new ConfigurationFilePrincipalDatabaseManager(_configuration); _authenticationManager = new PrincipalDatabaseAuthenticationManager(null, null); @@ -104,8 +106,6 @@ public class ConfigurationFileApplicationRegistry extends ApplicationRegistry _managedObjectRegistry.start(); - _pluginManager = new PluginManager(_configuration.getString("plugin-directory")); - initialiseVirtualHosts(); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java index 597ef042f9..e68dca285c 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java @@ -28,6 +28,7 @@ import org.apache.qpid.server.management.ManagedObjectRegistry; import org.apache.qpid.server.plugins.PluginManager; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; import org.apache.qpid.server.security.auth.database.PrincipalDatabaseManager; +import org.apache.qpid.server.security.access.ACLManager; import org.apache.qpid.server.security.access.ACLPlugin; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; import org.apache.mina.common.IoAcceptor; @@ -74,7 +75,7 @@ public interface IApplicationRegistry VirtualHostRegistry getVirtualHostRegistry(); - ACLPlugin getAccessManager(); + ACLManager getAccessManager(); PluginManager getPluginManager(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLManager.java index 539f32a732..356ee815dd 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLManager.java @@ -20,142 +20,300 @@ */ package org.apache.qpid.server.security.access; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.ConfigurationException; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.security.access.plugins.DenyAll; -import org.apache.qpid.configuration.PropertyUtils; import org.apache.log4j.Logger; - -import java.util.List; -import java.lang.reflect.Method; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.plugins.PluginManager; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.security.access.ACLPlugin.AuthzResult; +import org.apache.qpid.server.security.access.plugins.SimpleXML; +import org.apache.qpid.server.virtualhost.VirtualHost; public class ACLManager { private static final Logger _logger = Logger.getLogger(ACLManager.class); + private PluginManager _pluginManager; + private Map<String, ACLPluginFactory> _allSecurityPlugins = new HashMap<String, ACLPluginFactory>(); + private Map<String, ACLPlugin> _globalPlugins = new HashMap<String, ACLPlugin>(); + private Map<String, ACLPlugin> _hostPlugins = new HashMap<String, ACLPlugin>(); - public static ACLPlugin loadACLManager(String name, Configuration hostConfig) throws ConfigurationException + public ACLManager(Configuration configuration, PluginManager manager) { - ACLPlugin aclPlugin = ApplicationRegistry.getInstance().getAccessManager(); + this(configuration, manager, null); + } - if (hostConfig == null) - { - _logger.warn("No Configuration specified. Using default ACLPlugin '" + aclPlugin.getPluginName() - + "' for VirtualHost:'" + name + "'"); - return aclPlugin; - } + public ACLManager(Configuration configuration, PluginManager manager, ACLPluginFactory securityPlugin) + { + _pluginManager = manager; - String accessClass = hostConfig.getString("security.access.class"); - if (accessClass == null) + if (manager == null) // No plugin manager, no plugins { - - _logger.warn("No ACL Plugin specified. Using default ACL Plugin '" + aclPlugin.getPluginName() + - "' for VirtualHost:'" + name + "'"); - return aclPlugin; + return; } - Object o; - try + _allSecurityPlugins = _pluginManager.getSecurityPlugins(); + if (securityPlugin != null) { - o = Class.forName(accessClass).newInstance(); - } - catch (Exception e) - { - throw new ConfigurationException("Error initialising ACL: " + e, e); + _allSecurityPlugins.put(securityPlugin.getClass().getName(), securityPlugin); } - if (!(o instanceof ACLPlugin)) - { - throw new ConfigurationException("ACL Plugins must implement the ACLPlugin interface"); - } + _globalPlugins = configurePlugins(configuration); + } - initialiseAccessControl((ACLPlugin) o, hostConfig); - aclPlugin = getManager((ACLPlugin) o); - if (_logger.isInfoEnabled()) + public void configureHostPlugins(Configuration hostConfig) + { + _hostPlugins = configurePlugins(hostConfig); + } + + public Map<String, ACLPlugin> configurePlugins(Configuration configuration) + { + Configuration securityConfig = configuration.subset("security"); + Map<String, ACLPlugin> plugins = new HashMap<String, ACLPlugin>(); + Iterator keys = securityConfig.getKeys(); + Collection<String> handledTags = new HashSet(); + while (keys.hasNext()) { - _logger.info("Initialised ACL Plugin '" + aclPlugin.getPluginName() - + "' for virtualhost '" + name + "' successfully"); + // Splitting the string is necessary here because of the way that getKeys() returns only + // bottom level children + String tag = ((String) keys.next()).split("\\.", 2)[0]; + + if (!handledTags.contains(tag)) + { + for (ACLPluginFactory plugin : _allSecurityPlugins.values()) + { + if (plugin.supportsTag(tag)) + { + _logger.warn("Plugin handling security section "+tag+" is "+plugin.getClass().getSimpleName()); + handledTags.add(tag); + plugins.put(plugin.getClass().getName(), plugin.newInstance(securityConfig)); + } + } + } + if (!handledTags.contains(tag)) + { + _logger.warn("No plugin handled security section "+tag); + } } + return plugins; + } - return aclPlugin; + public static Logger getLogger() + { + return _logger; } - - private static void initialiseAccessControl(ACLPlugin accessManager, Configuration config) - throws ConfigurationException + private abstract class AccessCheck { - //First provide the ACLPlugin with the host configuration + abstract AuthzResult allowed(ACLPlugin plugin); + } - accessManager.setConfiguaration(config); + private boolean checkAllPlugins(AccessCheck checker) + { + AuthzResult result = AuthzResult.ABSTAIN; + HashMap<String, ACLPlugin> remainingPlugins = new HashMap<String, ACLPlugin>(); + remainingPlugins.putAll(_globalPlugins); + for (Entry<String, ACLPlugin> plugin : _hostPlugins.entrySet()) + { + result = checker.allowed(plugin.getValue()); + if (result == AuthzResult.DENIED) + { + // Something vetoed the access, we're done + return false; + } + else if (result == AuthzResult.ALLOWED) + { + // Remove plugin from global check list since + // host allow overrides global allow + remainingPlugins.remove(plugin.getKey()); + } + } + + for (ACLPlugin plugin : remainingPlugins.values()) + { + result = checker.allowed(plugin); + if (result == AuthzResult.DENIED) + { + return false; + } + } + return true; + } - //Provide additional attribute customisation. - String baseName = "security.access.attributes.attribute."; - List<String> argumentNames = config.getList(baseName + "name"); - List<String> argumentValues = config.getList(baseName + "value"); - for (int i = 0; i < argumentNames.size(); i++) + public boolean authoriseBind(final AMQProtocolSession session, final Exchange exch, final AMQQueue queue, + final AMQShortString routingKey) + { + return checkAllPlugins(new AccessCheck() { - String argName = argumentNames.get(i); - if (argName == null || argName.length() == 0) + + @Override + AuthzResult allowed(ACLPlugin plugin) { - throw new ConfigurationException("Access Control argument names must have length >= 1 character"); + return plugin.authoriseBind(session, exch, queue, routingKey); } - if (Character.isLowerCase(argName.charAt(0))) + + }); + } + + public boolean authoriseConnect(final AMQProtocolSession session, final VirtualHost virtualHost) + { + return checkAllPlugins(new AccessCheck() + { + + @Override + AuthzResult allowed(ACLPlugin plugin) { - argName = Character.toUpperCase(argName.charAt(0)) + argName.substring(1); + return plugin.authoriseConnect(session, virtualHost); } - String methodName = "set" + argName; - Method method = null; - try + + }); + } + + public boolean authoriseConsume(final AMQProtocolSession session, final boolean noAck, final AMQQueue queue) + { + return checkAllPlugins(new AccessCheck() + { + + @Override + AuthzResult allowed(ACLPlugin plugin) { - method = accessManager.getClass().getMethod(methodName, String.class); + return plugin.authoriseConsume(session, noAck, queue); } - catch (NoSuchMethodException e) + + }); + } + + public boolean authoriseConsume(final AMQProtocolSession session, final boolean exclusive, final boolean noAck, + final boolean noLocal, final boolean nowait, final AMQQueue queue) + { + return checkAllPlugins(new AccessCheck() + { + + @Override + AuthzResult allowed(ACLPlugin plugin) { - //do nothing as method will be null + return plugin.authoriseConsume(session, exclusive, noAck, noLocal, nowait, queue); } - if (method == null) + }); + } + + public boolean authoriseCreateExchange(final AMQProtocolSession session, final boolean autoDelete, + final boolean durable, final AMQShortString exchangeName, final boolean internal, final boolean nowait, + final boolean passive, final AMQShortString exchangeType) + { + return checkAllPlugins(new AccessCheck() + { + + @Override + AuthzResult allowed(ACLPlugin plugin) { - throw new ConfigurationException("No method " + methodName + " found in class " + accessManager.getClass() + - " hence unable to configure access control. The method must be public and " + - "have a single String argument with a void return type"); + return plugin.authoriseCreateExchange(session, autoDelete, durable, exchangeName, internal, nowait, + passive, exchangeType); } - try + + }); + } + + public boolean authoriseCreateQueue(final AMQProtocolSession session, final boolean autoDelete, + final boolean durable, final boolean exclusive, final boolean nowait, final boolean passive, + final AMQShortString queue) + { + return checkAllPlugins(new AccessCheck() + { + + @Override + AuthzResult allowed(ACLPlugin plugin) { - method.invoke(accessManager, PropertyUtils.replaceProperties(argumentValues.get(i))); + return plugin.authoriseCreateQueue(session, autoDelete, durable, exclusive, nowait, passive, queue); } - catch (Exception e) + + }); + } + + public boolean authoriseDelete(final AMQProtocolSession session, final AMQQueue queue) + { + return checkAllPlugins(new AccessCheck() + { + + @Override + AuthzResult allowed(ACLPlugin plugin) { - ConfigurationException ce = new ConfigurationException(e.getMessage(), e.getCause()); - ce.initCause(e); - throw ce; + return plugin.authoriseDelete(session, queue); } - } + + }); } + public boolean authoriseDelete(final AMQProtocolSession session, final Exchange exchange) + { + return checkAllPlugins(new AccessCheck() + { + + @Override + AuthzResult allowed(ACLPlugin plugin) + { + return plugin.authoriseDelete(session, exchange); + } - private static ACLPlugin getManager(ACLPlugin manager) + }); + } + + public boolean authorisePublish(final AMQProtocolSession session, final boolean immediate, final boolean mandatory, + final AMQShortString routingKey, final Exchange e) { - if (manager == null) + return checkAllPlugins(new AccessCheck() { - if (ApplicationRegistry.getInstance().getAccessManager() == null) + + @Override + AuthzResult allowed(ACLPlugin plugin) { - return new DenyAll(); + return plugin.authorisePublish(session, immediate, mandatory, routingKey, e); } - else + + }); + } + + public boolean authorisePurge(final AMQProtocolSession session, final AMQQueue queue) + { + return checkAllPlugins(new AccessCheck() + { + + @Override + AuthzResult allowed(ACLPlugin plugin) { - return ApplicationRegistry.getInstance().getAccessManager(); + return plugin.authorisePurge(session, queue); } - } - else + + }); + } + + public boolean authoriseUnbind(final AMQProtocolSession session, final Exchange exch, + final AMQShortString routingKey, final AMQQueue queue) + { + return checkAllPlugins(new AccessCheck() { - return manager; - } + + @Override + AuthzResult allowed(ACLPlugin plugin) + { + return plugin.authoriseUnbind(session, exch, routingKey, queue); + } + + }); } - public static Logger getLogger() + public void addHostPlugin(ACLPlugin aclPlugin) { - return _logger; + _hostPlugins.put(aclPlugin.getClass().getName(), aclPlugin); } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPlugin.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPlugin.java index 164fbad911..ca760f3360 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPlugin.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPlugin.java @@ -29,36 +29,41 @@ import org.apache.qpid.server.virtualhost.VirtualHost; public interface ACLPlugin { - String getPluginName(); + public enum AuthzResult + { + ALLOWED, + DENIED, + ABSTAIN + } - void setConfiguaration(Configuration config); + void setConfiguration(Configuration config); // These return true if the plugin thinks the action should be allowed, and false if not. - boolean authoriseBind(AMQProtocolSession session, Exchange exch, AMQQueue queue, AMQShortString routingKey); + AuthzResult authoriseBind(AMQProtocolSession session, Exchange exch, AMQQueue queue, AMQShortString routingKey); - boolean authoriseCreateExchange(AMQProtocolSession session, boolean autoDelete, boolean durable, + AuthzResult authoriseCreateExchange(AMQProtocolSession session, boolean autoDelete, boolean durable, AMQShortString exchangeName, boolean internal, boolean nowait, boolean passive, AMQShortString exchangeType); - boolean authoriseCreateQueue(AMQProtocolSession session, boolean autoDelete, boolean durable, boolean exclusive, + AuthzResult authoriseCreateQueue(AMQProtocolSession session, boolean autoDelete, boolean durable, boolean exclusive, boolean nowait, boolean passive, AMQShortString queue); - boolean authoriseConnect(AMQProtocolSession session, VirtualHost virtualHost); + AuthzResult authoriseConnect(AMQProtocolSession session, VirtualHost virtualHost); - boolean authoriseConsume(AMQProtocolSession session, boolean noAck, AMQQueue queue); + AuthzResult authoriseConsume(AMQProtocolSession session, boolean noAck, AMQQueue queue); - boolean authoriseConsume(AMQProtocolSession session, boolean exclusive, boolean noAck, boolean noLocal, + AuthzResult authoriseConsume(AMQProtocolSession session, boolean exclusive, boolean noAck, boolean noLocal, boolean nowait, AMQQueue queue); - boolean authoriseDelete(AMQProtocolSession session, AMQQueue queue); + AuthzResult authoriseDelete(AMQProtocolSession session, AMQQueue queue); - boolean authoriseDelete(AMQProtocolSession session, Exchange exchange); + AuthzResult authoriseDelete(AMQProtocolSession session, Exchange exchange); - boolean authorisePublish(AMQProtocolSession session, boolean immediate, boolean mandatory, + AuthzResult authorisePublish(AMQProtocolSession session, boolean immediate, boolean mandatory, AMQShortString routingKey, Exchange e); - boolean authorisePurge(AMQProtocolSession session, AMQQueue queue); + AuthzResult authorisePurge(AMQProtocolSession session, AMQQueue queue); - boolean authoriseUnbind(AMQProtocolSession session, Exchange exch, AMQShortString routingKey, AMQQueue queue); + AuthzResult authoriseUnbind(AMQProtocolSession session, Exchange exch, AMQShortString routingKey, AMQQueue queue); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPluginFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPluginFactory.java new file mode 100644 index 0000000000..aee6af93d0 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPluginFactory.java @@ -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.qpid.server.security.access; + +import org.apache.commons.configuration.Configuration; + +public interface ACLPluginFactory +{ + + public boolean supportsTag(String name); + + public ACLPlugin newInstance(Configuration config); + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java index 86f155d862..d722da4ae0 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java @@ -33,12 +33,12 @@ public class AccessResult public AccessResult(ACLPlugin authorizer, AccessStatus status) { _status = status; - _authorizer = authorizer.getPluginName(); + _authorizer = authorizer.getClass().getSimpleName(); } public void setAuthorizer(ACLPlugin authorizer) { - _authorizer += authorizer.getPluginName(); + _authorizer += authorizer.getClass().getSimpleName(); } public String getAuthorizer() @@ -58,7 +58,7 @@ public class AccessResult public void addAuthorizer(ACLPlugin accessManager) { - _authorizer = accessManager.getPluginName() + "->" + _authorizer; + _authorizer = accessManager.getClass().getSimpleName() + "->" + _authorizer; } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/AuthorizationManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/AuthorizationManager.java new file mode 100644 index 0000000000..9527120f30 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/AuthorizationManager.java @@ -0,0 +1,6 @@ +package org.apache.qpid.server.security.access; + +public class AuthorizationManager +{ + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalPermissions.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalPermissions.java index 00c63ede7c..35b76bcf32 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalPermissions.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalPermissions.java @@ -25,6 +25,7 @@ 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.security.access.ACLPlugin.AuthzResult; import org.apache.qpid.server.exchange.Exchange; import java.util.*; @@ -336,13 +337,13 @@ public class PrincipalPermissions * PURGE: none * UNBIND: none */ - public boolean authorise(Permission permission, Object... parameters) + public AuthzResult authorise(Permission permission, Object... parameters) { switch (permission) { case ACCESS: - return true; // This is here for completeness but the SimpleXML ACLManager never calls it. + return AuthzResult.ALLOWED; // 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 @@ -368,7 +369,7 @@ public class PrincipalPermissions if (exchangeDetails == null) //Then all queue can be bound to all exchanges. { - return true; + return AuthzResult.ALLOWED; } // Check to see if we have a white list of routingkeys to check @@ -378,7 +379,7 @@ public class PrincipalPermissions if (rkeys == null) { // There is no routingkey white list - return true; + return AuthzResult.ALLOWED; } else { @@ -400,7 +401,7 @@ public class PrincipalPermissions } - return matched; + return (matched) ? AuthzResult.ALLOWED : AuthzResult.DENIED; } @@ -425,14 +426,14 @@ public class PrincipalPermissions // 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); + return ((Boolean) exchangeDetails.get(CREATE_QUEUE_EXCHANGES_TEMPORARY_KEY)) ? AuthzResult.ALLOWED : AuthzResult.DENIED; } //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; + return AuthzResult.ALLOWED; } case CREATEQUEUE:// Parameters : boolean autodelete, AMQShortString name @@ -442,7 +443,7 @@ public class PrincipalPermissions // If there are no create rights then deny request if (createRights == null) { - return false; + return AuthzResult.DENIED; } //Look up the Queue Creation Rights @@ -457,12 +458,20 @@ public class PrincipalPermissions if (autoDelete)// we have a temporary queue { - return (Boolean) create_queues.get(CREATE_QUEUE_TEMPORARY_KEY); + return ((Boolean) create_queues.get(CREATE_QUEUE_TEMPORARY_KEY)) ? AuthzResult.ALLOWED : AuthzResult.DENIED; } else { // If there is a white list then check - return create_queues_queues == null || create_queues_queues.containsKey(queueName); + if (create_queues_queues == null || create_queues_queues.containsKey(queueName)) + { + return AuthzResult.ALLOWED; + } + else + { + return AuthzResult.DENIED; + } + } case CREATEEXCHANGE: Map rights = (Map) _permissions.get(permission); @@ -471,7 +480,14 @@ public class PrincipalPermissions // If the exchange list is doesn't exist then all is allowed else // check the valid exchanges - return rights == null || rights.containsKey(exchangeName); + if (rights == null || rights.containsKey(exchangeName)) + { + return AuthzResult.ALLOWED; + } + else + { + return AuthzResult.DENIED; + } case CONSUME: // Parameters : AMQQueue if (parameters.length == 1 && parameters[0] instanceof AMQQueue) @@ -492,11 +508,11 @@ public class PrincipalPermissions // 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); + return (!ownQueuesOnly || queue.getOwner().equals(_user)) ? AuthzResult.ALLOWED : AuthzResult.DENIED; } else { - return false; + return AuthzResult.DENIED; } } @@ -508,21 +524,21 @@ public class PrincipalPermissions { if (queue.getOwner().equals(_user)) { - return queues.size() == 0 || queues.contains(queue.getName()); + return (queues.size() == 0 || queues.contains(queue.getName())) ? AuthzResult.ALLOWED : AuthzResult.DENIED; } else { - return false; + return AuthzResult.DENIED; } } // If we are - return queues.size() == 0 || queues.contains(queue.getName()); + return (queues.size() == 0 || queues.contains(queue.getName())) ? AuthzResult.ALLOWED : AuthzResult.DENIED; } } // Can't authenticate without the right parameters - return false; + return AuthzResult.DENIED; case DELETE: break; @@ -531,7 +547,7 @@ public class PrincipalPermissions if (publishRights == null) { - return false; + return AuthzResult.DENIED; } Map exchanges = (Map) publishRights.get(PUBLISH_EXCHANGES_KEY); @@ -539,14 +555,14 @@ public class PrincipalPermissions // Having no exchanges listed gives full publish rights to all exchanges if (exchanges == null) { - return true; + return AuthzResult.ALLOWED; } // 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(((Exchange) parameters[0]).getName())) { - return false; + return AuthzResult.DENIED; } else { @@ -557,7 +573,7 @@ public class PrincipalPermissions // Having no routingKeys in the map then all are allowed. if (routingKeys == null) { - return true; + return AuthzResult.ALLOWED; } else { @@ -581,7 +597,7 @@ public class PrincipalPermissions matched = publishRKey.equals(rkey); } } - return matched; + return (matched) ? AuthzResult.ALLOWED : AuthzResult.DENIED; } } case PURGE: @@ -591,6 +607,6 @@ public class PrincipalPermissions } - return false; + return AuthzResult.DENIED; } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java index f78c9a2e16..4af178574b 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java @@ -21,24 +21,34 @@ package org.apache.qpid.server.security.access.plugins; import org.apache.commons.configuration.Configuration; +import org.apache.qpid.server.security.access.ACLPlugin; +import org.apache.qpid.server.security.access.ACLPluginFactory; public class AllowAll extends BasicACLPlugin { - public String getPluginName() + public static final ACLPluginFactory FACTORY = new ACLPluginFactory() { - return "AllowAll"; - } + public boolean supportsTag(String name) + { + return false; + } - public void setConfiguaration(Configuration config) + public ACLPlugin newInstance(Configuration config) + { + return new AllowAll(); + } + }; + + public String getPluginName() { - // no-op + return this.getClass().getSimpleName(); } @Override - protected boolean getResult() + protected AuthzResult getResult() { // Always allow - return true; + return AuthzResult.ALLOWED; } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicACLPlugin.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicACLPlugin.java index 26d3162f3a..f7e537b02b 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicACLPlugin.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicACLPlugin.java @@ -33,31 +33,31 @@ public abstract class BasicACLPlugin implements ACLPlugin { // Returns true or false if the plugin should authorise or deny the request - protected abstract boolean getResult(); + protected abstract AuthzResult getResult(); @Override - public boolean authoriseBind(AMQProtocolSession session, Exchange exch, + public AuthzResult authoriseBind(AMQProtocolSession session, Exchange exch, AMQQueue queue, AMQShortString routingKey) { return getResult(); } @Override - public boolean authoriseConnect(AMQProtocolSession session, + public AuthzResult authoriseConnect(AMQProtocolSession session, VirtualHost virtualHost) { return getResult(); } @Override - public boolean authoriseConsume(AMQProtocolSession session, boolean noAck, + public AuthzResult authoriseConsume(AMQProtocolSession session, boolean noAck, AMQQueue queue) { return getResult(); } @Override - public boolean authoriseConsume(AMQProtocolSession session, + public AuthzResult authoriseConsume(AMQProtocolSession session, boolean exclusive, boolean noAck, boolean noLocal, boolean nowait, AMQQueue queue) { @@ -65,7 +65,7 @@ public abstract class BasicACLPlugin implements ACLPlugin } @Override - public boolean authoriseCreateExchange(AMQProtocolSession session, + public AuthzResult authoriseCreateExchange(AMQProtocolSession session, boolean autoDelete, boolean durable, AMQShortString exchangeName, boolean internal, boolean nowait, boolean passive, AMQShortString exchangeType) @@ -74,7 +74,7 @@ public abstract class BasicACLPlugin implements ACLPlugin } @Override - public boolean authoriseCreateQueue(AMQProtocolSession session, + public AuthzResult authoriseCreateQueue(AMQProtocolSession session, boolean autoDelete, boolean durable, boolean exclusive, boolean nowait, boolean passive, AMQShortString queue) { @@ -82,19 +82,19 @@ public abstract class BasicACLPlugin implements ACLPlugin } @Override - public boolean authoriseDelete(AMQProtocolSession session, AMQQueue queue) + public AuthzResult authoriseDelete(AMQProtocolSession session, AMQQueue queue) { return getResult(); } @Override - public boolean authoriseDelete(AMQProtocolSession session, Exchange exchange) + public AuthzResult authoriseDelete(AMQProtocolSession session, Exchange exchange) { return getResult(); } @Override - public boolean authorisePublish(AMQProtocolSession session, + public AuthzResult authorisePublish(AMQProtocolSession session, boolean immediate, boolean mandatory, AMQShortString routingKey, Exchange e) { @@ -102,22 +102,28 @@ public abstract class BasicACLPlugin implements ACLPlugin } @Override - public boolean authorisePurge(AMQProtocolSession session, AMQQueue queue) + public AuthzResult authorisePurge(AMQProtocolSession session, AMQQueue queue) { return getResult(); } @Override - public boolean authoriseUnbind(AMQProtocolSession session, Exchange exch, + public AuthzResult authoriseUnbind(AMQProtocolSession session, Exchange exch, AMQShortString routingKey, AMQQueue queue) { return getResult(); } @Override - public void setConfiguaration(Configuration config) + public void setConfiguration(Configuration config) { // no-op } + public boolean supportsTag(String name) + { + // This plugin doesn't support any tags + return false; + } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/DenyAll.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/DenyAll.java index 1645236382..26a76c9af1 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/DenyAll.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/DenyAll.java @@ -26,11 +26,26 @@ import org.apache.qpid.framing.AMQMethodBody; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.security.access.ACLManager; +import org.apache.qpid.server.security.access.ACLPlugin; +import org.apache.qpid.server.security.access.ACLPluginFactory; import org.apache.qpid.server.security.access.AccessResult; import org.apache.qpid.server.security.access.Permission; public class DenyAll extends BasicACLPlugin { + public static final ACLPluginFactory FACTORY = new ACLPluginFactory() + { + public boolean supportsTag(String name) + { + return false; + } + + public ACLPlugin newInstance(Configuration config) + { + return new DenyAll(); + } + }; + public AccessResult authorise(AMQProtocolSession session, Permission permission, AMQMethodBody body, Object... parameters) throws AMQConnectionException @@ -47,19 +62,14 @@ public class DenyAll extends BasicACLPlugin public String getPluginName() { - return "DenyAll"; - } - - public void setConfiguaration(Configuration config) - { - // no-op + return getClass().getSimpleName(); } @Override - protected boolean getResult() + protected AuthzResult getResult() { // Always deny - return false; + return AuthzResult.DENIED; } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/SimpleXML.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/SimpleXML.java index 4fe1f8e777..2cc0c530de 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/SimpleXML.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/SimpleXML.java @@ -35,9 +35,11 @@ import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.security.access.ACLManager; import org.apache.qpid.server.security.access.ACLPlugin; +import org.apache.qpid.server.security.access.ACLPluginFactory; import org.apache.qpid.server.security.access.AccessResult; import org.apache.qpid.server.security.access.Permission; import org.apache.qpid.server.security.access.PrincipalPermissions; +import org.apache.qpid.server.security.access.ACLPlugin.AuthzResult; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Map; @@ -48,6 +50,21 @@ import java.util.concurrent.ConcurrentHashMap; */ public class SimpleXML implements ACLPlugin { + public static final ACLPluginFactory FACTORY = new ACLPluginFactory() + { + public boolean supportsTag(String name) + { + return name.startsWith("access_control_list"); + } + + public ACLPlugin newInstance(Configuration config) + { + SimpleXML plugin = new SimpleXML(); + plugin.setConfiguration(config); + return plugin; + } + }; + private Map<String, PrincipalPermissions> _users; private final AccessResult GRANTED = new AccessResult(this, AccessResult.AccessStatus.GRANTED); @@ -56,7 +73,7 @@ public class SimpleXML implements ACLPlugin _users = new ConcurrentHashMap<String, PrincipalPermissions>(); } - public void setConfiguaration(Configuration config) + public void setConfiguration(Configuration config) { processConfig(config); } @@ -78,7 +95,7 @@ public class SimpleXML implements ACLPlugin */ private void processPublish(Configuration config) { - Configuration publishConfig = config.subset("security.access_control_list.publish"); + Configuration publishConfig = config.subset("access_control_list.publish"); // Process users that have full publish permission String[] users = publishConfig.getStringArray("users.user"); @@ -149,7 +166,7 @@ public class SimpleXML implements ACLPlugin private void processConsume(Configuration config) { - Configuration consumeConfig = config.subset("security.access_control_list.consume"); + Configuration consumeConfig = config.subset("access_control_list.consume"); // Process queue limited users int queueCount = 0; @@ -186,7 +203,7 @@ public class SimpleXML implements ACLPlugin private void processCreate(Configuration config) { - Configuration createConfig = config.subset("security.access_control_list.create"); + Configuration createConfig = config.subset("access_control_list.create"); // Process create permissions for queue creation int queueCount = 0; @@ -273,13 +290,12 @@ public class SimpleXML implements ACLPlugin return "Simple"; } - @Override - public boolean authoriseBind(AMQProtocolSession session, Exchange exch, AMQQueue queue, AMQShortString routingKey) + public AuthzResult authoriseBind(AMQProtocolSession session, Exchange exch, AMQQueue queue, AMQShortString routingKey) { PrincipalPermissions principalPermissions = _users.get(session.getAuthorizedID().getName()); if (principalPermissions == null) { - return false; + return AuthzResult.DENIED; } else { @@ -287,13 +303,12 @@ public class SimpleXML implements ACLPlugin } } - @Override - public boolean authoriseConnect(AMQProtocolSession session, VirtualHost virtualHost) + public AuthzResult authoriseConnect(AMQProtocolSession session, VirtualHost virtualHost) { PrincipalPermissions principalPermissions = _users.get(session.getAuthorizedID().getName()); if (principalPermissions == null) { - return false; + return AuthzResult.DENIED; } else { @@ -301,13 +316,12 @@ public class SimpleXML implements ACLPlugin } } - @Override - public boolean authoriseConsume(AMQProtocolSession session, boolean noAck, AMQQueue queue) + public AuthzResult authoriseConsume(AMQProtocolSession session, boolean noAck, AMQQueue queue) { PrincipalPermissions principalPermissions = _users.get(session.getAuthorizedID().getName()); if (principalPermissions == null) { - return false; + return AuthzResult.DENIED; } else { @@ -315,21 +329,19 @@ public class SimpleXML implements ACLPlugin } } - @Override - public boolean authoriseConsume(AMQProtocolSession session, boolean exclusive, boolean noAck, boolean noLocal, + public AuthzResult authoriseConsume(AMQProtocolSession session, boolean exclusive, boolean noAck, boolean noLocal, boolean nowait, AMQQueue queue) { return authoriseConsume(session, noAck, queue); } - @Override - public boolean authoriseCreateExchange(AMQProtocolSession session, boolean autoDelete, boolean durable, + public AuthzResult authoriseCreateExchange(AMQProtocolSession session, boolean autoDelete, boolean durable, AMQShortString exchangeName, boolean internal, boolean nowait, boolean passive, AMQShortString exchangeType) { PrincipalPermissions principalPermissions = _users.get(session.getAuthorizedID().getName()); if (principalPermissions == null) { - return false; + return AuthzResult.DENIED; } else { @@ -337,14 +349,13 @@ public class SimpleXML implements ACLPlugin } } - @Override - public boolean authoriseCreateQueue(AMQProtocolSession session, boolean autoDelete, boolean durable, boolean exclusive, + public AuthzResult authoriseCreateQueue(AMQProtocolSession session, boolean autoDelete, boolean durable, boolean exclusive, boolean nowait, boolean passive, AMQShortString queue) { PrincipalPermissions principalPermissions = _users.get(session.getAuthorizedID().getName()); if (principalPermissions == null) { - return false; + return AuthzResult.DENIED; } else { @@ -352,13 +363,12 @@ public class SimpleXML implements ACLPlugin } } - @Override - public boolean authoriseDelete(AMQProtocolSession session, AMQQueue queue) + public AuthzResult authoriseDelete(AMQProtocolSession session, AMQQueue queue) { PrincipalPermissions principalPermissions = _users.get(session.getAuthorizedID().getName()); if (principalPermissions == null) { - return false; + return AuthzResult.DENIED; } else { @@ -366,13 +376,12 @@ public class SimpleXML implements ACLPlugin } } - @Override - public boolean authoriseDelete(AMQProtocolSession session, Exchange exchange) + public AuthzResult authoriseDelete(AMQProtocolSession session, Exchange exchange) { PrincipalPermissions principalPermissions = _users.get(session.getAuthorizedID().getName()); if (principalPermissions == null) { - return false; + return AuthzResult.DENIED; } else { @@ -380,14 +389,13 @@ public class SimpleXML implements ACLPlugin } } - @Override - public boolean authorisePublish(AMQProtocolSession session, boolean immediate, boolean mandatory, + public AuthzResult authorisePublish(AMQProtocolSession session, boolean immediate, boolean mandatory, AMQShortString routingKey, Exchange e) { PrincipalPermissions principalPermissions = _users.get(session.getAuthorizedID().getName()); if (principalPermissions == null) { - return false; + return AuthzResult.DENIED; } else { @@ -395,13 +403,12 @@ public class SimpleXML implements ACLPlugin } } - @Override - public boolean authorisePurge(AMQProtocolSession session, AMQQueue queue) + public AuthzResult authorisePurge(AMQProtocolSession session, AMQQueue queue) { PrincipalPermissions principalPermissions = _users.get(session.getAuthorizedID().getName()); if (principalPermissions == null) { - return false; + return AuthzResult.DENIED; } else { @@ -409,17 +416,17 @@ public class SimpleXML implements ACLPlugin } } - @Override - public boolean authoriseUnbind(AMQProtocolSession session, Exchange exch, AMQShortString routingKey, AMQQueue queue) + public AuthzResult authoriseUnbind(AMQProtocolSession session, Exchange exch, AMQShortString routingKey, AMQQueue queue) { PrincipalPermissions principalPermissions = _users.get(session.getAuthorizedID().getName()); if (principalPermissions == null) { - return false; + return AuthzResult.DENIED; } else { return principalPermissions.authorise(Permission.UNBIND); } } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.java index ee5f9d5e88..83b18e7a47 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.java @@ -27,16 +27,13 @@ import java.util.Properties; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.MapConfiguration; -import org.apache.qpid.server.management.ManagedObjectRegistry; import org.apache.qpid.server.management.NoopManagedObjectRegistry; import org.apache.qpid.server.plugins.PluginManager; import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.security.auth.manager.AuthenticationManager; -import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager; -import org.apache.qpid.server.security.auth.database.PrincipalDatabaseManager; -import org.apache.qpid.server.security.auth.database.PropertiesPrincipalDatabaseManager; -import org.apache.qpid.server.security.access.ACLPlugin; +import org.apache.qpid.server.security.access.ACLManager; import org.apache.qpid.server.security.access.plugins.AllowAll; +import org.apache.qpid.server.security.auth.database.PropertiesPrincipalDatabaseManager; +import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; @@ -59,13 +56,13 @@ public class NullApplicationRegistry extends ApplicationRegistry _databaseManager = new PropertiesPrincipalDatabaseManager("default", users); - _accessManager = new AllowAll(); + _accessManager = new ACLManager(_configuration, _pluginManager, AllowAll.FACTORY); _authenticationManager = new PrincipalDatabaseAuthenticationManager(null, null); _managedObjectRegistry = new NoopManagedObjectRegistry(); _virtualHostRegistry = new VirtualHostRegistry(); - VirtualHost dummyHost = new VirtualHost("test", getConfiguration()); + VirtualHost dummyHost = new VirtualHost("test", _configuration); _virtualHostRegistry.registerVirtualHost(dummyHost); _virtualHostRegistry.setDefaultVirtualHostName("test"); _pluginManager = new PluginManager(""); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java index 9229863c35..1a0d0ce8de 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java @@ -20,35 +20,35 @@ */ package org.apache.qpid.server.virtualhost; +import java.util.Timer; +import java.util.TimerTask; + import javax.management.NotCompliantMBeanException; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; import org.apache.qpid.server.AMQBrokerManagerMBean; +import org.apache.qpid.server.configuration.Configurator; import org.apache.qpid.server.connection.ConnectionRegistry; import org.apache.qpid.server.connection.IConnectionRegistry; -import org.apache.qpid.server.security.access.ACLPlugin; -import org.apache.qpid.server.security.access.ACLManager; -import org.apache.qpid.server.security.access.Accessable; -import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager; -import org.apache.qpid.server.security.auth.manager.AuthenticationManager; -import org.apache.qpid.server.configuration.Configurator; import org.apache.qpid.server.exchange.DefaultExchangeFactory; import org.apache.qpid.server.exchange.DefaultExchangeRegistry; import org.apache.qpid.server.exchange.ExchangeFactory; import org.apache.qpid.server.exchange.ExchangeRegistry; import org.apache.qpid.server.management.AMQManagedObject; import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.DefaultQueueRegistry; import org.apache.qpid.server.queue.QueueRegistry; -import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.security.access.ACLManager; +import org.apache.qpid.server.security.access.Accessable; +import org.apache.qpid.server.security.access.plugins.SimpleXML; +import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager; import org.apache.qpid.server.store.MessageStore; -import org.apache.qpid.AMQException; - -import java.util.Timer; -import java.util.TimerTask; public class VirtualHost implements Accessable { @@ -73,7 +73,7 @@ public class VirtualHost implements Accessable private AuthenticationManager _authenticationManager; - private ACLPlugin _accessManager; + private ACLManager _accessManager; private final Timer _houseKeepingTimer; @@ -183,8 +183,9 @@ public class VirtualHost implements Accessable _authenticationManager = new PrincipalDatabaseAuthenticationManager(name, hostConfig); - _accessManager = ACLManager.loadACLManager(name, hostConfig); - + _accessManager = ApplicationRegistry.getInstance().getAccessManager(); + _accessManager.configureHostPlugins(hostConfig); + _brokerMBean = new AMQBrokerManagerMBean(_virtualHostMBean); _brokerMBean.register(); initialiseHouseKeeping(hostConfig); @@ -258,7 +259,6 @@ public class VirtualHost implements Accessable return instance; } - public String getName() { return _name; @@ -294,7 +294,7 @@ public class VirtualHost implements Accessable return _authenticationManager; } - public ACLPlugin getAccessManager() + public ACLManager getAccessManager() { return _accessManager; } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/plugins/MockPluginManager.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/plugins/MockPluginManager.java new file mode 100644 index 0000000000..9599848dde --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/plugins/MockPluginManager.java @@ -0,0 +1,51 @@ +/* + * 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.plugins; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.qpid.server.exchange.ExchangeType; +import org.apache.qpid.server.security.access.ACLPlugin; +import org.apache.qpid.server.security.access.ACLPluginFactory; +import org.apache.qpid.server.security.access.QueueDenier; + +public class MockPluginManager extends PluginManager +{ + + private Map<String, ACLPluginFactory> _securityPlugins = new HashMap<String, ACLPluginFactory>(); + + public MockPluginManager(String plugindir) throws Exception + { + super(plugindir); + _securityPlugins.put("org.apache.qpid.server.security.access.QueueDenier", QueueDenier.FACTORY); + } + + @Override + public Map<String, ExchangeType<?>> getExchanges() + { + return null; + } + + @Override + public Map<String, ACLPluginFactory> getSecurityPlugins() + { + return _securityPlugins; + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/plugins/PluginTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/plugins/PluginTest.java index 0762a7a561..11d6105704 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/plugins/PluginTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/plugins/PluginTest.java @@ -48,7 +48,6 @@ public class PluginTest extends TestCase { PluginManager manager = new PluginManager("/path/to/nowhere"); Map<String, ExchangeType<?>> exchanges = manager.getExchanges(); - assertNull("Exchanges found", exchanges); - } - + assertEquals("Exchanges found", 0, exchanges.size()); + } } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java index cecb430574..3fc26a6f08 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java @@ -40,10 +40,21 @@ import java.util.LinkedList; public class MockAMQQueue implements AMQQueue { private boolean _deleted = false; + private AMQShortString _name; + + public MockAMQQueue(String name) + { + _name = new AMQShortString(name); + } + + public MockAMQQueue() + { + + } public AMQShortString getName() { - return null; //To change body of implemented methods use File | Settings | File Templates. + return _name; } public boolean isDurable() diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/ACLManagerTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/ACLManagerTest.java new file mode 100644 index 0000000000..d12a0b1f1b --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/ACLManagerTest.java @@ -0,0 +1,97 @@ +/* + * 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 java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; + +import junit.framework.TestCase; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.PropertiesConfiguration; +import org.apache.commons.configuration.XMLConfiguration; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.plugins.MockPluginManager; +import org.apache.qpid.server.plugins.PluginManager; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.MockAMQQueue; +import org.apache.qpid.server.queue.MockProtocolSession; +import org.apache.qpid.server.store.TestableMemoryMessageStore; + +public class ACLManagerTest extends TestCase +{ + + private ACLManager _authzManager; + private AMQProtocolSession _session; + private XMLConfiguration _conf; + private PluginManager _pluginManager; + + @Override + public void setUp() throws Exception + { + File tmpFile = File.createTempFile(getClass().getName(), "testconfig"); + tmpFile.deleteOnExit(); + BufferedWriter out = new BufferedWriter(new FileWriter(tmpFile)); + out.write("<broker><security><queueDenier>notyet</queueDenier><exchangeDenier>yes</exchangeDenier></security></broker>"); + out.close(); + + _conf = new XMLConfiguration(tmpFile); + + // Create ACLManager + + _pluginManager = new MockPluginManager(""); + _authzManager = new ACLManager(_conf, _pluginManager); + + _session = new MockProtocolSession(new TestableMemoryMessageStore()); + } + + public void testACLManagerConfigurationPluginManager() throws Exception + { + AMQQueue queue = new MockAMQQueue("notyet"); + AMQQueue otherQueue = new MockAMQQueue("other"); + + assertFalse(_authzManager.authoriseDelete(_session, queue)); + + // This should only be denied if the config hasn't been correctly passed in + assertTrue(_authzManager.authoriseDelete(_session, otherQueue)); + assertTrue(_authzManager.authorisePurge(_session, queue)); + } + + public void testACLManagerConfigurationPluginManagerACLPlugin() + { + _authzManager = new ACLManager(_conf, _pluginManager, ExchangeDenier.FACTORY); + + Exchange exchange = null; + assertFalse(_authzManager.authoriseDelete(_session, exchange)); + } + + public void testConfigurePlugins() + { + Configuration hostConfig = new PropertiesConfiguration(); + hostConfig.setProperty("security.queueDenier", "thisoneneither"); + _authzManager.configureHostPlugins(hostConfig); + AMQQueue queue = new MockAMQQueue("thisoneneither"); + assertFalse(_authzManager.authoriseDelete(_session, queue)); + } + +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/ExchangeDenier.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/ExchangeDenier.java new file mode 100644 index 0000000000..f62b0c6241 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/ExchangeDenier.java @@ -0,0 +1,62 @@ +/* + * 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.commons.configuration.Configuration; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.security.access.plugins.AllowAll; + +public class ExchangeDenier extends AllowAll +{ + + public static final ACLPluginFactory FACTORY = new ACLPluginFactory() + { + public boolean supportsTag(String name) + { + return name.startsWith("exchangeDenier"); + } + + public ACLPlugin newInstance(Configuration config) + { + return new ExchangeDenier(); + } + }; + + @Override + public AuthzResult authoriseDelete(AMQProtocolSession session, Exchange exchange) + { + return AuthzResult.DENIED; + } + + @Override + public String getPluginName() + { + return getClass().getSimpleName(); + } + + @Override + public boolean supportsTag(String name) + { + return name.equals("exchangeDenier"); + } + +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/PrincipalPermissionsTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/PrincipalPermissionsTest.java index df41ac9dc2..1e47f764df 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/PrincipalPermissionsTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/PrincipalPermissionsTest.java @@ -31,6 +31,7 @@ import org.apache.qpid.framing.amqp_8_0.QueueBindBodyImpl; import org.apache.qpid.server.exchange.DirectExchange; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.server.security.access.ACLPlugin.AuthzResult; import org.apache.qpid.server.store.SkeletonMessageStore; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -79,7 +80,7 @@ public class PrincipalPermissionsTest extends TestCase public void testPrincipalPermissions() { assertNotNull(_perms); - assertTrue(_perms.authorise(Permission.ACCESS, (Object[]) null)); + assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.ACCESS, (Object[]) null)); } // FIXME: test has been disabled since the permissions assume that the user has tried to create @@ -89,9 +90,9 @@ public class PrincipalPermissionsTest extends TestCase QueueBindBodyImpl bind = new QueueBindBodyImpl(_ticket, _queueName, _exchangeName, _routingKey, _nowait, _arguments); Object[] args = new Object[]{bind, _exchange, _queue, _routingKey}; - assertFalse(_perms.authorise(Permission.BIND, args)); + assertEquals(AuthzResult.DENIED, _perms.authorise(Permission.BIND, args)); _perms.grant(Permission.BIND, (Object[]) null); - assertTrue(_perms.authorise(Permission.BIND, args)); + assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.BIND, args)); } public void testQueueCreate() @@ -99,9 +100,9 @@ public class PrincipalPermissionsTest extends TestCase Object[] grantArgs = new Object[]{_temporary , _queueName, _exchangeName, _routingKey}; Object[] authArgs = new Object[]{_autoDelete, _queueName}; - assertFalse(_perms.authorise(Permission.CREATEQUEUE, authArgs)); + assertEquals(AuthzResult.DENIED, _perms.authorise(Permission.CREATEQUEUE, authArgs)); _perms.grant(Permission.CREATEQUEUE, grantArgs); - assertTrue(_perms.authorise(Permission.CREATEQUEUE, authArgs)); + assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.CREATEQUEUE, authArgs)); } @@ -114,9 +115,9 @@ public class PrincipalPermissionsTest extends TestCase Object[] authArgs = new Object[]{exchangeDeclare}; Object[] grantArgs = new Object[]{_exchangeName, _exchangeType}; - assertFalse(_perms.authorise(Permission.CREATEEXCHANGE, authArgs)); + assertEquals(AuthzResult.DENIED, _perms.authorise(Permission.CREATEEXCHANGE, authArgs)); _perms.grant(Permission.CREATEEXCHANGE, grantArgs); - assertTrue(_perms.authorise(Permission.CREATEEXCHANGE, authArgs)); + assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.CREATEEXCHANGE, authArgs)); } public void testConsume() @@ -128,7 +129,7 @@ public class PrincipalPermissionsTest extends TestCase * assertFalse(_perms.authorise(Permission.CONSUME, authArgs)); */ _perms.grant(Permission.CONSUME, grantArgs); - assertTrue(_perms.authorise(Permission.CONSUME, authArgs)); + assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.CONSUME, authArgs)); } public void testPublish() @@ -136,9 +137,9 @@ public class PrincipalPermissionsTest extends TestCase Object[] authArgs = new Object[]{_exchange, _routingKey}; Object[] grantArgs = new Object[]{_exchange.getName(), _routingKey}; - assertFalse(_perms.authorise(Permission.PUBLISH, authArgs)); + assertEquals(AuthzResult.DENIED, _perms.authorise(Permission.PUBLISH, authArgs)); _perms.grant(Permission.PUBLISH, grantArgs); - assertTrue(_perms.authorise(Permission.PUBLISH, authArgs)); + assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.PUBLISH, authArgs)); } } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/QueueDenier.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/QueueDenier.java new file mode 100644 index 0000000000..5497f0ae44 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/QueueDenier.java @@ -0,0 +1,68 @@ +/* + * 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.commons.configuration.Configuration; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.security.access.ACLPlugin.AuthzResult; +import org.apache.qpid.server.security.access.plugins.AllowAll; + +public class QueueDenier extends AllowAll +{ + + public static final ACLPluginFactory FACTORY = new ACLPluginFactory() + { + public boolean supportsTag(String name) + { + return name.equals("queueDenier"); + } + + public ACLPlugin newInstance(Configuration config) + { + QueueDenier plugin = new QueueDenier(); + plugin.setConfiguration(config); + return plugin; + } + }; + + private String _queueName = ""; + + + @Override + public AuthzResult authoriseDelete(AMQProtocolSession session, AMQQueue queue) + { + if (!(queue.getName().toString().equals(_queueName))) + { + return AuthzResult.ALLOWED; + } + else + { + return AuthzResult.DENIED; + } + } + + @Override + public void setConfiguration(Configuration config) + { + _queueName = config.getString("queueDenier"); + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java index 15449dc613..b6d42e6068 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java @@ -26,6 +26,7 @@ import org.apache.qpid.server.exchange.ExchangeRegistry; import org.apache.qpid.server.management.NoopManagedObjectRegistry; import org.apache.qpid.server.queue.QueueRegistry; import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.security.access.ACLManager; import org.apache.qpid.server.security.access.ACLPlugin; import org.apache.qpid.server.security.access.plugins.AllowAll; import org.apache.qpid.server.security.auth.database.PropertiesPrincipalDatabaseManager; @@ -66,7 +67,7 @@ public class TestApplicationRegistry extends ApplicationRegistry _databaseManager = new PropertiesPrincipalDatabaseManager("default", users); - _accessManager = new AllowAll(); + _accessManager = new ACLManager(_configuration, _pluginManager, AllowAll.FACTORY); _authenticationManager = new PrincipalDatabaseAuthenticationManager(null, null); @@ -108,7 +109,7 @@ public class TestApplicationRegistry extends ApplicationRegistry return Arrays.asList(hosts); } - public void setAccessManager(ACLPlugin newManager) + public void setAccessManager(ACLManager newManager) { _accessManager = newManager; } |