diff options
author | Robert Gemmell <robbie@apache.org> | 2010-05-31 16:03:41 +0000 |
---|---|---|
committer | Robert Gemmell <robbie@apache.org> | 2010-05-31 16:03:41 +0000 |
commit | 48e49bef0775e91625ba7b5c03823dbaca943bf7 (patch) | |
tree | f987246a0d61c3a23e5c52b9a233778d57e3fca4 | |
parent | a2d26b71f141f3166bdd0342b481723d98b0bb99 (diff) | |
download | qpid-python-48e49bef0775e91625ba7b5c03823dbaca943bf7.tar.gz |
QPID-2606: Access Control Modifications
Applied patch from Andrew Kennedy <andrew.international@gmail.com>
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk/qpid@949781 13f79535-47bb-0310-9956-ffa450edef68
110 files changed, 4328 insertions, 2620 deletions
diff --git a/java/broker/etc/config.xml b/java/broker/etc/config.xml index 2f78003afb..87101678c0 100644 --- a/java/broker/etc/config.xml +++ b/java/broker/etc/config.xml @@ -25,6 +25,7 @@ <conf>${prefix}/etc</conf> <plugin-directory>${QPID_HOME}/lib/plugins</plugin-directory> + <cache-directory>${QPID_WORK}/cache</cache-directory> <connector> <!-- To enable SSL edit the keystorePath and keystorePassword @@ -83,9 +84,7 @@ </principal-database> </principal-databases> - <access> - <class>org.apache.qpid.server.security.access.plugins.AllowAll</class> - </access> + <allow-all /> <msg-auth>false</msg-auth> diff --git a/java/broker/src/main/java/org/apache/qpid/qmf/QMFService.java b/java/broker/src/main/java/org/apache/qpid/qmf/QMFService.java index 8bb1a6b9fa..7e999a720b 100644 --- a/java/broker/src/main/java/org/apache/qpid/qmf/QMFService.java +++ b/java/broker/src/main/java/org/apache/qpid/qmf/QMFService.java @@ -1063,7 +1063,14 @@ public class QMFService implements ConfigStore.ConfigEventListener, Closeable public BrokerSchema.QueueClass.PurgeMethodResponseCommand purge(final BrokerSchema.QueueClass.PurgeMethodResponseCommandFactory factory, final Long request) { - _obj.purge(request); + try + { + _obj.purge(request); + } catch (AMQException e) + { + // TODO + throw new RuntimeException(); + } return factory.createResponseCommand(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java b/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java index b2e43b4f0d..811e45f4ae 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java +++ b/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java @@ -1,5 +1,4 @@ /* - * * 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 @@ -16,24 +15,6 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. - * - */ -/* - * - * Copyright (c) 2006 The Apache Software Foundation - * - * Licensed 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; @@ -189,8 +170,9 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr * @param type * @param durable * @throws JMException + * @throws MBeanException */ - public void createNewExchange(String exchangeName, String type, boolean durable) throws JMException + public void createNewExchange(String exchangeName, String type, boolean durable) throws JMException, MBeanException { CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger())); try @@ -216,7 +198,8 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr } catch (AMQException ex) { - throw new MBeanException(ex, "Error in creating exchange " + exchangeName); + JMException jme = new JMException(ex.toString()); + throw new MBeanException(jme, "Error in creating exchange " + exchangeName); } finally { @@ -229,8 +212,9 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr * * @param exchangeName * @throws JMException + * @throws MBeanException */ - public void unregisterExchange(String exchangeName) throws JMException + public void unregisterExchange(String exchangeName) throws JMException, MBeanException { // TODO // Check if the exchange is in use. @@ -244,7 +228,8 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr } catch (AMQException ex) { - throw new MBeanException(ex, "Error in unregistering exchange " + exchangeName); + JMException jme = new JMException(ex.toString()); + throw new MBeanException(jme, "Error in unregistering exchange " + exchangeName); } finally { @@ -260,8 +245,9 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr * @param durable * @param owner * @throws JMException + * @throws MBeanException */ - public void createNewQueue(String queueName, String owner, boolean durable) throws JMException + public void createNewQueue(String queueName, String owner, boolean durable) throws JMException, MBeanException { AMQQueue queue = _queueRegistry.getQueue(new AMQShortString(queueName)); if (queue != null) @@ -278,8 +264,7 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr ownerShortString = new AMQShortString(owner); } - queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString(queueName), durable, ownerShortString, false, false, - getVirtualHost(), null); + queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString(queueName), durable, ownerShortString, false, false, getVirtualHost(), null); if (queue.isDurable() && !queue.isAutoDelete()) { _durableConfig.createQueue(queue); @@ -289,8 +274,7 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr } catch (AMQException ex) { - JMException jme = new JMException(ex.getMessage()); - jme.initCause(ex); + JMException jme = new JMException(ex.toString()); throw new MBeanException(jme, "Error in creating queue " + queueName); } finally @@ -309,13 +293,14 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr * * @param queueName * @throws JMException + * @throws MBeanException */ - public void deleteQueue(String queueName) throws JMException + public void deleteQueue(String queueName) throws JMException, MBeanException { AMQQueue queue = _queueRegistry.getQueue(new AMQShortString(queueName)); if (queue == null) { - throw new JMException("The Queue " + queueName + " is not a registerd queue."); + throw new JMException("The Queue " + queueName + " is not a registered queue."); } CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger())); @@ -329,8 +314,7 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr } catch (AMQException ex) { - JMException jme = new JMException(ex.getMessage()); - jme.initCause(ex); + JMException jme = new JMException(ex.toString()); throw new MBeanException(jme, "Error in deleting queue " + queueName); } finally @@ -339,14 +323,16 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr } } + @Override public ManagedObject getParentObject() { return _virtualHostMBean; } // This will have a single instance for a virtual host, so not having the name property in the ObjectName + @Override public ObjectName getObjectName() throws MalformedObjectNameException { return getObjectNameForSingleInstanceMBean(); } -} // End of MBean class +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java b/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java index 7d0163510a..8a0e49f537 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java +++ b/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java @@ -23,6 +23,7 @@ package org.apache.qpid.server; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; +import org.apache.qpid.AMQSecurityException; import org.apache.qpid.framing.AMQMethodBody; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicContentHeaderProperties; @@ -244,9 +245,12 @@ public class AMQChannel implements SessionConfig, AMQSessionModel return _channelId; } - public void setPublishFrame(MessagePublishInfo info, final Exchange e) throws AMQException + public void setPublishFrame(MessagePublishInfo info, final Exchange e) throws AMQSecurityException { - + if (!getVirtualHost().getSecurityManager().authorisePublish(info.isImmediate(), info.getRoutingKey().asString(), e.getName())) + { + throw new AMQSecurityException("Permission denied: " + e.getName()); + } _currentMessage = new IncomingMessage(info); _currentMessage.setExchange(e); } @@ -421,7 +425,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel { throw new AMQException("Consumer already exists with same tag: " + tag); } - + Subscription subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(_channelId, _session, tag, acks, filters, noLocal, _creditManager); diff --git a/java/broker/src/main/java/org/apache/qpid/server/Main.java b/java/broker/src/main/java/org/apache/qpid/server/Main.java index 90afd2e4ac..d52267ad57 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/Main.java +++ b/java/broker/src/main/java/org/apache/qpid/server/Main.java @@ -25,12 +25,12 @@ import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; import java.net.InetSocketAddress; -import java.util.Properties; -import java.util.Set; +import java.util.Arrays; +import java.util.EnumSet; import java.util.HashSet; import java.util.List; -import java.util.Collection; -import java.util.Arrays; +import java.util.Properties; +import java.util.Set; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.HelpFormatter; @@ -42,8 +42,6 @@ import org.apache.commons.cli.PosixParser; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.xml.QpidLog4JConfigurator; -import org.apache.qpid.transport.network.ConnectionBinding; -import org.apache.qpid.transport.*; import org.apache.qpid.common.QpidProperties; import org.apache.qpid.framing.ProtocolVersion; import org.apache.qpid.server.configuration.ServerConfiguration; @@ -56,21 +54,18 @@ import org.apache.qpid.server.logging.management.LoggingManagementMBean; import org.apache.qpid.server.logging.messages.BrokerMessages; import org.apache.qpid.server.protocol.AMQProtocolEngineFactory; import org.apache.qpid.server.protocol.MultiVersionProtocolEngineFactory; +import org.apache.qpid.server.protocol.MultiVersionProtocolEngineFactory.VERSION; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; -import org.apache.qpid.server.registry.IApplicationRegistry; -import org.apache.qpid.server.transport.ServerConnection; import org.apache.qpid.server.transport.QpidAcceptor; import org.apache.qpid.ssl.SSLContextFactory; import org.apache.qpid.transport.NetworkDriver; import org.apache.qpid.transport.network.mina.MINANetworkDriver; -import org.apache.qpid.server.protocol.MultiVersionProtocolEngineFactory.VERSION; /** * Main entry point for AMQPD. * */ -@SuppressWarnings({"AccessStaticViaInstance"}) public class Main { private static Logger _logger; @@ -83,7 +78,6 @@ public class Main private static final int IPV4_ADDRESS_LENGTH = 4; private static final char IPV4_LITERAL_SEPARATOR = '.'; - private static final Collection<VERSION> ALL_VERSIONS = Arrays.asList(VERSION.values()); protected static class InitException extends Exception { @@ -123,6 +117,7 @@ public class Main } } + @SuppressWarnings("static-access") protected void setOptions(Options options) { Option help = new Option("h", "help", false, "print this message"); @@ -403,7 +398,7 @@ public class Main NetworkDriver driver = new MINANetworkDriver(); - Set<VERSION> supported = new HashSet<VERSION>(ALL_VERSIONS); + Set<VERSION> supported = EnumSet.allOf(VERSION.class); if(exclude_0_10.contains(port)) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/binding/BindingFactory.java b/java/broker/src/main/java/org/apache/qpid/server/binding/BindingFactory.java index 5423f02107..59626a7b13 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/binding/BindingFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/binding/BindingFactory.java @@ -20,7 +20,12 @@ */ package org.apache.qpid.server.binding; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + import org.apache.qpid.AMQException; +import org.apache.qpid.AMQSecurityException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.configuration.BindingConfig; @@ -35,13 +40,8 @@ import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.virtualhost.VirtualHost; -import java.util.Collections; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - public class BindingFactory { - private final VirtualHost _virtualHost; private final DurableConfigurationStore.Source _configSource; private final Exchange _defaultExchange; @@ -51,14 +51,14 @@ public class BindingFactory public BindingFactory(final VirtualHost vhost) { - this(vhost,vhost.getExchangeRegistry().getDefaultExchange()); + this(vhost, vhost.getExchangeRegistry().getDefaultExchange()); } public BindingFactory(final DurableConfigurationStore.Source configSource, final Exchange defaultExchange) { _configSource = configSource; _defaultExchange = defaultExchange; - if(configSource instanceof VirtualHost) + if (configSource instanceof VirtualHost) { _virtualHost = (VirtualHost) configSource; } @@ -83,7 +83,7 @@ public class BindingFactory private BindingImpl(String bindingKey, final AMQQueue queue, final Exchange exchange, final Map<String, Object> arguments) { - super(queue.getVirtualHost().getConfigStore().createId(),bindingKey, queue, exchange, arguments); + super(queue.getVirtualHost().getConfigStore().createId(), bindingKey, queue, exchange, arguments); _logSubject = new BindingLogSubject(bindingKey,exchange,queue); } @@ -94,7 +94,7 @@ public class BindingFactory removeBinding(this); } - public void onClose(final Exchange exchange) + public void onClose(final Exchange exchange) throws AMQSecurityException { removeBinding(this); } @@ -138,7 +138,7 @@ public class BindingFactory - public boolean addBinding(String bindingKey, AMQQueue queue, Exchange exchange, Map<String, Object> arguments) + public boolean addBinding(String bindingKey, AMQQueue queue, Exchange exchange, Map<String, Object> arguments) throws AMQSecurityException { return makeBinding(bindingKey, queue, exchange, arguments, false, false); } @@ -147,37 +147,43 @@ public class BindingFactory public boolean replaceBinding(final String bindingKey, final AMQQueue queue, final Exchange exchange, - final Map<String, Object> arguments) + final Map<String, Object> arguments) throws AMQSecurityException { return makeBinding(bindingKey, queue, exchange, arguments, false, true); } - private boolean makeBinding(String bindingKey, AMQQueue queue, Exchange exchange, Map<String, Object> arguments, boolean restore, boolean force) + private boolean makeBinding(String bindingKey, AMQQueue queue, Exchange exchange, Map<String, Object> arguments, boolean restore, boolean force) throws AMQSecurityException { assert queue != null; - if(bindingKey == null) + if (bindingKey == null) { bindingKey = ""; } - if(exchange == null) + if (exchange == null) { exchange = _defaultExchange; } - if(arguments == null) + if (arguments == null) { - arguments = Collections.EMPTY_MAP; + arguments = Collections.emptyMap(); } - + + //Perform ACLs + if (!getVirtualHost().getSecurityManager().authoriseBind(exchange, queue, new AMQShortString(bindingKey))) + { + throw new AMQSecurityException("Permission denied: binding " + bindingKey); + } + BindingImpl b = new BindingImpl(bindingKey,queue,exchange,arguments); BindingImpl existingMapping = _bindings.putIfAbsent(b,b); - if(existingMapping == null || force) + if (existingMapping == null || force) { - if(existingMapping != null) + if (existingMapping != null) { removeBinding(existingMapping); } - if(b.isDurable() && !restore) + if (b.isDurable() && !restore) { try { @@ -185,7 +191,7 @@ public class BindingFactory } catch (AMQException e) { - throw new RuntimeException(e); + throw new RuntimeException(e); // FIXME } } @@ -201,8 +207,6 @@ public class BindingFactory { return false; } - - } private ConfigStore getConfigStore() @@ -210,43 +214,49 @@ public class BindingFactory return getVirtualHost().getConfigStore(); } - public void restoreBinding(final String bindingKey, final AMQQueue queue, final Exchange exchange, final Map<String, Object> argumentMap) + public void restoreBinding(final String bindingKey, final AMQQueue queue, final Exchange exchange, final Map<String, Object> argumentMap) throws AMQSecurityException { makeBinding(bindingKey,queue,exchange,argumentMap,true, false); } - public void removeBinding(final Binding b) + public void removeBinding(final Binding b) throws AMQSecurityException { removeBinding(b.getBindingKey(), b.getQueue(), b.getExchange(), b.getArguments()); } - public Binding removeBinding(String bindingKey, AMQQueue queue, Exchange exchange, Map<String, Object> arguments) + public Binding removeBinding(String bindingKey, AMQQueue queue, Exchange exchange, Map<String, Object> arguments) throws AMQSecurityException { assert queue != null; - if(bindingKey == null) + if (bindingKey == null) { bindingKey = ""; } - if(exchange == null) + if (exchange == null) { exchange = _defaultExchange; } - if(arguments == null) + if (arguments == null) { - arguments = Collections.EMPTY_MAP; + arguments = Collections.emptyMap(); } + // Check access + if (!getVirtualHost().getSecurityManager().authoriseUnbind(exchange, new AMQShortString(bindingKey), queue)) + { + throw new AMQSecurityException("Permission denied: binding " + bindingKey); + } + BindingImpl b = _bindings.remove(new BindingImpl(bindingKey,queue,exchange,arguments)); - if(b != null) + if (b != null) { exchange.removeBinding(b); queue.removeBinding(b); exchange.removeCloseTask(b); queue.removeQueueDeleteTask(b); - if(b.isDurable()) + if (b.isDurable()) { try { @@ -257,12 +267,11 @@ public class BindingFactory } catch (AMQException e) { - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + throw new RuntimeException(e); // FIXME } } b.logDestruction(); getConfigStore().removeConfiguredObject(b); - } return b; @@ -281,7 +290,7 @@ public class BindingFactory } if(arguments == null) { - arguments = Collections.EMPTY_MAP; + arguments = Collections.emptyMap(); } BindingImpl b = new BindingImpl(bindingKey,queue,exchange,arguments); diff --git a/java/broker/src/main/java/org/apache/qpid/server/configuration/QueueConfiguration.java b/java/broker/src/main/java/org/apache/qpid/server/configuration/QueueConfiguration.java index 7fef4558c8..f62022922a 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/configuration/QueueConfiguration.java +++ b/java/broker/src/main/java/org/apache/qpid/server/configuration/QueueConfiguration.java @@ -25,6 +25,7 @@ import java.util.List; import org.apache.commons.configuration.CompositeConfiguration; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; public class QueueConfiguration extends ConfigurationPlugin @@ -109,7 +110,7 @@ public class QueueConfiguration extends ConfigurationPlugin public String getExchange() { - return getStringValue("exchange", null); + return getStringValue("exchange", ExchangeDefaults.DEFAULT_EXCHANGE_NAME.asString()); } public List getRoutingKeys() diff --git a/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java b/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java index 9f1a4c52f2..f8fc27e86a 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java +++ b/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java @@ -20,6 +20,15 @@ package org.apache.qpid.server.configuration; +import java.io.File; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; + import org.apache.commons.configuration.CompositeConfiguration; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; @@ -27,26 +36,17 @@ import org.apache.commons.configuration.ConfigurationFactory; import org.apache.commons.configuration.HierarchicalConfiguration; import org.apache.commons.configuration.SystemConfiguration; import org.apache.commons.configuration.XMLConfiguration; +import org.apache.log4j.Logger; import org.apache.qpid.server.configuration.management.ConfigurationManagementMBean; import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; -import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory; +import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; -import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.transport.NetworkDriverConfiguration; import sun.misc.Signal; import sun.misc.SignalHandler; -import java.io.File; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; - public class ServerConfiguration extends ConfigurationPlugin implements SignalHandler { protected static final Logger _logger = Logger.getLogger(ServerConfiguration.class); @@ -63,17 +63,22 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa public static final int DEFAULT_SSL_PORT = 8672; public static final long DEFAULT_HOUSEKEEPING_PERIOD = 30000L; public static final int DEFAULT_JMXPORT = 8999; + + public static final String QPID_HOME = "QPID_HOME"; + public static final String QPID_WORK = "QPID_WORK"; + public static final String LIB_DIR = "lib"; + public static final String PLUGIN_DIR = "plugins"; + public static final String CACHE_DIR = "cache"; private Map<String, VirtualHostConfiguration> _virtualHosts = new HashMap<String, VirtualHostConfiguration>(); private File _configFile; private File _vhostsFile; - private Logger _log = LoggerFactory.getLogger(this.getClass()); + private Logger _log = Logger.getLogger(this.getClass()); private ConfigurationManagementMBean _mbean; - // Map of environment variables to config items private static final Map<String, String> envVarMap = new HashMap<String, String>(); @@ -142,6 +147,7 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa } catch (IllegalArgumentException e) { + _logger.error("Signal HUP not supported for OS: " + System.getProperty("os.name")); // We're on something that doesn't handle SIGHUP, how sad, Windows. } } @@ -192,7 +198,7 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa public String[] getElementsProcessed() { - return new String[]{""}; + return new String[] { "" }; } @Override @@ -308,7 +314,6 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa */ public Locale getLocale() { - String localeString = getStringValue(ADVANCED_LOCALE); // Expecting locale of format langauge_country_variant @@ -432,10 +437,15 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa _logger.warn(SECURITY_CONFIG_RELOADED); } } - + public String getQpidWork() { - return System.getProperty("QPID_WORK", System.getProperty("java.io.tmpdir")); + return System.getProperty(QPID_WORK, System.getProperty("java.io.tmpdir")); + } + + public String getQpidHome() + { + return System.getProperty(QPID_HOME); } public void setJMXManagementPort(int mport) @@ -467,11 +477,16 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa { return _virtualHosts.keySet().toArray(new String[_virtualHosts.size()]); } - + public String getPluginDirectory() { return getStringValue("plugin-directory"); } + + public String getCacheDirectory() + { + return getStringValue("cache-directory"); + } public VirtualHostConfiguration getVirtualHostConfig(String name) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchangeMBean.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchangeMBean.java index ece8bc90fe..0dca91b7be 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchangeMBean.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchangeMBean.java @@ -20,6 +20,8 @@ */ package org.apache.qpid.server.exchange; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQSecurityException; import org.apache.qpid.server.management.AMQManagedObject; import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.management.ManagedObjectRegistry; @@ -32,6 +34,7 @@ import org.apache.qpid.management.common.mbeans.ManagedExchange; import org.apache.qpid.framing.AMQShortString; import javax.management.openmbean.*; +import javax.management.MBeanException; import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; import javax.management.MalformedObjectNameException; @@ -133,7 +136,15 @@ public abstract class AbstractExchangeMBean<T extends AbstractExchange> extends } CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger())); - vhost.getBindingFactory().addBinding(binding,queue,getExchange(),null); + try + { + vhost.getBindingFactory().addBinding(binding,queue,getExchange(),null); + } + catch (AMQException ex) + { + JMException jme = new JMException(ex.toString()); + throw new MBeanException(jme, "Error creating new binding " + binding); + } CurrentActor.remove(); } -} // End of MBean class +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java index 9be8bddd28..7837a9bc38 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java @@ -26,9 +26,12 @@ import java.util.HashMap; import java.util.Map; import org.apache.log4j.Logger; +import org.apache.qpid.AMQConnectionException; import org.apache.qpid.AMQException; +import org.apache.qpid.AMQSecurityException; import org.apache.qpid.AMQUnknownExchangeType; import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.qmf.ManagementExchange; import org.apache.qpid.server.configuration.VirtualHostConfiguration; import org.apache.qpid.server.registry.ApplicationRegistry; @@ -79,27 +82,26 @@ public class DefaultExchangeFactory implements ExchangeFactory public Exchange createExchange(String exchange, String type, boolean durable, boolean autoDelete) throws AMQException { - ExchangeType<? extends Exchange> exchType = _exchangeClassMap.get(new AMQShortString(type)); - if (exchType == null) - { - - throw new AMQUnknownExchangeType("Unknown exchange type: " + type,null); - } - Exchange e = exchType.newInstance(_host, (new AMQShortString(exchange)).intern(), durable, 0, autoDelete); - return e; - + return createExchange(new AMQShortString(exchange), new AMQShortString(type), durable, autoDelete, 0); } public Exchange createExchange(AMQShortString exchange, AMQShortString type, boolean durable, boolean autoDelete, int ticket) throws AMQException { + // Check access + if (!_host.getSecurityManager().authoriseCreateExchange(autoDelete, durable, exchange, null, null, null, type)) + { + String description = "Permission denied: exchange-name '" + exchange.asString() + "'"; + throw new AMQSecurityException(description); + } + ExchangeType<? extends Exchange> exchType = _exchangeClassMap.get(type); if (exchType == null) { - throw new AMQUnknownExchangeType("Unknown exchange type: " + type,null); } + Exchange e = exchType.newInstance(_host, exchange, durable, ticket, autoDelete); return e; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java index 84444450c9..0e7459498a 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java @@ -20,18 +20,18 @@ */ package org.apache.qpid.server.exchange; +import java.util.Collection; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + import org.apache.log4j.Logger; import org.apache.qpid.AMQException; +import org.apache.qpid.AMQSecurityException; import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.exchange.ExchangeInitialiser; import org.apache.qpid.server.queue.IncomingMessage; import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.virtualhost.VirtualHost; -import java.util.Collection; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - public class DefaultExchangeRegistry implements ExchangeRegistry { private static final Logger _log = Logger.getLogger(DefaultExchangeRegistry.class); @@ -87,7 +87,14 @@ public class DefaultExchangeRegistry implements ExchangeRegistry public void unregisterExchange(AMQShortString name, boolean inUse) throws AMQException { + // Check access + if (!_host.getSecurityManager().authoriseDelete(_exchangeMap.get(name))) + { + throw new AMQSecurityException(); + } + // TODO: check inUse argument + Exchange e = _exchangeMap.remove(name); _exchangeMapStr.remove(name.toString()); if (e != null) diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java index 8a31b1bab1..13fe767d3f 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java @@ -21,6 +21,7 @@ package org.apache.qpid.server.exchange; import org.apache.qpid.AMQException; +import org.apache.qpid.AMQSecurityException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; @@ -138,6 +139,6 @@ public interface Exchange extends ExchangeReferrer, ExchangeConfig public static interface Task { - public void onClose(Exchange exchange); + public void onClose(Exchange exchange) throws AMQSecurityException; } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java index aa4cc1ec24..92795487e4 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java @@ -22,8 +22,6 @@ package org.apache.qpid.server.exchange; import java.util.Collection; -import org.apache.commons.configuration.Configuration; - import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.configuration.VirtualHostConfiguration; diff --git a/java/broker/src/main/java/org/apache/qpid/server/federation/Bridge.java b/java/broker/src/main/java/org/apache/qpid/server/federation/Bridge.java index ae578eb196..a4974c75ff 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/federation/Bridge.java +++ b/java/broker/src/main/java/org/apache/qpid/server/federation/Bridge.java @@ -674,13 +674,22 @@ public class Bridge implements BridgeConfig options.put("qpid.trace.exclude", _link.getFederationTag()); options.put("qpid.trace.id",_link.getRemoteFederationTag()); - _queue = AMQQueueFactory.createAMQQueueImpl(_tmpQueueName, + try + { + _queue = AMQQueueFactory.createAMQQueueImpl(_tmpQueueName, isDurable(), _link.getFederationTag(), false, false, - getVirtualHost(), options); - + getVirtualHost(), + options); + } + catch (AMQException e) + { + // TODO + throw new RuntimeException(e); + } + FlowCreditManager_0_10 creditManager = new WindowCreditManager(0xFFFFFFFF,getMessageWindowSize()); Subscription_0_10 sub = new Subscription_0_10((ServerSession)session, @@ -695,14 +704,13 @@ public class Bridge implements BridgeConfig try { _queue.registerSubscription(sub, true); + getVirtualHost().getBindingFactory().addBinding(_key, _queue, exchange, Collections.<String, Object>emptyMap()); } catch (AMQException e) { // TODO throw new RuntimeException(e); } - - getVirtualHost().getBindingFactory().addBinding(_key, _queue, exchange, Collections.EMPTY_MAP); } public void close() diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java index 50019090d9..a5999711bc 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java @@ -51,9 +51,6 @@ public class BasicConsumeMethodHandler implements StateAwareMethodListener<Basic { AMQProtocolSession protocolConnection = stateManager.getProtocolSession(); - - - AMQChannel channel = protocolConnection.getChannel(channelId); VirtualHost vHost = protocolConnection.getVirtualHost(); @@ -93,17 +90,9 @@ public class BasicConsumeMethodHandler implements StateAwareMethodListener<Basic } else { - final AMQShortString consumerTagName; - // Check authz - if (!vHost.getAccessManager().authoriseConsume(protocolConnection, - body.getExclusive(), body.getNoAck(), - body.getNoLocal(), body.getNowait(), queue)) - { - throw body.getConnectionException(AMQConstant.ACCESS_REFUSED, "Permission denied"); - } - else if (queue.isExclusive() && !queue.isDurable()) + if (queue.isExclusive() && !queue.isDurable()) { AMQSessionModel session = queue.getExclusiveOwningSession(); if (session == null || session.getConnectionModel() != protocolConnection) diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java index 2ab0b04fb9..83ca526578 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java @@ -92,13 +92,7 @@ public class BasicGetMethodHandler implements StateAwareMethodListener<BasicGetB } else { - - //Perform ACLs - if (!vHost.getAccessManager().authoriseConsume(protocolConnection, body.getNoAck(), queue)) - { - throw body.getConnectionException(AMQConstant.ACCESS_REFUSED, "Permission denied"); - } - else if (queue.isExclusive()) + if (queue.isExclusive()) { AMQSessionModel session = queue.getExclusiveOwningSession(); if (session == null || session.getConnectionModel() != protocolConnection) diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java index a7d3ad6217..8f23b1c4d4 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java @@ -23,14 +23,13 @@ package org.apache.qpid.server.handler; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.exchange.ExchangeDefaults; -import org.apache.qpid.framing.BasicPublishBody; import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicPublishBody; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.security.access.Permission; import org.apache.qpid.server.state.AMQStateManager; import org.apache.qpid.server.state.StateAwareMethodListener; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -59,18 +58,17 @@ public class BasicPublishMethodHandler implements StateAwareMethodListener<Basic _logger.debug("Publish received on channel " + channelId); } - AMQShortString exchange = body.getExchange(); + AMQShortString exchangeName = body.getExchange(); // TODO: check the delivery tag field details - is it unique across the broker or per subscriber? - if (exchange == null) + if (exchangeName == null) { - exchange = ExchangeDefaults.DEFAULT_EXCHANGE_NAME; - + exchangeName = ExchangeDefaults.DEFAULT_EXCHANGE_NAME; } VirtualHost vHost = session.getVirtualHost(); - Exchange e = vHost.getExchangeRegistry().getExchange(exchange); + Exchange exch = vHost.getExchangeRegistry().getExchange(exchangeName); // if the exchange does not exist we raise a channel exception - if (e == null) + if (exch == null) { throw body.getChannelException(AMQConstant.NOT_FOUND, "Unknown exchange name"); } @@ -86,17 +84,9 @@ public class BasicPublishMethodHandler implements StateAwareMethodListener<Basic throw body.getChannelNotFoundException(channelId); } - //Access Control - if (!vHost.getAccessManager().authorisePublish(session, - body.getImmediate(), body.getMandatory(), - body.getRoutingKey(), e)) - { - throw body.getConnectionException(AMQConstant.ACCESS_REFUSED, "Permission denied"); - } - MessagePublishInfo info = session.getMethodRegistry().getProtocolVersionMethodConverter().convertToInfo(body); - info.setExchange(exchange); - channel.setPublishFrame(info, e); + info.setExchange(exchangeName); + channel.setPublishFrame(info, exch); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.java index f5c2b93f46..6d874ee971 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.java @@ -20,15 +20,19 @@ */ package org.apache.qpid.server.handler; -import java.util.UUID; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.UUID; +import org.apache.log4j.Logger; import org.apache.qpid.AMQException; -import org.apache.qpid.framing.*; -import org.apache.qpid.framing.amqp_0_91.MethodRegistry_0_91; +import org.apache.qpid.framing.ChannelOpenBody; +import org.apache.qpid.framing.ChannelOpenOkBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.framing.ProtocolVersion; import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9; +import org.apache.qpid.framing.amqp_0_91.MethodRegistry_0_91; import org.apache.qpid.framing.amqp_8_0.MethodRegistry_8_0; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.AMQChannel; @@ -39,6 +43,8 @@ import org.apache.qpid.server.virtualhost.VirtualHost; public class ChannelOpenHandler implements StateAwareMethodListener<ChannelOpenBody> { + private static final Logger _logger = Logger.getLogger(ChannelOpenHandler.class); + private static ChannelOpenHandler _instance = new ChannelOpenHandler(); public static ChannelOpenHandler getInstance() @@ -54,19 +60,16 @@ public class ChannelOpenHandler implements StateAwareMethodListener<ChannelOpenB { AMQProtocolSession session = stateManager.getProtocolSession(); VirtualHost virtualHost = session.getVirtualHost(); - // Protect the broker against out of order frame request. if (virtualHost == null) { throw new AMQException(AMQConstant.COMMAND_INVALID, "Virtualhost has not yet been set. ConnectionOpen has not been called.", null); } + _logger.info("Connecting to: " + virtualHost.getName()); - final AMQChannel channel = new AMQChannel(session,channelId, - virtualHost.getMessageStore()); - + final AMQChannel channel = new AMQChannel(session,channelId, virtualHost.getMessageStore()); - session.addChannel(channel); ChannelOpenOkBody response; diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java index b5194084f2..76d1e5378f 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java @@ -20,18 +20,18 @@ */ package org.apache.qpid.server.handler; +import org.apache.log4j.Logger; import org.apache.qpid.AMQException; -import org.apache.qpid.framing.*; -import org.apache.qpid.framing.amqp_0_91.MethodRegistry_0_91; -import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9; -import org.apache.qpid.framing.amqp_8_0.MethodRegistry_8_0; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ConnectionOpenBody; +import org.apache.qpid.framing.MethodRegistry; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.state.AMQState; import org.apache.qpid.server.state.AMQStateManager; import org.apache.qpid.server.state.StateAwareMethodListener; import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.log4j.Logger; public class ConnectionOpenMethodHandler implements StateAwareMethodListener<ConnectionOpenBody> { @@ -57,7 +57,6 @@ public class ConnectionOpenMethodHandler implements StateAwareMethodListener<Con { AMQProtocolSession session = stateManager.getProtocolSession(); - //ignore leading '/' String virtualHostName; if ((body.getVirtualHost() != null) && body.getVirtualHost().charAt(0) == '/') @@ -77,14 +76,15 @@ public class ConnectionOpenMethodHandler implements StateAwareMethodListener<Con } else { - session.setVirtualHost(virtualHost); - - //Perform ACL - if (!virtualHost.getAccessManager().authoriseConnect(session, virtualHost)) + // Check virtualhost access + if (!virtualHost.getSecurityManager().accessVirtualhost(virtualHostName, session.getRemoteAddress().toString())) { - throw body.getConnectionException(AMQConstant.ACCESS_REFUSED, "Permission denied"); + throw body.getConnectionException(AMQConstant.ACCESS_REFUSED, "Permission denied: '" + virtualHost.getName() + "'"); } + session.setVirtualHost(virtualHost); + _logger.error(session.getPrincipal().getName()); + // See Spec (0.8.2). Section 3.1.2 Virtual Hosts if (session.getContextKey() == null) { @@ -97,8 +97,6 @@ public class ConnectionOpenMethodHandler implements StateAwareMethodListener<Con stateManager.changeState(AMQState.CONNECTION_OPEN); session.writeFrame(responseBody.generateFrame(channelId)); - - } } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java index a2a6faf21b..cda8cff25a 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java @@ -97,7 +97,7 @@ public class ConnectionSecureOkMethodHandler implements StateAwareMethodListener ApplicationRegistry.getInstance().getConfiguration().getHeartBeatDelay()); session.writeFrame(tuneBody.generateFrame(0)); session.setAuthorizedID(new UsernamePrincipal(ss.getAuthorizationID())); - disposeSaslServer(session); + disposeSaslServer(session); break; case CONTINUE: stateManager.changeState(AMQState.CONNECTION_NOT_AUTH); diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java index 6698ae888a..6512ff1a14 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java @@ -60,11 +60,11 @@ public class ConnectionStartOkMethodHandler implements StateAwareMethodListener< public void methodReceived(AMQStateManager stateManager, ConnectionStartOkBody body, int channelId) throws AMQException { AMQProtocolSession session = stateManager.getProtocolSession(); - + _logger.info("SASL Mechanism selected: " + body.getMechanism()); _logger.info("Locale selected: " + body.getLocale()); - AuthenticationManager authMgr = ApplicationRegistry.getInstance().getAuthenticationManager();//session.getVirtualHost().getAuthenticationManager(); + AuthenticationManager authMgr = ApplicationRegistry.getInstance().getAuthenticationManager(); SaslServer ss = null; try @@ -73,8 +73,7 @@ public class ConnectionStartOkMethodHandler implements StateAwareMethodListener< if (ss == null) { - throw body.getConnectionException(AMQConstant.RESOURCE_ERROR, "Unable to create SASL Server:" + body.getMechanism() - ); + throw body.getConnectionException(AMQConstant.RESOURCE_ERROR, "Unable to create SASL Server:" + body.getMechanism()); } session.setSaslServer(ss); diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java index 7cfd1fc121..98a0d33487 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java @@ -45,8 +45,6 @@ public class ExchangeDeclareHandler implements StateAwareMethodListener<Exchange return _instance; } - - private ExchangeDeclareHandler() { } @@ -58,28 +56,15 @@ public class ExchangeDeclareHandler implements StateAwareMethodListener<Exchange ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry(); ExchangeFactory exchangeFactory = virtualHost.getExchangeFactory(); - if (!body.getPassive()) - { - // Perform ACL if request is not passive - if (!virtualHost.getAccessManager().authoriseCreateExchange(session, body.getAutoDelete(), - body.getDurable(), body.getExchange(), body.getInternal(), body.getNowait(), body.getPassive(), - body.getType())) - { - throw body.getConnectionException(AMQConstant.ACCESS_REFUSED, "Permission denied"); - } - - } - if (_logger.isDebugEnabled()) { _logger.debug("Request to declare exchange of type " + body.getType() + " with name " + body.getExchange()); } + synchronized(exchangeRegistry) { Exchange exchange = exchangeRegistry.getExchange(body.getExchange()); - - if (exchange == null) { if(body.getPassive() && ((body.getType() == null) || body.getType().length() ==0)) @@ -90,7 +75,6 @@ public class ExchangeDeclareHandler implements StateAwareMethodListener<Exchange { try { - exchange = exchangeFactory.createExchange(body.getExchange() == null ? null : body.getExchange().intern(), body.getType() == null ? null : body.getType().intern(), body.getDurable(), @@ -113,14 +97,12 @@ public class ExchangeDeclareHandler implements StateAwareMethodListener<Exchange throw new AMQConnectionException(AMQConstant.NOT_ALLOWED, "Attempt to redeclare exchange: " + body.getExchange() + " of type " + exchange.getTypeShortString() + " to " + body.getType() +".",body.getClazz(), body.getMethod(),body.getMajor(),body.getMinor(),null); } - } if(!body.getNowait()) { MethodRegistry methodRegistry = session.getMethodRegistry(); AMQMethodBody responseBody = methodRegistry.createExchangeDeclareOkBody(); session.writeFrame(responseBody.generateFrame(channelId)); - } } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.java index 8dbd457cc9..586aaf9336 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.java @@ -27,7 +27,6 @@ import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.exchange.ExchangeInUseException; import org.apache.qpid.server.exchange.ExchangeRegistry; import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.security.access.Permission; import org.apache.qpid.server.state.AMQStateManager; import org.apache.qpid.server.state.StateAwareMethodListener; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -51,13 +50,6 @@ public class ExchangeDeleteHandler implements StateAwareMethodListener<ExchangeD VirtualHost virtualHost = session.getVirtualHost(); ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry(); - //Perform ACLs - if (!virtualHost.getAccessManager().authoriseDelete(session, - exchangeRegistry.getExchange(body.getExchange()))) - { - throw body.getConnectionException(AMQConstant.ACCESS_REFUSED, "Permission denied"); - } - try { if(exchangeRegistry.getExchange(body.getExchange()) == null) @@ -75,6 +67,5 @@ public class ExchangeDeleteHandler implements StateAwareMethodListener<ExchangeD throw body.getChannelException(AMQConstant.IN_USE, "Exchange in use"); // TODO: sort out consistent channel close mechanism that does all clean up etc. } - } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java index 594edb090c..0eb69e4b16 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java @@ -65,7 +65,6 @@ public class QueueBindHandler implements StateAwareMethodListener<QueueBindBody> ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry(); QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); - final AMQQueue queue; final AMQShortString routingKey; @@ -113,14 +112,7 @@ public class QueueBindHandler implements StateAwareMethodListener<QueueBindBody> try { - - //Perform ACLs - if (!virtualHost.getAccessManager().authoriseBind(protocolConnection, exch, - queue, routingKey)) - { - throw body.getConnectionException(AMQConstant.ACCESS_REFUSED, "Permission denied"); - } - else if (queue.isExclusive() && !queue.isDurable()) + if (queue.isExclusive() && !queue.isDurable()) { AMQSessionModel session = queue.getExclusiveOwningSession(); if (session == null || session.getConnectionModel() != protocolConnection) diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java index 4f7d275e71..8939cfa334 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java @@ -69,21 +69,9 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); DurableConfigurationStore store = virtualHost.getDurableConfigurationStore(); - - if (!body.getPassive()) - { - // Perform ACL if request is not passive - if (!virtualHost.getAccessManager().authoriseCreateQueue(protocolConnection, body.getAutoDelete(), body.getDurable(), - body.getExclusive(), body.getNowait(), body.getPassive(), body.getQueue())) - { - throw body.getConnectionException(AMQConstant.ACCESS_REFUSED, "Permission denied"); - } - } - final AMQShortString queueName; // if we aren't given a queue name, we create one which we return to the client - if ((body.getQueue() == null) || (body.getQueue().length() == 0)) { queueName = createName(); @@ -94,11 +82,11 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar } AMQQueue queue; + //TODO: do we need to check that the queue already exists with exactly the same "configuration"? synchronized (queueRegistry) { - queue = queueRegistry.getQueue(queueName); AMQSessionModel owningSession = null; @@ -110,7 +98,6 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar if (queue == null) { - if (body.getPassive()) { String msg = "Queue: " + queueName + " not found on VirtualHost(" + virtualHost + ")."; @@ -129,9 +116,8 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar queue.setDeleteOnNoConsumers(true); } queueRegistry.registerQueue(queue); - if(body.getExclusive()) + if (body.getExclusive()) { - queue.setExclusiveOwningSession(protocolConnection.getChannel(channelId)); queue.setPrincipalHolder(protocolConnection); @@ -153,7 +139,6 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar } }); } - } if (autoRegister) { @@ -239,7 +224,6 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar final AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueName, body.getDurable(), owner, body.getAutoDelete(), body.getExclusive(),virtualHost, body.getArguments()); - if (body.getExclusive() && !body.getDurable()) { final AMQProtocolSession.Task deleteQueueTask = @@ -263,7 +247,7 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar session.removeSessionCloseTask(deleteQueueTask); } }); - }// if exclusive and not durable + } return queue; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java index 4a1940ee01..da52268e52 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java @@ -99,25 +99,18 @@ public class QueueDeleteHandler implements StateAwareMethodListener<QueueDeleteB { // TODO - Error code throw body.getChannelException(AMQConstant.IN_USE, "Queue: " + body.getQueue() + " is still used."); - } else { - AMQSessionModel session = queue.getExclusiveOwningSession(); - //Perform ACLs - if (!virtualHost.getAccessManager().authoriseDelete(protocolConnection, queue)) - { - throw body.getConnectionException(AMQConstant.ACCESS_REFUSED, "Permission denied"); - } - else if (queue.isExclusive() && !queue.isDurable() && (session == null || session.getConnectionModel() != protocolConnection)) + if (queue.isExclusive() && !queue.isDurable() && (session == null || session.getConnectionModel() != protocolConnection)) { throw body.getConnectionException(AMQConstant.NOT_ALLOWED, "Queue " + queue.getNameShortString() + " is exclusive, but not created on this Connection."); } + int purged = queue.delete(); - if (queue.isDurable()) { store.removeQueue(queue); diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java index a465c5a20f..759eec0129 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java @@ -101,12 +101,7 @@ public class QueuePurgeHandler implements StateAwareMethodListener<QueuePurgeBod { AMQSessionModel session = queue.getExclusiveOwningSession(); - //Perform ACLs - if (!virtualHost.getAccessManager().authorisePurge(protocolConnection, queue)) - { - throw body.getConnectionException(AMQConstant.ACCESS_REFUSED, "Permission denied"); - } - else if (queue.isExclusive() && (session == null || session.getConnectionModel() != protocolConnection)) + if (queue.isExclusive() && (session == null || session.getConnectionModel() != protocolConnection)) { throw body.getConnectionException(AMQConstant.NOT_ALLOWED, "Queue is exclusive, but not created on this Connection."); diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java index 29c30de9cc..8391a4b184 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java @@ -20,24 +20,23 @@ package org.apache.qpid.server.handler; * */ - import org.apache.log4j.Logger; - -import org.apache.qpid.framing.*; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.QueueUnbindBody; import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9; -import org.apache.qpid.server.state.StateAwareMethodListener; -import org.apache.qpid.server.state.AMQStateManager; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.security.access.Permission; -import org.apache.qpid.AMQException; -import org.apache.qpid.AMQInvalidRoutingKeyException; -import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.VirtualHost; public class QueueUnbindHandler implements StateAwareMethodListener<QueueUnbindBody> { @@ -100,12 +99,6 @@ public class QueueUnbindHandler implements StateAwareMethodListener<QueueUnbindB throw body.getChannelException(AMQConstant.NOT_FOUND, "Exchange " + body.getExchange() + " does not exist."); } - //Perform ACLs - if (!virtualHost.getAccessManager().authoriseUnbind(session, exch, routingKey, queue)) - { - throw body.getConnectionException(AMQConstant.ACCESS_REFUSED, "Permission denied"); - } - if(virtualHost.getBindingFactory().getBinding(String.valueOf(routingKey), queue, exch, FieldTable.convertToMap(body.getArguments())) == null) { throw body.getChannelException(AMQConstant.NOT_FOUND,"No such binding"); diff --git a/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java b/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java index 0ee5763d91..7e7ac94d06 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java @@ -20,34 +20,36 @@ */ package org.apache.qpid.server.management; -import org.apache.qpid.management.common.mbeans.ConfigurationManagement; -import org.apache.qpid.management.common.mbeans.LoggingManagement; -import org.apache.qpid.management.common.mbeans.UserManagement; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.actors.ManagementActor; -import org.apache.qpid.server.logging.messages.ManagementConsoleMessages; -import org.apache.log4j.Logger; - -import javax.management.remote.MBeanServerForwarder; -import javax.management.remote.JMXPrincipal; -import javax.management.remote.JMXConnectionNotification; -import javax.management.MBeanServer; -import javax.management.ObjectName; -import javax.management.MBeanInfo; -import javax.management.MBeanOperationInfo; -import javax.management.JMException; -import javax.management.NotificationListener; -import javax.management.Notification; -import javax.security.auth.Subject; import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Proxy; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.security.AccessControlContext; import java.security.AccessController; import java.security.Principal; -import java.security.AccessControlContext; -import java.util.HashSet; -import java.util.Set; import java.util.Properties; +import java.util.Set; + +import javax.management.Attribute; +import javax.management.JMException; +import javax.management.MBeanInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanServer; +import javax.management.Notification; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.remote.JMXConnectionNotification; +import javax.management.remote.JMXPrincipal; +import javax.management.remote.MBeanServerForwarder; +import javax.security.auth.Subject; + +import org.apache.log4j.Logger; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.actors.ManagementActor; +import org.apache.qpid.server.logging.messages.ManagementConsoleMessages; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.security.SecurityManager; +import org.apache.qpid.server.security.access.Operation; /** * This class can be used by the JMXConnectorServer as an InvocationHandler for the mbean operations. This implements @@ -65,18 +67,11 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati private MBeanServer _mbs; private static Properties _userRoles = new Properties(); private static ManagementActor _logActor; - - private static HashSet<String> _adminOnlyMethods = new HashSet<String>(); - { - _adminOnlyMethods.add(UserManagement.TYPE); - _adminOnlyMethods.add(LoggingManagement.TYPE); - _adminOnlyMethods.add(ConfigurationManagement.TYPE); - } public static MBeanServerForwarder newProxyInstance() { final InvocationHandler handler = new MBeanInvocationHandlerImpl(); - final Class[] interfaces = new Class[]{MBeanServerForwarder.class}; + final Class<?>[] interfaces = new Class[] { MBeanServerForwarder.class }; _logActor = new ManagementActor(CurrentActor.get().getRootMessageLogger()); @@ -87,7 +82,7 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - final String methodName = method.getName(); + final String methodName = getMethodName(method, args); if (methodName.equals("getMBeanServer")) { @@ -112,145 +107,165 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati AccessControlContext acc = AccessController.getContext(); Subject subject = Subject.getSubject(acc); - // Allow operations performed locally on behalf of the connector server itself - if (subject == null) - { - return method.invoke(_mbs, args); - } - - if (args == null || DELEGATE.equals(args[0])) - { - return method.invoke(_mbs, args); - } - - // Restrict access to "createMBean" and "unregisterMBean" to any user - if (methodName.equals("createMBean") || methodName.equals("unregisterMBean")) - { - _logger.debug("User trying to create or unregister an MBean"); - throw new SecurityException("Access denied"); - } - - // Retrieve JMXPrincipal from Subject - Set<JMXPrincipal> principals = subject.getPrincipals(JMXPrincipal.class); - if (principals == null || principals.isEmpty()) - { - throw new SecurityException("Access denied"); - } - - Principal principal = principals.iterator().next(); - String identity = principal.getName(); - - if (isAdminMethod(args)) + try { - if (isAdmin(identity)) + // Allow operations performed locally on behalf of the connector server itself + if (subject == null) + { + return method.invoke(_mbs, args); + } + + if (args == null || DELEGATE.equals(args[0])) { return method.invoke(_mbs, args); } + + // Restrict access to "createMBean" and "unregisterMBean" to any user + if (methodName.equals("createMBean") || methodName.equals("unregisterMBean")) + { + _logger.debug("User trying to create or unregister an MBean"); + throw new SecurityException("Access denied: " + methodName); + } + + // Allow querying available object names + if (methodName.equals("queryNames")) + { + return method.invoke(_mbs, args); + } + + // Retrieve JMXPrincipal from Subject + Set<JMXPrincipal> principals = subject.getPrincipals(JMXPrincipal.class); + if (principals == null || principals.isEmpty()) + { + throw new SecurityException("Access denied: no principal"); + } + + // Save the principal + Principal principal = principals.iterator().next(); + SecurityManager.setThreadPrincipal(principal); + + // Get the component, type and impact, which may be null + String type = getType(method, args); + String vhost = getVirtualHost(method, args); + int impact = getImpact(method, args); + + // Get the security manager for the virtual host (if set) + SecurityManager security; + if (vhost == null) + { + security = ApplicationRegistry.getInstance().getSecurityManager(); + } else { - throw new SecurityException("Access denied"); + security = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost(vhost).getSecurityManager(); } + + if (isAccessMethod(methodName) || impact == MBeanOperationInfo.INFO) + { + // Check for read-only method invocation permission + if (!security.authoriseMethod(Operation.ACCESS, type, methodName)) + { + throw new SecurityException("Permission denied: Access " + methodName); + } + } + else if (isUpdateMethod(methodName)) + { + // Check for setting properties permission + if (!security.authoriseMethod(Operation.UPDATE, type, methodName)) + { + throw new SecurityException("Permission denied: Update " + methodName); + } + } + else + { + // Check for invoking/executing method action/operation permission + if (!security.authoriseMethod(Operation.EXECUTE, type, methodName)) + { + throw new SecurityException("Permission denied: Execute " + methodName); + } + } + + // Actually invoke the method + return method.invoke(_mbs, args); } - - // Following users can perform any operation other than "createMBean" and "unregisterMBean" - if (isAllowedToModify(identity)) - { - return method.invoke(_mbs, args); - } - - // These users can only call "getAttribute" on the MBeanServerDelegate MBean - // Here we can add other fine grained permissions like specific method for a particular mbean - if (isReadOnlyUser(identity) && isReadOnlyMethod(method, args)) + catch (InvocationTargetException e) { - return method.invoke(_mbs, args); + throw e.getTargetException(); } + } - throw new SecurityException("Access denied"); + private String getType(Method method, Object[] args) + { + if (args[0] instanceof ObjectName) + { + ObjectName object = (ObjectName) args[0]; + String type = object.getKeyProperty("type"); + + return type; + } + return null; } - private boolean isAdminMethod(Object[] args) - { + private String getVirtualHost(Method method, Object[] args) + { if (args[0] instanceof ObjectName) { ObjectName object = (ObjectName) args[0]; + String vhost = object.getKeyProperty("VirtualHost"); - return _adminOnlyMethods.contains(object.getKeyProperty("type")); - } - return false; - } - - // Initialises the user roles - public static void setAccessRights(Properties accessRights) - { - _userRoles = accessRights; - } - - private boolean isAdmin(String userName) - { - if (ADMIN.equals(_userRoles.getProperty(userName))) - { - return true; + return vhost; } - return false; + return null; } - - private boolean isAllowedToModify(String userName) + + private String getMethodName(Method method, Object[] args) { - if (ADMIN.equals(_userRoles.getProperty(userName)) - || READWRITE.equals(_userRoles.getProperty(userName))) - { - return true; - } - return false; - } + String methodName = method.getName(); - private boolean isReadOnlyUser(String userName) - { - if (READONLY.equals(_userRoles.getProperty(userName))) + // if arguments are set, try and work out real method name + if (args != null && args.length >= 1 && args[0] instanceof ObjectName) { - return true; + if (methodName.equals("getAttribute")) + { + methodName = "get" + (String) args[1]; + } + else if (methodName.equals("setAttribute")) + { + methodName = "set" + ((Attribute) args[1]).getName(); + } + else if (methodName.equals("invoke")) + { + methodName = (String) args[1]; + } } - return false; + + return methodName; } - private boolean isReadOnlyMethod(Method method, Object[] args) + private int getImpact(Method method, Object[] args) { - String methodName = method.getName(); - - //handle standard get/set/query and select 'is' methods from MBeanServer - if (methodName.startsWith("query") || methodName.startsWith("get") - ||methodName.startsWith("isInstanceOf") || methodName.startsWith("isRegistered")) - { - return true; - } - else if (methodName.startsWith("set")) - { - return false; - } - //handle invocation of other methods on mbeans - if ((args[0] instanceof ObjectName) && (methodName.equals("invoke"))) + if ((args[0] instanceof ObjectName) && (method.getName().equals("invoke"))) { - //get invoked method name String mbeanMethod = (args.length > 1) ? (String) args[1] : null; if (mbeanMethod == null) { - return false; + return -1; } - + try { - //check if the given method is tagged with an INFO impact attribute + //Get the impact attribute MBeanInfo mbeanInfo = _mbs.getMBeanInfo((ObjectName) args[0]); if (mbeanInfo != null) { MBeanOperationInfo[] opInfos = mbeanInfo.getOperations(); for (MBeanOperationInfo opInfo : opInfos) { - if (opInfo.getName().equals(mbeanMethod) && (opInfo.getImpact() == MBeanOperationInfo.INFO)) + if (opInfo.getName().equals(mbeanMethod)) { - return true; + return opInfo.getImpact(); } } } @@ -261,7 +276,20 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati } } - return false; + return -1; + } + + private boolean isAccessMethod(String methodName) + { + //handle standard get/query/is methods from MBeanServer + return (methodName.startsWith("query") || methodName.startsWith("get") || methodName.startsWith("is")); + } + + + private boolean isUpdateMethod(String methodName) + { + //handle standard set methods from MBeanServer + return methodName.startsWith("set"); } public void handleNotification(Notification notification, Object handback) diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java index 4260307d04..25571f1022 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java @@ -20,16 +20,52 @@ */ package org.apache.qpid.server.protocol; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.security.Principal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; + +import javax.management.JMException; +import javax.security.sasl.SaslServer; + import org.apache.log4j.Logger; import org.apache.mina.transport.vmpipe.VmPipeAddress; - import org.apache.qpid.AMQChannelException; import org.apache.qpid.AMQConnectionException; import org.apache.qpid.AMQException; +import org.apache.qpid.AMQSecurityException; import org.apache.qpid.codec.AMQCodecFactory; import org.apache.qpid.codec.AMQDecoder; import org.apache.qpid.common.ClientProperties; -import org.apache.qpid.framing.*; +import org.apache.qpid.framing.AMQBody; +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.AMQProtocolHeaderException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ChannelCloseBody; +import org.apache.qpid.framing.ChannelCloseOkBody; +import org.apache.qpid.framing.ConnectionCloseBody; +import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.HeartbeatBody; +import org.apache.qpid.framing.MethodDispatcher; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.framing.ProtocolInitiation; +import org.apache.qpid.framing.ProtocolVersion; import org.apache.qpid.pool.Job; import org.apache.qpid.pool.ReferenceCountingExecutorService; import org.apache.qpid.protocol.AMQConstant; @@ -61,25 +97,6 @@ import org.apache.qpid.server.virtualhost.VirtualHostRegistry; import org.apache.qpid.transport.NetworkDriver; import org.apache.qpid.transport.Sender; -import javax.management.JMException; -import javax.security.sasl.SaslServer; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.ByteBuffer; -import java.security.Principal; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; - public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocolSession, ConnectionConfig { private static final Logger _logger = Logger.getLogger(AMQProtocolEngine.class); @@ -117,6 +134,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol private Object _lastSent; protected volatile boolean _closed; + // maximum number of channels this session should have private long _maxNoOfChannels = 1000; @@ -358,14 +376,12 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol public void methodFrameReceived(int channelId, AMQMethodBody methodBody) { - final AMQMethodEvent<AMQMethodBody> evt = new AMQMethodEvent<AMQMethodBody>(channelId, methodBody); try { try { - boolean wasAnyoneInterested = _stateManager.methodReceived(evt); if (!_frameListeners.isEmpty()) @@ -418,10 +434,15 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol _logger.info(e.getMessage() + " whilst processing:" + methodBody); closeConnection(channelId, e, false); } + catch (AMQSecurityException e) + { + AMQConnectionException ce = evt.getMethod().getConnectionException(AMQConstant.ACCESS_REFUSED, e.getMessage()); + _logger.info(e.getMessage() + " whilst processing:" + methodBody); + closeConnection(channelId, ce, false); + } } catch (Exception e) { - for (AMQMethodListener listener : _frameListeners) { listener.error(e); @@ -999,7 +1020,6 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol { if (throwable instanceof AMQProtocolHeaderException) { - writeFrame(new ProtocolInitiation(ProtocolVersion.getLatestSupportedVersion())); _networkDriver.close(); diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java index 366abd3f7c..8b53257e88 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java @@ -21,6 +21,7 @@ package org.apache.qpid.server.queue; import org.apache.qpid.AMQException; +import org.apache.qpid.AMQSecurityException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.protocol.AMQConnectionModel; @@ -194,7 +195,7 @@ public interface AMQQueue extends Managable, Comparable<AMQQueue>, ExchangeRefer void deleteMessageFromTop(); - long clearQueue(); + long clearQueue() throws AMQException; /** * Checks the status of messages on the queue, purging expired ones, firing age related alerts etc. diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java index 3340c1e20a..a547205d27 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java @@ -21,6 +21,7 @@ package org.apache.qpid.server.queue; import org.apache.qpid.AMQException; +import org.apache.qpid.AMQSecurityException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -29,7 +30,6 @@ import org.apache.qpid.server.configuration.QueueConfiguration; import java.util.Map; import java.util.HashMap; - public class AMQQueueFactory { public static final AMQShortString X_QPID_PRIORITIES = new AMQShortString("x-qpid-priorities"); @@ -128,22 +128,20 @@ public class AMQQueueFactory }; - + /** @see #createAMQQueueImpl(String, boolean, String, boolean, boolean, VirtualHost, Map) */ public static AMQQueue createAMQQueueImpl(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, - boolean exclusive, - VirtualHost virtualHost, - final FieldTable arguments) + boolean exclusive, + VirtualHost virtualHost, final FieldTable arguments) throws AMQException { return createAMQQueueImpl(name == null ? null : name.toString(), durable, owner == null ? null : owner.toString(), autoDelete, exclusive, - virtualHost, - FieldTable.convertToMap(arguments)); + virtualHost, FieldTable.convertToMap(arguments)); } @@ -152,8 +150,15 @@ public class AMQQueueFactory String owner, boolean autoDelete, boolean exclusive, - VirtualHost virtualHost, Map<String, Object> arguments) + VirtualHost virtualHost, Map<String, Object> arguments) throws AMQSecurityException { + // Access check + if (!virtualHost.getSecurityManager().authoriseCreateQueue(autoDelete, durable, exclusive, null, null, new AMQShortString(queueName), owner)) + { + String description = "Permission denied: queue-name '" + queueName + "'"; + throw new AMQSecurityException(description); + } + int priorities = 1; String conflationKey = null; if(arguments != null) diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java index af1f412843..806b7f3744 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java @@ -28,6 +28,7 @@ import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.management.common.mbeans.ManagedQueue; import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor; import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; +import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.management.AMQManagedObject; import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.message.ServerMessage; @@ -39,6 +40,7 @@ import org.apache.qpid.server.txn.LocalTransaction; import org.apache.qpid.transport.MessageProperties; import javax.management.JMException; +import javax.management.MBeanException; import javax.management.MBeanNotificationInfo; import javax.management.Notification; import javax.management.OperationsException; @@ -336,7 +338,14 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que */ public Long clearQueue() throws JMException { - return _queue.clearQueue(); + try + { + return _queue.clearQueue(); + } + catch (AMQException ex) + { + throw new MBeanException(ex, "Error clearing queue " + _queueName); + } } /** diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java index afc7fb6480..7e78fd0481 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java @@ -1,11 +1,31 @@ +/* + * 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.queue; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; +import org.apache.qpid.AMQSecurityException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.pool.ReadWriteRunnable; import org.apache.qpid.pool.ReferenceCountingExecutorService; +import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.server.protocol.AMQSessionModel; @@ -50,26 +70,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; -/* -* -* 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. -* -*/ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener { private static final Logger _logger = Logger.getLogger(SimpleAMQQueue.class); @@ -386,9 +386,16 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener // ------ Manage Subscriptions - public synchronized void registerSubscription(final Subscription subscription, final boolean exclusive) throws AMQException + public synchronized void registerSubscription(final Subscription subscription, final boolean exclusive) + throws AMQSecurityException, ExistingExclusiveSubscription, ExistingSubscriptionPreventsExclusive { - + // Access control + if (!getVirtualHost().getSecurityManager().authoriseConsume(this)) + { + throw new AMQSecurityException("Permission denied"); + } + + if (hasExclusiveSubscriber()) { throw new ExistingExclusiveSubscription(); @@ -403,7 +410,6 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener else { _exclusiveSubscriber = subscription; - } } @@ -1212,38 +1218,9 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } - public void purge(final long request) + public void purge(final long request) throws AMQException { - if(request == 0l) - { - clearQueue(); - } - else if(request > 0l) - { - - QueueEntryIterator queueListIterator = _entries.iterator(); - long count = 0; - - ServerTransaction txn = new LocalTransaction(getVirtualHost().getTransactionLog()); - - while (queueListIterator.advance()) - { - QueueEntry node = queueListIterator.getNode(); - if (!node.isDeleted() && node.acquire()) - { - dequeueEntry(node, txn); - if(++count == request) - { - break; - } - } - - } - - txn.commit(); - - - } + clear(request); } public long getCreateTime() @@ -1270,9 +1247,19 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } } - public long clearQueue() - { + public long clearQueue() throws AMQException + { + return clear(0l); + } + private long clear(final long request) throws AMQSecurityException + { + //Perform ACLs + if (!getVirtualHost().getSecurityManager().authorisePurge(this)) + { + throw new AMQSecurityException("Permission denied: queue " + getName()); + } + QueueEntryIterator queueListIterator = _entries.iterator(); long count = 0; @@ -1284,7 +1271,10 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener if (!node.isDeleted() && node.acquire()) { dequeueEntry(node, txn); - count++; + if(++count == request) + { + break; + } } } @@ -1292,7 +1282,6 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener txn.commit(); return count; - } private void dequeueEntry(final QueueEntry node) @@ -1329,12 +1318,18 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener _deleteTaskList.remove(task); } - public int delete() throws AMQException + // TODO list all thrown exceptions + public int delete() throws AMQSecurityException, AMQException { if (!_deleted.getAndSet(true)) { + // Check access + if (!_virtualHost.getSecurityManager().authoriseDelete(this)) + { + throw new AMQSecurityException("Permission denied: " + getName()); + } - for(Binding b : getBindings()) + for (Binding b : getBindings()) { _virtualHost.getBindingFactory().removeBinding(b); } @@ -1606,6 +1601,11 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener public void flushSubscription(Subscription sub) throws AMQException { + // Access control + if (!getVirtualHost().getSecurityManager().authoriseConsume(this)) + { + throw new AMQSecurityException("Permission denied: " + getName()); + } flushSubscription(sub, Long.MAX_VALUE); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java index 4cb3d9e209..9adab58a0c 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java @@ -124,7 +124,6 @@ public abstract class ApplicationRegistry implements IApplicationRegistry @SuppressWarnings("finally") public static void initialise(IApplicationRegistry instance, int instanceID) throws Exception { - _logger.error("initialise(IApplicationRegistry instance, int instanceID)"); if (instance != null) { _logger.info("Initialising Application Registry(" + instance + "):" + instanceID); @@ -142,7 +141,6 @@ public abstract class ApplicationRegistry implements IApplicationRegistry try { - _logger.error("instance.initialise(instanceID)"); instance.initialise(instanceID); } catch (Exception e) @@ -237,12 +235,11 @@ public abstract class ApplicationRegistry implements IApplicationRegistry public void configure() throws ConfigurationException { - _configurationManager = new ConfigurationManager(); try { - _pluginManager = new PluginManager(_configuration.getPluginDirectory()); + _pluginManager = new PluginManager(_configuration.getPluginDirectory(), _configuration.getCacheDirectory()); } catch (Exception e) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/AbstractPlugin.java b/java/broker/src/main/java/org/apache/qpid/server/security/AbstractPlugin.java new file mode 100644 index 0000000000..8c9c2050e8 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/security/AbstractPlugin.java @@ -0,0 +1,69 @@ +/* + * + * 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; + +import org.apache.log4j.Logger; +import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; +import org.apache.qpid.server.security.access.ObjectProperties; +import org.apache.qpid.server.security.access.ObjectType; +import org.apache.qpid.server.security.access.Operation; + +/** + * This is intended as the parent for all simple plugins. + */ +public abstract class AbstractPlugin implements SecurityPlugin +{ + protected final Logger _logger = Logger.getLogger(getClass()); + + public ConfigurationPlugin _config; + + public String getPluginName() + { + return getClass().getSimpleName(); + } + + public Result getDefault() + { + return Result.ABSTAIN; + } + + public abstract Result access(ObjectType object, Object instance); + + public abstract Result authorise(Operation operation, ObjectType object, ObjectProperties properties); + + public boolean isConfigured() + { + if (_config == null) + { + return false; + } + + for (String key : _config.getElementsProcessed()) + { + if (!_config.getConfig().containsKey(key) && _config.getConfig().subset(key).isEmpty()) + { + return false; + } + } + + return true; + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/AbstractProxyPlugin.java b/java/broker/src/main/java/org/apache/qpid/server/security/AbstractProxyPlugin.java new file mode 100644 index 0000000000..7f3b89b46b --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/security/AbstractProxyPlugin.java @@ -0,0 +1,139 @@ +/* + * + * 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; + +import org.apache.commons.configuration.Configuration; +import org.apache.qpid.server.security.access.ObjectProperties; +import org.apache.qpid.server.security.access.ObjectType; +import org.apache.qpid.server.security.access.Operation; + +/** + * This {@link SecurityPlugin} proxies the authorise calls to a serries of methods, one per {@link Operation}. + * + * Plugins that extend this class should override the relevant authorise method and implement their own + * {@link #setConfiguration(Configuration)} method. + */ +public abstract class AbstractProxyPlugin extends AbstractPlugin +{ + public Result authoriseConsume(ObjectType object, ObjectProperties properties) + { + return getDefault(); + } + + public Result authorisePublish(ObjectType object, ObjectProperties properties) + { + return getDefault(); + } + + public Result authoriseCreate(ObjectType object, ObjectProperties properties) + { + return getDefault(); + } + + public Result authoriseAccess(ObjectType object, ObjectProperties properties) + { + return getDefault(); + } + + public Result authoriseBind(ObjectType object, ObjectProperties properties) + { + return getDefault(); + } + + public Result authoriseUnbind(ObjectType object, ObjectProperties properties) + { + return getDefault(); + } + + public Result authoriseDelete(ObjectType object, ObjectProperties properties) + { + return getDefault(); + } + + public Result authorisePurge(ObjectType object, ObjectProperties properties) + { + return getDefault(); + } + + public Result authoriseExecute(ObjectType object, ObjectProperties properties) + { + return getDefault(); + } + + public Result authoriseUpdate(ObjectType object, ObjectProperties properties) + { + return getDefault(); + } + + public Result accessBroker(Object instance) + { + return getDefault(); + } + + public Result accessVirtualhost(Object instance) + { + return getDefault(); + } + + @Override + public Result access(ObjectType objectType, Object instance) + { + switch (objectType) + { + case BROKER: + return accessBroker(instance); + case VIRTUALHOST: + return accessVirtualhost(instance); + } + + return getDefault(); + } + + @Override + public Result authorise(Operation operation, ObjectType objectType, ObjectProperties properties) + { + switch (operation) + { + case CONSUME: + return authoriseConsume(objectType, properties); + case PUBLISH: + return authorisePublish(objectType, properties); + case CREATE: + return authoriseCreate(objectType, properties); + case ACCESS: + return authoriseAccess(objectType, properties); + case BIND: + return authoriseBind(objectType, properties); + case UNBIND: + return authoriseUnbind(objectType, properties); + case DELETE: + return authoriseDelete(objectType, properties); + case PURGE: + return authorisePurge(objectType, properties); + case EXECUTE: + return authoriseExecute(objectType, properties); + case UPDATE: + return authoriseUpdate(objectType, properties); + } + + return getDefault(); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/Result.java b/java/broker/src/main/java/org/apache/qpid/server/security/Result.java new file mode 100644 index 0000000000..f79721799e --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/security/Result.java @@ -0,0 +1,46 @@ +/* + * 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; + +/** + * The result of a security plugin decision, normally {@link #ALLOWED} or {@link #DENIED}. + */ +public enum Result +{ + /** + * The request is allowed. + */ + ALLOWED, + + /** + * The request is denied. + */ + DENIED, + + /** + * Indicates that a plugin does not handle a particular type of request. + */ + ABSTAIN, + + /** + * A plugin instance cannot make a decision on a request it is able to handle, + * and another instance of the plugin should be checked. + */ + DEFER; +}
\ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java new file mode 100755 index 0000000000..035b7fa854 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java @@ -0,0 +1,384 @@ +/* + * 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; + +import static org.apache.qpid.server.security.access.ObjectType.*; +import static org.apache.qpid.server.security.access.Operation.*; + +import java.security.Principal; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.log4j.Logger; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; +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.ObjectProperties; +import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; + +/** + * The security manager contains references to all loaded {@link SecurityPlugin}s and delegates security decisions to them based + * on virtual host name. The plugins can be external <em>OSGi</em> .jar files that export the required classes or just internal + * objects for simpler plugins. + * + * @see SecurityPlugin + */ +public class SecurityManager +{ + private static final Logger _logger = Logger.getLogger(SecurityManager.class); + + /** Container for the {@link Principal} that is using to this thread. */ + private static final ThreadLocal<Principal> _principal = new ThreadLocal<Principal>(); + + private PluginManager _pluginManager; + private Map<String, SecurityPluginFactory> _pluginFactories = new HashMap<String, SecurityPluginFactory>(); + private Map<String, SecurityPlugin> _globalPlugins = new HashMap<String, SecurityPlugin>(); + private Map<String, SecurityPlugin> _hostPlugins = new HashMap<String, SecurityPlugin>(); + + public SecurityManager(SecurityManager parent) throws ConfigurationException + { + _pluginManager = parent._pluginManager; + _pluginFactories = parent._pluginFactories; + + // our global plugins are the parent's host plugins + _globalPlugins = parent._hostPlugins; + } + + public SecurityManager(ConfigurationPlugin configuration, PluginManager manager) throws ConfigurationException + { + this(configuration, manager, null); + } + + public SecurityManager(ConfigurationPlugin configuration, PluginManager manager, SecurityPluginFactory plugin) throws ConfigurationException + { + _pluginManager = manager; + if (manager == null) // No plugin manager, no plugins + { + return; + } + + _pluginFactories = _pluginManager.getSecurityPlugins(); + if (plugin != null) + { + _pluginFactories.put(plugin.getPluginName(), plugin); + } + + configureHostPlugins(configuration); + } + + public static Principal getThreadPrincipal() + { + return _principal.get(); + } + + public static void setThreadPrincipal(Principal principal) + { + _principal.set(principal); + } + + public static void setThreadPrincipal(String authId) + { + setThreadPrincipal(new UsernamePrincipal(authId)); + } + + public void configureHostPlugins(ConfigurationPlugin hostConfig) throws ConfigurationException + { + _hostPlugins = configurePlugins(hostConfig); + } + + public void configureGlobalPlugins(ConfigurationPlugin configuration) throws ConfigurationException + { + _globalPlugins = configurePlugins(configuration); + } + + public Map<String, SecurityPlugin> configurePlugins(ConfigurationPlugin hostConfig) throws ConfigurationException + { + Map<String, SecurityPlugin> plugins = new HashMap<String, SecurityPlugin>(); + for (SecurityPluginFactory<?> factory : _pluginFactories.values()) + { + SecurityPlugin plugin = factory.newInstance(hostConfig); + if (plugin.isConfigured()) + { + plugins.put(factory.getPluginName(), plugin); + } + } + return plugins; + } + + public void addHostPlugin(SecurityPlugin plugin) + { + _hostPlugins.put(plugin.getClass().getName(), plugin); + } + + public static Logger getLogger() + { + return _logger; + } + + private abstract class AccessCheck + { + abstract Result allowed(SecurityPlugin plugin); + } + + private boolean checkAllPlugins(AccessCheck checker) + { + HashMap<String, SecurityPlugin> remainingPlugins = new HashMap<String, SecurityPlugin>(_globalPlugins); + + for (Entry<String, SecurityPlugin> hostEntry : _hostPlugins.entrySet()) + { + // Create set of global only plugins + SecurityPlugin globalPlugin = remainingPlugins.get(hostEntry.getKey()); + if (globalPlugin != null) + { + remainingPlugins.remove(hostEntry.getKey()); + } + + Result host = checker.allowed(hostEntry.getValue()); + + if (host == Result.DENIED) + { + // Something vetoed the access, we're done + return false; + } + + // host allow overrides global allow, so only check global on abstain or defer + if (host != Result.ALLOWED) + { + if (globalPlugin == null) + { + if (host == Result.DEFER) + { + host = hostEntry.getValue().getDefault(); + } + if (host == Result.DENIED) + { + return false; + } + } + else + { + Result global = checker.allowed(globalPlugin); + if (global == Result.DEFER) + { + global = globalPlugin.getDefault(); + } + if (global == Result.ABSTAIN && host == Result.DEFER) + { + global = hostEntry.getValue().getDefault(); + } + if (global == Result.DENIED) + { + return false; + } + } + } + } + + for (SecurityPlugin plugin : remainingPlugins.values()) + { + Result remaining = checker.allowed(plugin); + if (remaining == Result.DEFER) + { + remaining = plugin.getDefault(); + } + if (remaining == Result.DENIED) + { + return false; + } + } + + // getting here means either allowed or abstained from all plugins + return true; + } + + public boolean authoriseBind(final Exchange exch, final AMQQueue queue, final AMQShortString routingKey) + { + return checkAllPlugins(new AccessCheck() + { + Result allowed(SecurityPlugin plugin) + { + return plugin.authorise(BIND, EXCHANGE, new ObjectProperties(exch, queue, routingKey)); + } + }); + } + + // TODO not implemented yet, awaiting consensus + public boolean authoriseObject(final String packageName, final String className) + { + return checkAllPlugins(new AccessCheck() + { + Result allowed(SecurityPlugin plugin) + { + ObjectProperties properties = new ObjectProperties(); + properties.put(ObjectProperties.Property.PACKAGE, packageName); + properties.put(ObjectProperties.Property.CLASS, className); + return plugin.authorise(ACCESS, OBJECT, properties); + } + }); + } + + public boolean authoriseMethod(final Operation operation, final String componentName, final String methodName) + { + return checkAllPlugins(new AccessCheck() + { + Result allowed(SecurityPlugin plugin) + { + ObjectProperties properties = new ObjectProperties(); + properties.setName(methodName); + if (componentName != null) + { + // Only set the property if there is a component name + properties.put(ObjectProperties.Property.COMPONENT, componentName); + } + return plugin.authorise(operation, METHOD, properties); + } + }); + } + + // TODO not implemented yet, awaiting consensus + public boolean accessBroker(final AMQProtocolSession session) + { + return checkAllPlugins(new AccessCheck() + { + Result allowed(SecurityPlugin plugin) + { + return plugin.access(BROKER, session); + } + }); + } + + public boolean accessVirtualhost(final String vhostname, final String remoteAddress) + { + return checkAllPlugins(new AccessCheck() + { + Result allowed(SecurityPlugin plugin) + { + return plugin.access(VIRTUALHOST, remoteAddress); + } + }); + } + + public boolean authoriseConsume(final AMQQueue queue) + { + return checkAllPlugins(new AccessCheck() + { + Result allowed(SecurityPlugin plugin) + { + return plugin.authorise(CONSUME, QUEUE, new ObjectProperties(queue)); + } + }); + } + + public boolean authoriseConsume(final boolean exclusive, final boolean noAck, final boolean noLocal, final boolean nowait, final AMQQueue queue) + { + return checkAllPlugins(new AccessCheck() + { + Result allowed(SecurityPlugin plugin) + { + return plugin.authorise(CONSUME, QUEUE, new ObjectProperties(exclusive, noAck, noLocal, nowait, queue)); + } + }); + } + + public boolean authoriseCreateExchange(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() + { + Result allowed(SecurityPlugin plugin) + { + return plugin.authorise(CREATE, EXCHANGE, new ObjectProperties(autoDelete, durable, exchangeName, + internal, nowait, passive, exchangeType)); + } + }); + } + + public boolean authoriseCreateQueue(final Boolean autoDelete, final Boolean durable, final Boolean exclusive, + final Boolean nowait, final Boolean passive, final AMQShortString queueName, final String owner) + { + return checkAllPlugins(new AccessCheck() + { + Result allowed(SecurityPlugin plugin) + { + return plugin.authorise(CREATE, QUEUE, new ObjectProperties(autoDelete, durable, exclusive, nowait, passive, queueName, owner)); + } + }); + } + + public boolean authoriseDelete(final AMQQueue queue) + { + return checkAllPlugins(new AccessCheck() + { + Result allowed(SecurityPlugin plugin) + { + return plugin.authorise(DELETE, QUEUE, new ObjectProperties(queue)); + } + }); + } + + public boolean authoriseDelete(final Exchange exchange) + { + return checkAllPlugins(new AccessCheck() + { + Result allowed(SecurityPlugin plugin) + { + return plugin.authorise(DELETE, EXCHANGE, new ObjectProperties(exchange.getName())); + } + }); + } + + public boolean authorisePublish(final boolean immediate, final String routingKey, final String exchangeName) + { + return checkAllPlugins(new AccessCheck() + { + Result allowed(SecurityPlugin plugin) + { + return plugin.authorise(PUBLISH, EXCHANGE, new ObjectProperties(exchangeName, routingKey, immediate)); + } + }); + } + + public boolean authorisePurge(final AMQQueue queue) + { + return checkAllPlugins(new AccessCheck() + { + Result allowed(SecurityPlugin plugin) + { + return plugin.authorise(PURGE, QUEUE, new ObjectProperties(queue)); + } + }); + } + + public boolean authoriseUnbind(final Exchange exch, final AMQShortString routingKey, final AMQQueue queue) + { + return checkAllPlugins(new AccessCheck() + { + Result allowed(SecurityPlugin plugin) + { + return plugin.authorise(UNBIND, EXCHANGE, new ObjectProperties(exch, queue, routingKey)); + } + }); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/SecurityPlugin.java b/java/broker/src/main/java/org/apache/qpid/server/security/SecurityPlugin.java new file mode 100644 index 0000000000..c3c06bf206 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/security/SecurityPlugin.java @@ -0,0 +1,47 @@ +/* + * 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; + +import org.apache.qpid.server.plugins.Plugin; +import org.apache.qpid.server.security.access.ObjectProperties; +import org.apache.qpid.server.security.access.ObjectType; +import org.apache.qpid.server.security.access.Operation; + +/** + * The two methods, {@link #access(ObjectType, Object)} and {@link #authorise(Operation, ObjectType, ObjectProperties)}, + * return the {@link Result} of the security decision, which may be to {@link Result#ABSTAIN} if no decision is made + * by this plugin. + */ +public interface SecurityPlugin extends Plugin +{ + /** + * Default result for {@link #access(ObjectType, Object)} or {@link #authorise(Operation, ObjectType, ObjectProperties)}. + */ + Result getDefault(); + + /** + * Authorise access granted to an object instance. + */ + Result access(ObjectType objectType, Object instance); + + /** + * Authorise an operation on an object defined by a set of properties. + */ + Result authorise(Operation operation, ObjectType objectType, ObjectProperties properties); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/SecurityPluginActivator.java b/java/broker/src/main/java/org/apache/qpid/server/security/SecurityPluginActivator.java new file mode 100644 index 0000000000..789bdd0073 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/security/SecurityPluginActivator.java @@ -0,0 +1,54 @@ +package org.apache.qpid.server.security; + +import org.apache.log4j.Logger; +import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +/** + * An OSGi {@link BundleActivator} that loads a {@link SecurityPluginFactory}. + */ +public abstract class SecurityPluginActivator implements BundleActivator +{ + private static final Logger _logger = Logger.getLogger(SecurityPluginActivator.class); + + private SecurityPluginFactory _factory; + private ConfigurationPluginFactory _config; + private BundleContext _ctx; + private String _bundleName; + + /** Implement this to return the factory this plugin activates. */ + public abstract SecurityPluginFactory getFactory(); + + /** Implement this to return the factory this plugin activates. */ + public abstract ConfigurationPluginFactory getConfigurationFactory(); + + /** + * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext ctx) throws Exception + { + _ctx = ctx; + _factory = getFactory(); + _config = getConfigurationFactory(); + _bundleName = ctx.getBundle().getSymbolicName(); + + // register the service + _logger.info("Registering security plugin: " + _bundleName); + _ctx.registerService(SecurityPluginFactory.class.getName(), _factory, null); + _ctx.registerService(ConfigurationPluginFactory.class.getName(), _config, null); + } + + /** + * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception + { + _logger.info("Stopping security plugin: " + _bundleName); + + // null object references + _factory = null; + _config = null; + _ctx = null; + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/AuthorizationManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/SecurityPluginFactory.java index 895ed52222..fe81cba282 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/access/AuthorizationManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/SecurityPluginFactory.java @@ -1,6 +1,5 @@ -package org.apache.qpid.server.security.access; /* - * + * * 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 @@ -8,20 +7,24 @@ package org.apache.qpid.server.security.access; * 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; +import org.apache.qpid.server.plugins.PluginFactory; -public class AuthorizationManager +/** + * The factory that generates instances of security plugins. Usually implemented as a static member class in the plugin itself. + */ +public interface SecurityPluginFactory<S extends SecurityPlugin> extends PluginFactory<S> { - } diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLManager.java deleted file mode 100644 index 7d6ae285c5..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLManager.java +++ /dev/null @@ -1,323 +0,0 @@ -/* - * 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.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.log4j.Logger; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.configuration.SecurityConfiguration; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.plugins.PluginManager; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.security.access.ACLPlugin.AuthzResult; -import org.apache.qpid.server.security.PrincipalHolder; -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 ACLManager(SecurityConfiguration configuration, PluginManager manager) throws ConfigurationException - { - this(configuration, manager, null); - } - - public ACLManager(SecurityConfiguration configuration, PluginManager manager, ACLPluginFactory securityPlugin) throws ConfigurationException - { - _pluginManager = manager; - - if (manager == null) // No plugin manager, no plugins - { - return; - } - - _allSecurityPlugins = _pluginManager.getSecurityPlugins(); - if (securityPlugin != null) - { - _allSecurityPlugins.put(securityPlugin.getClass().getName(), securityPlugin); - } - - configureGlobalPlugins(configuration); - } - - public void configureHostPlugins(SecurityConfiguration hostConfig) throws ConfigurationException - { - _hostPlugins = configurePlugins(hostConfig); - } - - public void configureGlobalPlugins(SecurityConfiguration configuration) throws ConfigurationException - { - _globalPlugins = configurePlugins(configuration); - } - - public Map<String, ACLPlugin> configurePlugins(SecurityConfiguration hostConfig) throws ConfigurationException - { - Configuration securityConfig = hostConfig.getConfiguration(); - Map<String, ACLPlugin> plugins = new HashMap<String, ACLPlugin>(); - Iterator keys = securityConfig.getKeys(); - Collection<String> handledTags = new HashSet(); - while (keys.hasNext()) - { - // 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.info("Plugin handling security section "+tag+" is "+plugin); - 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; - } - - public static Logger getLogger() - { - return _logger; - } - - private abstract class AccessCheck - { - abstract AuthzResult allowed(ACLPlugin plugin); - } - - 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; - } - - public boolean authoriseBind(final PrincipalHolder session, final Exchange exch, final AMQQueue queue, - final AMQShortString routingKey) - { - return checkAllPlugins(new AccessCheck() - { - - @Override - AuthzResult allowed(ACLPlugin plugin) - { - return plugin.authoriseBind(session, exch, queue, routingKey); - } - - }); - } - - public boolean authoriseConnect(final PrincipalHolder session, final VirtualHost virtualHost) - { - return checkAllPlugins(new AccessCheck() - { - - @Override - AuthzResult allowed(ACLPlugin plugin) - { - return plugin.authoriseConnect(session, virtualHost); - } - - }); - } - - public boolean authoriseConsume(final PrincipalHolder session, final boolean noAck, final AMQQueue queue) - { - return checkAllPlugins(new AccessCheck() - { - - @Override - AuthzResult allowed(ACLPlugin plugin) - { - return plugin.authoriseConsume(session, noAck, queue); - } - - }); - } - - public boolean authoriseConsume(final PrincipalHolder 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) - { - return plugin.authoriseConsume(session, exclusive, noAck, noLocal, nowait, queue); - } - - }); - } - - public boolean authoriseCreateExchange(final PrincipalHolder 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) - { - return plugin.authoriseCreateExchange(session, autoDelete, durable, exchangeName, internal, nowait, - passive, exchangeType); - } - - }); - } - - public boolean authoriseCreateQueue(final PrincipalHolder 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) - { - return plugin.authoriseCreateQueue(session, autoDelete, durable, exclusive, nowait, passive, queue); - } - - }); - } - - public boolean authoriseDelete(final PrincipalHolder session, final AMQQueue queue) - { - return checkAllPlugins(new AccessCheck() - { - - @Override - AuthzResult allowed(ACLPlugin plugin) - { - return plugin.authoriseDelete(session, queue); - } - - }); - } - - public boolean authoriseDelete(final PrincipalHolder session, final Exchange exchange) - { - return checkAllPlugins(new AccessCheck() - { - - @Override - AuthzResult allowed(ACLPlugin plugin) - { - return plugin.authoriseDelete(session, exchange); - } - - }); - } - - public boolean authorisePublish(final PrincipalHolder session, final boolean immediate, final boolean mandatory, - final AMQShortString routingKey, final Exchange e) - { - return checkAllPlugins(new AccessCheck() - { - - @Override - AuthzResult allowed(ACLPlugin plugin) - { - return plugin.authorisePublish(session, immediate, mandatory, routingKey, e); - } - - }); - } - - public boolean authorisePurge(final PrincipalHolder session, final AMQQueue queue) - { - return checkAllPlugins(new AccessCheck() - { - - @Override - AuthzResult allowed(ACLPlugin plugin) - { - return plugin.authorisePurge(session, queue); - } - - }); - } - - public boolean authoriseUnbind(final PrincipalHolder session, final Exchange exch, - final AMQShortString routingKey, final AMQQueue queue) - { - return checkAllPlugins(new AccessCheck() - { - - @Override - AuthzResult allowed(ACLPlugin plugin) - { - return plugin.authoriseUnbind(session, exch, routingKey, queue); - } - - }); - } - - public void addHostPlugin(ACLPlugin aclPlugin) - { - _hostPlugins.put(aclPlugin.getClass().getName(), aclPlugin); - } -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPlugin.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPlugin.java deleted file mode 100644 index cf8a3fede9..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPlugin.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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.commons.configuration.ConfigurationException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.security.PrincipalHolder; - -public interface ACLPlugin -{ - public enum AuthzResult - { - ALLOWED, - DENIED, - ABSTAIN - } - - void setConfiguration(Configuration config) throws ConfigurationException; - - // These return true if the plugin thinks the action should be allowed, and false if not. - - AuthzResult authoriseBind(PrincipalHolder session, Exchange exch, AMQQueue queue, AMQShortString routingKey); - - AuthzResult authoriseCreateExchange(PrincipalHolder session, boolean autoDelete, boolean durable, - AMQShortString exchangeName, boolean internal, boolean nowait, boolean passive, AMQShortString exchangeType); - - AuthzResult authoriseCreateQueue(PrincipalHolder session, boolean autoDelete, boolean durable, boolean exclusive, - boolean nowait, boolean passive, AMQShortString queue); - - AuthzResult authoriseConnect(PrincipalHolder session, VirtualHost virtualHost); - - AuthzResult authoriseConsume(PrincipalHolder session, boolean noAck, AMQQueue queue); - - AuthzResult authoriseConsume(PrincipalHolder session, boolean exclusive, boolean noAck, boolean noLocal, - boolean nowait, AMQQueue queue); - - AuthzResult authoriseDelete(PrincipalHolder session, AMQQueue queue); - - AuthzResult authoriseDelete(PrincipalHolder session, Exchange exchange); - - AuthzResult authorisePublish(PrincipalHolder session, boolean immediate, boolean mandatory, - AMQShortString routingKey, Exchange e); - - AuthzResult authorisePurge(PrincipalHolder session, AMQQueue queue); - - AuthzResult authoriseUnbind(PrincipalHolder session, Exchange exch, AMQShortString routingKey, AMQQueue queue); - -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPluginFactory.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPluginFactory.java deleted file mode 100644 index 256f093477..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPluginFactory.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * - * 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.commons.configuration.ConfigurationException; - -public interface ACLPluginFactory -{ - - public boolean supportsTag(String name); - - public ACLPlugin newInstance(Configuration config) throws ConfigurationException; - -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java deleted file mode 100644 index d722da4ae0..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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; - -public class AccessResult -{ - public enum AccessStatus - { - GRANTED, REFUSED - } - - private String _authorizer; - private AccessStatus _status; - - public AccessResult(ACLPlugin authorizer, AccessStatus status) - { - _status = status; - _authorizer = authorizer.getClass().getSimpleName(); - } - - public void setAuthorizer(ACLPlugin authorizer) - { - _authorizer += authorizer.getClass().getSimpleName(); - } - - public String getAuthorizer() - { - return _authorizer; - } - - public void setStatus(AccessStatus status) - { - _status = status; - } - - public AccessStatus getStatus() - { - return _status; - } - - public void addAuthorizer(ACLPlugin accessManager) - { - _authorizer = accessManager.getClass().getSimpleName() + "->" + _authorizer; - } - - -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessRights.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessRights.java deleted file mode 100644 index 1b79a5a0e0..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessRights.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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; - -public class AccessRights -{ - public enum Rights - { - ANY, - READ, - WRITE, - READWRITE - } - - Rights _right; - - public AccessRights(Rights right) - { - _right = right; - } - - public boolean allows(Rights rights) - { - switch (_right) - { - case ANY: - return (rights.equals(Rights.WRITE) - || rights.equals(Rights.READ) - || rights.equals(Rights.READWRITE) - || rights.equals(Rights.ANY)); - case READ: - return rights.equals(Rights.READ) || rights.equals(Rights.ANY); - case WRITE: - return rights.equals(Rights.WRITE) || rights.equals(Rights.ANY); - case READWRITE: - return true; - } - return false; - } - - public Rights getRights() - { - return _right; - } -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectProperties.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectProperties.java new file mode 100644 index 0000000000..af47ed6bf9 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectProperties.java @@ -0,0 +1,315 @@ +/* + * 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.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.queue.AMQQueue; + +/** + * An set of properties for an access control v2 rule {@link ObjectType}. + * + * The {@link #matches(ObjectProperties)} method is intended to be used when determining precedence of rules, and + * {@link #equals(Object)} and {@link #hashCode()} are intended for use in maps. This is due to the wildcard matching + * described above. + */ +public class ObjectProperties extends HashMap<ObjectProperties.Property, String> +{ + /** serialVersionUID */ + private static final long serialVersionUID = -1356019341374170495L; + + public static final String STAR= "*"; + + public static final ObjectProperties EMPTY = new ObjectProperties(); + + public enum Property + { + ROUTING_KEY, + NAME, + QUEUE_NAME, + OWNER, + TYPE, + ALTERNATE, + IMMEDIATE, + INTERNAL, + NO_WAIT, + NO_LOCAL, + NO_ACK, + PASSIVE, + DURABLE, + EXCLUSIVE, + TEMPORARY, + AUTO_DELETE, + COMPONENT, + PACKAGE, + CLASS; + + public static Property parse(String text) + { + for (Property property : values()) + { + if (property.getName().equalsIgnoreCase(text)) + { + return property; + } + } + throw new IllegalArgumentException("Not a valid property: " + text); + } + + public String getName() + { + return StringUtils.remove(name(), '_').toLowerCase(); + } + + public static List<String> getPropertyNames() + { + List<String> properties = new ArrayList<String>(); + for (Property property : values()) + { + properties.add(property.getName()); + } + return properties; + } + } + + public static List<String> getAllPropertyNames() + { + List<String> properties = new ArrayList<String>(); + for (Property property : Property.values()) + { + properties.add(StringUtils.remove(property.name(), '_').toLowerCase()); + } + return properties; + } + + public ObjectProperties() + { + super(); + } + + public ObjectProperties(ObjectProperties copy) + { + super(); + + putAll(copy); + } + + public ObjectProperties(String name) + { + super(); + + setName(name); + } + + + public ObjectProperties(AMQShortString name) + { + super(); + + setName(name); + } + + public ObjectProperties(AMQQueue queue) + { + super(); + + setName(queue.getName()); + + put(Property.AUTO_DELETE, queue.isAutoDelete()); + put(Property.TEMPORARY, queue.isAutoDelete()); + put(Property.DURABLE, queue.isDurable()); + put(Property.EXCLUSIVE, queue.isExclusive()); + if (queue.getAlternateExchange() != null) + { + put(Property.ALTERNATE, queue.getAlternateExchange().getName()); + } + if (queue.getOwner() != null) + { + put(Property.OWNER, queue.getOwner()); + } + else if (queue.getPrincipalHolder() != null) + { + put(Property.OWNER, queue.getPrincipalHolder().getPrincipal().getName()); + } + } + + public ObjectProperties(Exchange exch, AMQQueue queue, AMQShortString routingKey) + { + this(queue); + + setName(exch.getName()); + + put(Property.QUEUE_NAME, queue.getName()); + put(Property.ROUTING_KEY, routingKey); + } + + public ObjectProperties(Exchange exch, AMQShortString routingKey) + { + this(exch.getName(), routingKey.asString()); + } + + public ObjectProperties(String exchangeName, String routingKey, Boolean immediate) + { + this(exchangeName, routingKey); + + put(Property.IMMEDIATE, immediate); + } + + public ObjectProperties(String exchangeName, String routingKey) + { + super(); + + setName(exchangeName); + + put(Property.ROUTING_KEY, routingKey); + } + + public ObjectProperties(Boolean autoDelete, Boolean durable, AMQShortString exchangeName, + Boolean internal, Boolean nowait, Boolean passive, AMQShortString exchangeType) + { + super(); + + setName(exchangeName); + + put(Property.AUTO_DELETE, autoDelete); + put(Property.TEMPORARY, autoDelete); + put(Property.DURABLE, durable); + put(Property.INTERNAL, internal); + put(Property.NO_WAIT, nowait); + put(Property.PASSIVE, passive); + put(Property.TYPE, exchangeType); + } + + public ObjectProperties(Boolean autoDelete, Boolean durable, Boolean exclusive, Boolean nowait, Boolean passive, + AMQShortString queueName, String owner) + { + super(); + + setName(queueName); + + put(Property.AUTO_DELETE, autoDelete); + put(Property.TEMPORARY, autoDelete); + put(Property.DURABLE, durable); + put(Property.EXCLUSIVE, exclusive); + put(Property.NO_WAIT, nowait); + put(Property.PASSIVE, passive); + put(Property.OWNER, owner); + } + + public ObjectProperties(Boolean exclusive, Boolean noAck, Boolean noLocal, Boolean nowait, AMQQueue queue) + { + this(queue); + + put(Property.NO_LOCAL, noLocal); + put(Property.NO_ACK, noAck); + put(Property.EXCLUSIVE, exclusive); + put(Property.NO_WAIT, nowait); + } + + public List<String> getPropertyNames() + { + List<String> properties = new ArrayList<String>(); + for (Property property : keySet()) + { + properties.add(property.getName()); + } + return properties; + } + + public Boolean isSet(Property key) + { + return containsKey(key) && Boolean.valueOf(get(key)); + } + + public String getName() + { + return get(Property.NAME); + } + + public void setName(String name) + { + put(Property.NAME, name); + } + + public void setName(AMQShortString name) + { + put(Property.NAME, name); + } + + public String put(Property key, AMQShortString value) + { + return put(key, value == null ? "" : value.asString()); + } + + @Override + public String put(Property key, String value) + { + return super.put(key, value == null ? "" : value.trim()); + } + + public void put(Property key, Boolean value) + { + if (value != null) + { + super.put(key, Boolean.toString(value)); + } + } + + public boolean matches(ObjectProperties properties) + { + if (properties.keySet().isEmpty()) + { + return true; + } + + if (!keySet().containsAll(properties.keySet())) + { + return false; + } + + for (Property key : properties.keySet()) + { + String ruleValue = properties.get(key); + String thisValue = get(key); + + if (!valueMatches(thisValue, ruleValue)) + { + return false; + } + } + + return true; + } + + private boolean valueMatches(String thisValue, String ruleValue) + { + return (StringUtils.isEmpty(ruleValue) + || StringUtils.equals(thisValue, ruleValue)) + || ruleValue.equals(STAR) + || (ruleValue.endsWith(STAR) + && thisValue != null + && thisValue.length() > ruleValue.length() + && thisValue.startsWith(ruleValue.substring(0, ruleValue.length() - 2))); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectType.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectType.java new file mode 100644 index 0000000000..66ef388976 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectType.java @@ -0,0 +1,90 @@ +/* + * 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 static org.apache.qpid.server.security.access.Operation.*; + +import java.util.EnumSet; +import java.util.Set; + +/** + * An enumeration of all possible object types that can form part of an access control v2 rule. + * + * Each object type is valid only for a certain set of {@link Operation}s, which are passed as a list to + * the constructor, and can be checked using the {@link #isAllowed(Operation)} method. + */ +public enum ObjectType +{ + ALL(Operation.ALL), + VIRTUALHOST(ACCESS), + QUEUE(CREATE, DELETE, PURGE, CONSUME), + TOPIC(CREATE, DELETE, PURGE, CONSUME), + EXCHANGE(ACCESS, CREATE, DELETE, BIND, UNBIND, PUBLISH), + BROKER(ACCESS), + LINK, // Not allowed in the Java broker + ROUTE, // Not allowed in the Java broker + METHOD(Operation.ALL, ACCESS, UPDATE, EXECUTE), + OBJECT(ACCESS); + + private EnumSet<Operation> _actions; + + private ObjectType() + { + _actions = EnumSet.noneOf(Operation.class); + } + + private ObjectType(Operation operation) + { + if (operation == Operation.ALL) + { + _actions = EnumSet.allOf(Operation.class); + } + else + { + _actions = EnumSet.of(operation); + } + } + + private ObjectType(Operation first, Operation...rest) + { + _actions = EnumSet.of(first, rest); + } + + public Set<Operation> getActions() + { + return _actions; + } + + public boolean isAllowed(Operation operation) + { + return _actions.contains(operation); + } + + public static ObjectType parse(String text) + { + for (ObjectType object : values()) + { + if (object.name().equalsIgnoreCase(text)) + { + return object; + } + } + throw new IllegalArgumentException("Not a valid object type: " + text); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/security/access/ExchangeDenier.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/Operation.java index 37a0fd7fc3..7077257d01 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/security/access/ExchangeDenier.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/Operation.java @@ -15,35 +15,35 @@ * 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.security.access.plugins.AllowAll; -import org.apache.qpid.server.security.PrincipalHolder; - -public class ExchangeDenier extends AllowAll +/** + * An enumeration of all possible actions that can form part of an access control v2 rule. + */ +public enum Operation { - - public static final ACLPluginFactory FACTORY = new ACLPluginFactory() + ALL, + CONSUME, + PUBLISH, + CREATE, + ACCESS, + BIND, + UNBIND, + DELETE, + PURGE, + UPDATE, + EXECUTE; + + public static Operation parse(String text) { - public boolean supportsTag(String name) + for (Operation operation : values()) { - return name.startsWith("exchangeDenier"); + if (operation.name().equalsIgnoreCase(text)) + { + return operation; + } } - - public ACLPlugin newInstance(Configuration config) - { - return new ExchangeDenier(); - } - }; - - @Override - public AuthzResult authoriseDelete(PrincipalHolder session, Exchange exchange) - { - return AuthzResult.DENIED; + throw new IllegalArgumentException("Not a valid operation: " + text); } -} +}
\ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/Permission.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/Permission.java index b65b0cdc6c..49b3a331f9 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/access/Permission.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/Permission.java @@ -20,19 +20,28 @@ */ package org.apache.qpid.server.security.access; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.queue.AMQQueue; +import org.apache.commons.lang.StringUtils; +/** + * An enumeration of all possible permissions that can be applied to an access control v2 rule. + */ public enum Permission { - CONSUME, - PUBLISH, - CREATEQUEUE, - CREATEEXCHANGE, - ACCESS, - BIND, - UNBIND, - DELETE, - PURGE -} + ALLOW, + ALLOW_LOG, + DENY, + DENY_LOG; + + public static Permission parse(String text) + { + + for (Permission permission : values()) + { + if (permission.name().equalsIgnoreCase(StringUtils.replaceChars(text, '-', '_'))) + { + return permission; + } + } + throw new IllegalArgumentException("Not a valid permission: " + text); + } +}
\ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/VirtualHostAccess.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/VirtualHostAccess.java deleted file mode 100644 index 13151a66b8..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/security/access/VirtualHostAccess.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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; - -public class VirtualHostAccess -{ - private String _vhost; - private AccessRights _rights; - - public VirtualHostAccess(String vhostaccess) - { - //format <vhost>(<rights>) - int hostend = vhostaccess.indexOf('('); - - if (hostend == -1) - { - throw new IllegalArgumentException("VirtualHostAccess format string contains no access _rights"); - } - - _vhost = vhostaccess.substring(0, hostend); - - String rights = vhostaccess.substring(hostend); - - if (rights.indexOf('r') != -1) - { - if (rights.indexOf('w') != -1) - { - _rights = new AccessRights(AccessRights.Rights.READWRITE); - } - else - { - _rights = new AccessRights(AccessRights.Rights.READ); - } - } - else if (rights.indexOf('w') != -1) - { - _rights = new AccessRights(AccessRights.Rights.WRITE); - } - } - - public AccessRights getAccessRights() - { - return _rights; - } - - public String getVirtualHost() - { - return _vhost; - } -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AbstractACLPlugin.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AbstractACLPlugin.java deleted file mode 100644 index f99f3a60f7..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AbstractACLPlugin.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * - * 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.plugins; - -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.security.access.ACLPlugin; -import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.security.PrincipalHolder; - -/** - * This ACLPlugin abstains from all votes. Useful if your plugin only cares about a few operations. - */ -public abstract class AbstractACLPlugin implements ACLPlugin -{ - - private static final AuthzResult DEFAULT_ANSWER = AuthzResult.ABSTAIN; - - public AuthzResult authoriseBind(PrincipalHolder session, Exchange exch, AMQQueue queue, - AMQShortString routingKey) - { - return DEFAULT_ANSWER; - } - - public AuthzResult authoriseConnect(PrincipalHolder session, VirtualHost virtualHost) - { - return DEFAULT_ANSWER; - } - - public AuthzResult authoriseConsume(PrincipalHolder session, boolean noAck, AMQQueue queue) - { - return DEFAULT_ANSWER; - } - - public AuthzResult authoriseConsume(PrincipalHolder session, boolean exclusive, boolean noAck, boolean noLocal, - boolean nowait, AMQQueue queue) - { - return DEFAULT_ANSWER; - } - - public AuthzResult authoriseCreateExchange(PrincipalHolder session, boolean autoDelete, boolean durable, - AMQShortString exchangeName, boolean internal, boolean nowait, boolean passive, AMQShortString exchangeType) - { - // TODO Auto-generated method stub - return null; - } - - public AuthzResult authoriseCreateQueue(PrincipalHolder session, boolean autoDelete, boolean durable, - boolean exclusive, boolean nowait, boolean passive, AMQShortString queue) - { - return DEFAULT_ANSWER; - } - - public AuthzResult authoriseDelete(PrincipalHolder session, AMQQueue queue) - { - return DEFAULT_ANSWER; - } - - public AuthzResult authoriseDelete(PrincipalHolder session, Exchange exchange) - { - return DEFAULT_ANSWER; - } - - public AuthzResult authorisePublish(PrincipalHolder session, boolean immediate, boolean mandatory, - AMQShortString routingKey, Exchange e) - { - return DEFAULT_ANSWER; - } - - public AuthzResult authorisePurge(PrincipalHolder session, AMQQueue queue) - { - return DEFAULT_ANSWER; - } - - public AuthzResult authoriseUnbind(PrincipalHolder session, Exchange exch, AMQShortString routingKey, - AMQQueue queue) - { - return DEFAULT_ANSWER; - } -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java index 4af178574b..82963bbadc 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java @@ -15,40 +15,72 @@ * 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.plugins; +import java.util.Arrays; +import java.util.List; + import org.apache.commons.configuration.Configuration; -import org.apache.qpid.server.security.access.ACLPlugin; -import org.apache.qpid.server.security.access.ACLPluginFactory; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; +import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory; +import org.apache.qpid.server.security.Result; +import org.apache.qpid.server.security.SecurityPluginFactory; -public class AllowAll extends BasicACLPlugin +/** Always allow. */ +public class AllowAll extends BasicPlugin { + public static class AllowAllConfiguration extends ConfigurationPlugin { + public static final ConfigurationPluginFactory FACTORY = new ConfigurationPluginFactory() + { + public List<String> getParentPaths() + { + return Arrays.asList("security", "virtualhosts.virtualhost.security"); + } - public static final ACLPluginFactory FACTORY = new ACLPluginFactory() + public ConfigurationPlugin newInstance(String path, Configuration config) throws ConfigurationException + { + ConfigurationPlugin instance = new AllowAllConfiguration(); + instance.setConfiguration(path, config); + return instance; + } + }; + + public String[] getElementsProcessed() + { + return new String[] { "allow-all" }; + } + } + + public static final SecurityPluginFactory<AllowAll> FACTORY = new SecurityPluginFactory<AllowAll>() { - public boolean supportsTag(String name) + public AllowAll newInstance(ConfigurationPlugin config) throws ConfigurationException { - return false; + AllowAll plugin = new AllowAll(config); + plugin.configure(); + return plugin; } - public ACLPlugin newInstance(Configuration config) + public String getPluginName() { - return new AllowAll(); + return AllowAll.class.getName(); } - }; - public String getPluginName() - { - return this.getClass().getSimpleName(); + public Class<AllowAll> getPluginClass() + { + return AllowAll.class; + } + }; + + @Override + public Result getDefault() + { + return Result.ALLOWED; } - @Override - protected AuthzResult getResult() + public AllowAll(ConfigurationPlugin config) { - // Always allow - return AuthzResult.ALLOWED; + _config = config.getConfiguration(AllowAllConfiguration.class); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicACLPlugin.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicACLPlugin.java deleted file mode 100644 index d0df354d78..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicACLPlugin.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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.plugins; - -import org.apache.commons.configuration.Configuration; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.security.access.ACLPlugin; -import org.apache.qpid.server.security.PrincipalHolder; -import org.apache.qpid.server.virtualhost.VirtualHost; - -public abstract class BasicACLPlugin implements ACLPlugin -{ - - // Returns true or false if the plugin should authorise or deny the request - protected abstract AuthzResult getResult(); - - public AuthzResult authoriseBind(PrincipalHolder session, Exchange exch, - AMQQueue queue, AMQShortString routingKey) - { - return getResult(); - } - - public AuthzResult authoriseConnect(PrincipalHolder session, - VirtualHost virtualHost) - { - return getResult(); - } - - public AuthzResult authoriseConsume(PrincipalHolder session, boolean noAck, - AMQQueue queue) - { - return getResult(); - } - - public AuthzResult authoriseConsume(PrincipalHolder session, - boolean exclusive, boolean noAck, boolean noLocal, boolean nowait, - AMQQueue queue) - { - return getResult(); - } - - public AuthzResult authoriseCreateExchange(PrincipalHolder session, - boolean autoDelete, boolean durable, AMQShortString exchangeName, - boolean internal, boolean nowait, boolean passive, - AMQShortString exchangeType) - { - return getResult(); - } - - public AuthzResult authoriseCreateQueue(PrincipalHolder session, - boolean autoDelete, boolean durable, boolean exclusive, - boolean nowait, boolean passive, AMQShortString queue) - { - return getResult(); - } - - public AuthzResult authoriseDelete(PrincipalHolder session, AMQQueue queue) - { - return getResult(); - } - - public AuthzResult authoriseDelete(PrincipalHolder session, Exchange exchange) - { - return getResult(); - } - - public AuthzResult authorisePublish(PrincipalHolder session, - boolean immediate, boolean mandatory, AMQShortString routingKey, - Exchange e) - { - return getResult(); - } - - public AuthzResult authorisePurge(PrincipalHolder session, AMQQueue queue) - { - return getResult(); - } - - public AuthzResult authoriseUnbind(PrincipalHolder session, Exchange exch, - AMQShortString routingKey, AMQQueue queue) - { - return getResult(); - } - - public void setConfiguration(Configuration config) - { - // no-op - } - -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicPlugin.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicPlugin.java new file mode 100644 index 0000000000..5fc1ef7795 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicPlugin.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.security.access.plugins; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.server.security.AbstractPlugin; +import org.apache.qpid.server.security.Result; +import org.apache.qpid.server.security.SecurityPlugin; +import org.apache.qpid.server.security.access.ObjectProperties; +import org.apache.qpid.server.security.access.ObjectType; +import org.apache.qpid.server.security.access.Operation; + +/** + * This {@link SecurityPlugin} simply abstains from all authorisation requests and ignores configuration. + */ +public class BasicPlugin extends AbstractPlugin +{ + public Result access(ObjectType objectType, Object instance) + { + return getDefault(); + } + + public Result authorise(Operation operation, ObjectType objectType, ObjectProperties properties) + { + return getDefault(); + } + + @Override + public void configure() throws ConfigurationException + { + // Not used + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/DenyAll.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/DenyAll.java index 77d3c4bcdf..24af215a0c 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/DenyAll.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/DenyAll.java @@ -15,61 +15,72 @@ * 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.plugins; +import java.util.Arrays; +import java.util.List; + import org.apache.commons.configuration.Configuration; -import org.apache.qpid.AMQConnectionException; -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; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; +import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory; +import org.apache.qpid.server.security.Result; +import org.apache.qpid.server.security.SecurityPluginFactory; -public class DenyAll extends BasicACLPlugin +/** Always Deny. */ +public class DenyAll extends BasicPlugin { - public static final ACLPluginFactory FACTORY = new ACLPluginFactory() - { - public boolean supportsTag(String name) + public static class DenyAllConfiguration extends ConfigurationPlugin { + public static final ConfigurationPluginFactory FACTORY = new ConfigurationPluginFactory() { - return false; - } + public List<String> getParentPaths() + { + return Arrays.asList("security", "virtualhosts.virtualhost.security"); + } - public ACLPlugin newInstance(Configuration config) + public ConfigurationPlugin newInstance(String path, Configuration config) throws ConfigurationException + { + ConfigurationPlugin instance = new DenyAllConfiguration(); + instance.setConfiguration(path, config); + return instance; + } + }; + + public String[] getElementsProcessed() { - return new DenyAll(); + return new String[] { "deny-all" }; } - }; + } - public AccessResult authorise(AMQProtocolSession session, - Permission permission, AMQMethodBody body, Object... parameters) - throws AMQConnectionException + public static final SecurityPluginFactory<DenyAll> FACTORY = new SecurityPluginFactory<DenyAll>() { + public DenyAll newInstance(ConfigurationPlugin config) throws ConfigurationException + { + DenyAll plugin = new DenyAll(config); + plugin.configure(); + return plugin; + } - if (ACLManager.getLogger().isInfoEnabled()) + public String getPluginName() { - ACLManager.getLogger().info( - "Denying user:" + session.getPrincipal()); + return DenyAll.class.getName(); } - throw body.getConnectionException(AMQConstant.ACCESS_REFUSED, - "DenyAll Plugin"); - } - public String getPluginName() - { - return getClass().getSimpleName(); + public Class<DenyAll> getPluginClass() + { + return DenyAll.class; + } + }; + + @Override + public Result getDefault() + { + return Result.DENIED; } - @Override - protected AuthzResult getResult() + public DenyAll(ConfigurationPlugin config) throws ConfigurationException { - // Always deny - return AuthzResult.DENIED; + _config = config.getConfiguration(DenyAllConfiguration.class); } - } diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/LegacyAccess.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/LegacyAccess.java new file mode 100644 index 0000000000..2c0994b52a --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/LegacyAccess.java @@ -0,0 +1,81 @@ +/* + * 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.plugins; + +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; +import org.apache.qpid.server.configuration.VirtualHostConfiguration; +import org.apache.qpid.server.configuration.ServerConfiguration; +import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory; +import org.apache.qpid.server.security.SecurityPluginFactory; + +/** Always Abstain. */ +public class LegacyAccess extends BasicPlugin +{ + public static class LegacyAccessConfiguration extends ConfigurationPlugin { + public static final ConfigurationPluginFactory FACTORY = new ConfigurationPluginFactory() + { + public List<String> getParentPaths() + { + return Arrays.asList("security", "virtualhosts.virtualhost.security"); + } + + public ConfigurationPlugin newInstance(String path, Configuration config) throws ConfigurationException + { + ConfigurationPlugin instance = new LegacyAccessConfiguration(); + instance.setConfiguration(path, config); + return instance; + } + }; + + public String[] getElementsProcessed() + { + return new String[] { "principal-databases", "access", "msg-auth", "false", "jmx" }; + } + } + + public static final SecurityPluginFactory<LegacyAccess> FACTORY = new SecurityPluginFactory<LegacyAccess>() + { + public LegacyAccess newInstance(ConfigurationPlugin config) throws ConfigurationException + { + LegacyAccess plugin = new LegacyAccess(config); + plugin.configure(); + return plugin; + } + + public String getPluginName() + { + return LegacyAccess.class.getName(); + } + + public Class<LegacyAccess> getPluginClass() + { + return LegacyAccess.class; + } + }; + + public LegacyAccess(ConfigurationPlugin config) throws ConfigurationException + { + _config = config.getConfiguration(LegacyAccessConfiguration.class); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/LegacyAccessPlugin.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/LegacyAccessPlugin.java deleted file mode 100644 index fc1bc048d4..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/LegacyAccessPlugin.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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.plugins; - -import java.util.Collection; -import java.util.HashSet; - -import org.apache.commons.configuration.Configuration; -import org.apache.qpid.server.security.access.ACLPlugin; -import org.apache.qpid.server.security.access.ACLPluginFactory; - -/** - * - * Used to suppress warnings in legacy config files that have things in <security> which aren't handled by a plugin directly. - * - */ -public class LegacyAccessPlugin extends BasicACLPlugin -{ - public static final ACLPluginFactory FACTORY = new ACLPluginFactory() - { - private Collection maskedTags = new HashSet<String>(); - { - maskedTags.add("principal-databases"); - maskedTags.add("access"); - maskedTags.add("msg-auth"); - maskedTags.add("false"); - maskedTags.add("jmx"); - } - - public boolean supportsTag(String name) - { - return maskedTags .contains(name); - } - - public ACLPlugin newInstance(Configuration config) - { - return new LegacyAccessPlugin(); - } - }; - - public String getPluginName() - { - return getClass().getSimpleName(); - } - - @Override - protected AuthzResult getResult() - { - // Always abstain - return AuthzResult.ABSTAIN; - } - -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java index 3f846b9dd0..62967ef7eb 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java @@ -20,8 +20,6 @@ */ package org.apache.qpid.server.security.auth; -import javax.security.sasl.SaslException; - public class AuthenticationResult { public enum AuthenticationStatus diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java index 889ce815f4..6ca9c8e762 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java @@ -21,7 +21,7 @@ package org.apache.qpid.server.security.auth.database; import org.apache.log4j.Logger; -import org.apache.qpid.server.security.access.management.AMQUserManagementMBean; +import org.apache.qpid.server.security.auth.management.AMQUserManagementMBean; import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser; import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HexInitialiser; diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java index 2619a69cfd..5cebb7d2d8 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java @@ -38,7 +38,7 @@ import org.apache.qpid.server.configuration.ServerConfiguration; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.security.auth.database.PrincipalDatabase; import org.apache.qpid.server.security.auth.database.PrincipalDatabaseManager; -import org.apache.qpid.server.security.access.management.AMQUserManagementMBean; +import org.apache.qpid.server.security.auth.management.AMQUserManagementMBean; import org.apache.qpid.AMQException; import javax.management.JMException; @@ -51,7 +51,7 @@ public class ConfigurationFilePrincipalDatabaseManager implements PrincipalDatab public ConfigurationFilePrincipalDatabaseManager(ServerConfiguration _configuration) throws Exception { - _logger.info("Initialising PrincipleDatabase authentication manager"); + _logger.info("Initialising PrincipalDatabase authentication manager"); _databases = initialisePrincipalDatabases(_configuration); } @@ -171,16 +171,13 @@ public class ConfigurationFilePrincipalDatabaseManager implements PrincipalDatab AMQUserManagementMBean _mbean = new AMQUserManagementMBean(); List<String> principalDBs = config.getManagementPrincipalDBs(); - - if (principalDBs.size() == 0) + if (principalDBs.isEmpty()) { throw new ConfigurationException("No principal-database specified for jmx security"); } String databaseName = principalDBs.get(0); - PrincipalDatabase database = getDatabases().get(databaseName); - if (database == null) { throw new ConfigurationException("Principal-database '" + databaseName + "' not found"); @@ -189,14 +186,13 @@ public class ConfigurationFilePrincipalDatabaseManager implements PrincipalDatab _mbean.setPrincipalDatabase(database); List<String> jmxaccesslist = config.getManagementAccessList(); - - if (jmxaccesslist.size() == 0) + if (jmxaccesslist.isEmpty()) { throw new ConfigurationException("No access control files specified for jmx security"); } String jmxaccesssFile = null; - + try { jmxaccesssFile = PropertyUtils.replaceProperties(jmxaccesslist.get(0)); @@ -205,7 +201,7 @@ public class ConfigurationFilePrincipalDatabaseManager implements PrincipalDatab { throw new ConfigurationException("Unable to parse access control filename '" + jmxaccesssFile + "'"); } - + try { _mbean.setAccessFile(jmxaccesssFile); diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBean.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/management/AMQUserManagementMBean.java index 5a2965cb32..153b8c25db 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBean.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/management/AMQUserManagementMBean.java @@ -18,51 +18,50 @@ * * */ -package org.apache.qpid.server.security.access.management; +package org.apache.qpid.server.security.auth.management; -import org.apache.qpid.management.common.mbeans.UserManagement; -import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; -import org.apache.qpid.management.common.mbeans.annotations.MBeanOperation; -import org.apache.qpid.server.management.AMQManagedObject; -import org.apache.qpid.server.management.MBeanInvocationHandlerImpl; -import org.apache.qpid.server.security.auth.database.PrincipalDatabase; -import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; -import org.apache.qpid.util.FileUtils; -import org.apache.log4j.Logger; -import org.apache.commons.configuration.ConfigurationException; - -import javax.management.JMException; -import javax.management.remote.JMXPrincipal; -import javax.management.openmbean.TabularData; -import javax.management.openmbean.TabularDataSupport; -import javax.management.openmbean.TabularType; -import javax.management.openmbean.SimpleType; -import javax.management.openmbean.CompositeType; -import javax.management.openmbean.OpenType; -import javax.management.openmbean.OpenDataException; -import javax.management.openmbean.CompositeData; -import javax.management.openmbean.CompositeDataSupport; -import javax.security.auth.login.AccountNotFoundException; -import javax.security.auth.Subject; import java.io.File; import java.io.FileInputStream; -import java.io.IOException; import java.io.FileOutputStream; -import java.util.Properties; -import java.util.List; +import java.io.IOException; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.Principal; import java.util.Enumeration; +import java.util.List; +import java.util.Properties; import java.util.Random; import java.util.Set; import java.util.concurrent.locks.ReentrantLock; -import java.security.Principal; -import java.security.AccessControlContext; -import java.security.AccessController; + +import javax.management.JMException; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; +import javax.management.remote.JMXPrincipal; +import javax.security.auth.Subject; +import javax.security.auth.login.AccountNotFoundException; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.log4j.Logger; +import org.apache.qpid.management.common.mbeans.UserManagement; +import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; +import org.apache.qpid.management.common.mbeans.annotations.MBeanOperation; +import org.apache.qpid.server.management.AMQManagedObject; +import org.apache.qpid.server.management.MBeanInvocationHandlerImpl; +import org.apache.qpid.server.security.auth.database.PrincipalDatabase; +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; /** MBean class for AMQUserManagementMBean. It implements all the management features exposed for managing users. */ @MBeanDescription("User Management Interface") public class AMQUserManagementMBean extends AMQManagedObject implements UserManagement { - private static final Logger _logger = Logger.getLogger(AMQUserManagementMBean.class); private PrincipalDatabase _principalDatabase; @@ -533,6 +532,8 @@ public class AMQUserManagementMBean extends AMQManagedObject implements UserMana { _logger.debug("Setting Access Rights:" + accessRights); _accessRights = accessRights; - MBeanInvocationHandlerImpl.setAccessRights(_accessRights); + + // TODO check where this is used + // MBeanInvocationHandlerImpl.setAccessRights(_accessRights); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java index d34d0c4d27..bc771162fd 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java @@ -20,13 +20,12 @@ */ package org.apache.qpid.server.security.auth.manager; -import org.apache.qpid.common.Closeable; -import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.security.auth.AuthenticationResult; - import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; +import org.apache.qpid.common.Closeable; +import org.apache.qpid.server.security.auth.AuthenticationResult; + public interface AuthenticationManager extends Closeable { String getMechanisms(); diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java index 98c060599a..2a967f02af 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java @@ -64,7 +64,7 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan public PrincipalDatabaseAuthenticationManager(String name, VirtualHostConfiguration hostConfig) throws Exception { _logger.info("Initialising " + (name == null ? "Default" : "'" + name + "'") - + " PrincipleDatabase authentication manager."); + + " PrincipalDatabase authentication manager."); // Fixme This should be done per Vhost but allowing global hack isn't right but ... // required as authentication is done before Vhost selection diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java index 77040e896c..0cbbccb3b8 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java @@ -99,7 +99,7 @@ public class RMIPasswordAuthenticator implements JMXAuthenticator } catch (AccountNotFoundException e) { - throw new SecurityException(INVALID_CREDENTIALS); + throw new SecurityException(INVALID_CREDENTIALS); // XXX } if (authenticated) diff --git a/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java b/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java index 6850724b10..6cc5e7b019 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java @@ -62,6 +62,7 @@ import org.apache.qpid.server.handler.TxCommitHandler; import org.apache.qpid.server.handler.TxRollbackHandler; import org.apache.qpid.server.handler.TxSelectHandler; import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; /** @@ -258,6 +259,7 @@ public class AMQStateManager implements AMQMethodListener public AMQProtocolSession getProtocolSession() { + SecurityManager.setThreadPrincipal(_protocolSession.getPrincipal()); return _protocolSession; } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java index 5badbad642..1bba2529c6 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java @@ -20,25 +20,17 @@ */ package org.apache.qpid.server.subscription; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.flow.FlowCreditManager; -import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.server.subscription.SubscriptionFactory; -import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.AMQException; +import org.apache.qpid.common.AMQPFilterTypes; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.AMQException; import org.apache.qpid.protocol.AMQConstant; -import org.apache.qpid.common.AMQPFilterTypes; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.flow.FlowCreditManager; +import org.apache.qpid.server.protocol.AMQProtocolSession; public class SubscriptionFactoryImpl implements SubscriptionFactory { - - /* private SubscriptionFactoryImpl() - { - - }*/ - public Subscription createSubscription(int channelId, AMQProtocolSession protocolSession, AMQShortString consumerTag, boolean acks, FieldTable filters, boolean noLocal, FlowCreditManager creditManager) throws AMQException diff --git a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java index a56951cf5c..38040ecfce 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java +++ b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java @@ -22,6 +22,7 @@ package org.apache.qpid.server.transport; import org.apache.qpid.transport.*; +import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -29,16 +30,13 @@ import javax.security.sasl.SaslServer; import javax.security.sasl.SaslException; import java.util.*; - public class ServerConnectionDelegate extends ServerDelegate { - private String _localFQDN; private final IApplicationRegistry _appRegistry; - public ServerConnectionDelegate(IApplicationRegistry appRegistry, - String localFQDN) + public ServerConnectionDelegate(IApplicationRegistry appRegistry, String localFQDN) { this(new HashMap<String,Object>(Collections.singletonMap("qpid.federation_tag",appRegistry.getBroker().getFederationTag())), Collections.singletonList((Object)"en_US"), appRegistry, localFQDN); } @@ -50,6 +48,7 @@ public class ServerConnectionDelegate extends ServerDelegate String localFQDN) { super(properties, parseToList(appRegistry.getAuthenticationManager().getMechanisms()), locales); + _appRegistry = appRegistry; _localFQDN = localFQDN; } @@ -65,9 +64,9 @@ public class ServerConnectionDelegate extends ServerDelegate return list; } - @Override public ServerSession getSession(Connection conn, SessionAttach atc) + @Override + public ServerSession getSession(Connection conn, SessionAttach atc) { - SessionDelegate serverSessionDelegate = new ServerSessionDelegate(_appRegistry); ServerSession ssn = new ServerSession(conn, serverSessionDelegate, new Binary(atc.getName()), 0); @@ -75,9 +74,6 @@ public class ServerConnectionDelegate extends ServerDelegate return ssn; } - - - @Override protected SaslServer createSaslServer(String mechanism) throws SaslException { @@ -85,11 +81,10 @@ public class ServerConnectionDelegate extends ServerDelegate } - @Override public void connectionOpen(Connection conn, ConnectionOpen open) { ServerConnection sconn = (ServerConnection) conn; - + VirtualHost vhost; String vhostName; if(open.hasVirtualHost()) @@ -102,19 +97,27 @@ public class ServerConnectionDelegate extends ServerDelegate } vhost = _appRegistry.getVirtualHostRegistry().getVirtualHost(vhostName); + SecurityManager.setThreadPrincipal(conn.getAuthorizationID()); + if(vhost != null) { sconn.setVirtualHost(vhost); - sconn.invoke(new ConnectionOpenOk(Collections.emptyList())); - - sconn.setState(Connection.State.OPEN); + if (!vhost.getSecurityManager().accessVirtualhost(vhostName, sconn.getConfig().getAddress())) + { + sconn.invoke(new ConnectionClose(ConnectionCloseCode.CONNECTION_FORCED, "Permission denied '"+vhostName+"'")); + sconn.setState(Connection.State.CLOSING); + } + else + { + sconn.invoke(new ConnectionOpenOk(Collections.emptyList())); + sconn.setState(Connection.State.OPEN); + } } else { - sconn.invoke(new ConnectionClose(ConnectionCloseCode.INVALID_PATH, "Unknown virtualhost '" + vhostName + "'")); + sconn.invoke(new ConnectionClose(ConnectionCloseCode.INVALID_PATH, "Unknown virtualhost '"+vhostName+"'")); sconn.setState(Connection.State.CLOSING); } - } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java index 8a16867ad8..bc7ba085fc 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java +++ b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java @@ -157,7 +157,6 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo catch (AMQException e) { // TODO - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. throw new RuntimeException(e); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java index 541810d2fe..73ec7f1231 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java +++ b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java @@ -20,31 +20,78 @@ */ package org.apache.qpid.server.transport; -import org.apache.qpid.transport.*; -import org.apache.qpid.server.registry.IApplicationRegistry; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.exchange.*; -import org.apache.qpid.server.queue.QueueRegistry; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQSecurityException; +import org.apache.qpid.AMQUnknownExchangeType; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.exchange.ExchangeFactory; +import org.apache.qpid.server.exchange.ExchangeInUseException; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.exchange.HeadersExchange; +import org.apache.qpid.server.flow.FlowCreditManager_0_10; +import org.apache.qpid.server.flow.WindowCreditManager; +import org.apache.qpid.server.message.MessageMetaData_0_10; +import org.apache.qpid.server.message.MessageTransferMessage; +import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.queue.BaseQueue; -import org.apache.qpid.server.message.MessageTransferMessage; -import org.apache.qpid.server.message.MessageMetaData_0_10; -import org.apache.qpid.server.subscription.Subscription_0_10; -import org.apache.qpid.server.flow.*; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.security.SecurityManager; +import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.StoredMessage; -import org.apache.qpid.server.store.DurableConfigurationStore; -import org.apache.qpid.server.protocol.AMQSessionModel; -import org.apache.qpid.AMQException; -import org.apache.qpid.AMQUnknownExchangeType; -import org.apache.qpid.framing.*; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; -import java.nio.ByteBuffer; +import org.apache.qpid.server.subscription.Subscription_0_10; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.transport.Acquired; +import org.apache.qpid.transport.DeliveryProperties; +import org.apache.qpid.transport.ExchangeBind; +import org.apache.qpid.transport.ExchangeBound; +import org.apache.qpid.transport.ExchangeBoundResult; +import org.apache.qpid.transport.ExchangeDeclare; +import org.apache.qpid.transport.ExchangeDelete; +import org.apache.qpid.transport.ExchangeQuery; +import org.apache.qpid.transport.ExchangeQueryResult; +import org.apache.qpid.transport.ExchangeUnbind; +import org.apache.qpid.transport.ExecutionErrorCode; +import org.apache.qpid.transport.ExecutionException; +import org.apache.qpid.transport.MessageAccept; +import org.apache.qpid.transport.MessageAcceptMode; +import org.apache.qpid.transport.MessageAcquire; +import org.apache.qpid.transport.MessageAcquireMode; +import org.apache.qpid.transport.MessageCancel; +import org.apache.qpid.transport.MessageFlow; +import org.apache.qpid.transport.MessageFlowMode; +import org.apache.qpid.transport.MessageFlush; +import org.apache.qpid.transport.MessageReject; +import org.apache.qpid.transport.MessageRejectCode; +import org.apache.qpid.transport.MessageRelease; +import org.apache.qpid.transport.MessageResume; +import org.apache.qpid.transport.MessageSetFlowMode; +import org.apache.qpid.transport.MessageStop; +import org.apache.qpid.transport.MessageSubscribe; +import org.apache.qpid.transport.MessageTransfer; +import org.apache.qpid.transport.Method; +import org.apache.qpid.transport.QueueDeclare; +import org.apache.qpid.transport.QueueDelete; +import org.apache.qpid.transport.QueuePurge; +import org.apache.qpid.transport.QueueQuery; +import org.apache.qpid.transport.QueueQueryResult; +import org.apache.qpid.transport.RangeSet; +import org.apache.qpid.transport.Session; +import org.apache.qpid.transport.SessionDelegate; +import org.apache.qpid.transport.TxCommit; +import org.apache.qpid.transport.TxRollback; +import org.apache.qpid.transport.TxSelect; public class ServerSessionDelegate extends SessionDelegate { @@ -58,6 +105,8 @@ public class ServerSessionDelegate extends SessionDelegate @Override public void command(Session session, Method method) { + SecurityManager.setThreadPrincipal(session.getConnection().getAuthorizationID()); + super.command(session, method); if (method.isSync()) { @@ -317,7 +366,6 @@ public class ServerSessionDelegate extends SessionDelegate catch (AMQException e) { //TODO - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. throw new RuntimeException(e); } } @@ -371,19 +419,7 @@ public class ServerSessionDelegate extends SessionDelegate } else { - if (!virtualHost.getAccessManager().authoriseCreateExchange((ServerSession)session, method.getAutoDelete(), - method.getDurable(), new AMQShortString(method.getExchange()), false, false, method.getPassive(), - new AMQShortString(method.getType()))) - { - - ExecutionErrorCode errorCode = ExecutionErrorCode.NOT_ALLOWED; - String description = "permission denied: exchange-name '" + exchangeName + "'"; - - exception(session, method, errorCode, description); - - - } - else if(exchange == null) + if (exchange == null) { ExchangeRegistry exchangeRegistry = getExchangeRegistry(session); ExchangeFactory exchangeFactory = virtualHost.getExchangeFactory(); @@ -417,12 +453,18 @@ public class ServerSessionDelegate extends SessionDelegate { exception(session, method, ExecutionErrorCode.NOT_FOUND, "Unknown Exchange Type: " + method.getType()); } + catch (AMQSecurityException e) + { + ExecutionErrorCode errorCode = ExecutionErrorCode.NOT_ALLOWED; + String description = "Permission denied: exchange-name '" + exchangeName + "'"; + + exception(session, method, errorCode, description); + } catch (AMQException e) { //TODO throw new RuntimeException(e); } - } else { @@ -478,47 +520,38 @@ public class ServerSessionDelegate extends SessionDelegate VirtualHost virtualHost = getVirtualHost(session); ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry(); - //Perform ACLs - if (!virtualHost.getAccessManager().authoriseDelete((ServerSession)session, - exchangeRegistry.getExchange(method.getExchange()))) - { - exception(session,method, ExecutionErrorCode.NOT_ALLOWED, "Permission denied"); - - } - else + try { + Exchange exchange = getExchange(session, method.getExchange()); - try + if(exchange != null && exchange.hasReferrers()) + { + exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Exchange in use as an alternate exchange"); + } + else { - Exchange exchange = getExchange(session, method.getExchange()); + exchangeRegistry.unregisterExchange(method.getExchange(), method.getIfUnused()); - if(exchange != null && exchange.hasReferrers()) - { - exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Exchange in use as an alternate exchange"); - } - else + if (exchange.isDurable() && !exchange.isAutoDelete()) { - exchangeRegistry.unregisterExchange(method.getExchange(), method.getIfUnused()); - - if (exchange.isDurable() && !exchange.isAutoDelete()) - { - DurableConfigurationStore store = virtualHost.getDurableConfigurationStore(); - store.removeExchange(exchange); - } - + DurableConfigurationStore store = virtualHost.getDurableConfigurationStore(); + store.removeExchange(exchange); } } - catch (ExchangeInUseException e) - { - exception(session, method, ExecutionErrorCode.PRECONDITION_FAILED, "Exchange in use"); - } - catch (AMQException e) - { - // TODO - throw new RuntimeException(e); - } } - + catch (ExchangeInUseException e) + { + exception(session, method, ExecutionErrorCode.PRECONDITION_FAILED, "Exchange in use"); + } + catch (AMQSecurityException e) + { + exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Permission denied: " + method.getExchange()); + } + catch (AMQException e) + { + // TODO + throw new RuntimeException(e); + } } @Override @@ -582,13 +615,6 @@ public class ServerSessionDelegate extends SessionDelegate { exception(session, method, ExecutionErrorCode.NOT_FOUND, "Exchange: '" + method.getExchange() + "' not found"); } - else if (!virtualHost.getAccessManager().authoriseBind((ServerSession)session, exchange, - queue, new AMQShortString(method.getBindingKey()))) - { - exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Bind Exchange: '" + method.getExchange() - + "' to Queue: '" + method.getQueue() - + "' not allowed"); - } else if(exchange.getTypeShortString().equals(HeadersExchange.TYPE.getName()) && (!method.hasArguments() || method.getArguments() == null || !method.getArguments().containsKey("x-match"))) { exception(session, method, ExecutionErrorCode.INTERNAL_ERROR, "Bindings to an exchange of type " + HeadersExchange.TYPE.getName() + " require an x-match header"); @@ -600,8 +626,15 @@ public class ServerSessionDelegate extends SessionDelegate if (!exchange.isBound(routingKey, fieldTable, queue)) { - virtualHost.getBindingFactory().addBinding(method.getBindingKey(), queue, exchange, method.getArguments()); - + try + { + virtualHost.getBindingFactory().addBinding(method.getBindingKey(), queue, exchange, method.getArguments()); + } + catch (AMQSecurityException e) + { + exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Bind Exchange: '" + method.getExchange() + + "' to Queue: '" + method.getQueue() + "' not allowed"); + } } else { @@ -649,7 +682,14 @@ public class ServerSessionDelegate extends SessionDelegate } else { - virtualHost.getBindingFactory().removeBinding(method.getBindingKey(), queue, exchange, null); + try + { + virtualHost.getBindingFactory().removeBinding(method.getBindingKey(), queue, exchange, null); + } + catch (AMQSecurityException e) + { + exception(session,method, ExecutionErrorCode.NOT_ALLOWED, "Permission denied"); + } } } @@ -768,25 +808,6 @@ public class ServerSessionDelegate extends SessionDelegate DurableConfigurationStore store = virtualHost.getDurableConfigurationStore(); String queueName = method.getQueue(); - - if (!method.getPassive()) - { - // Perform ACL if request is not passive - - if (!virtualHost.getAccessManager().authoriseCreateQueue(((ServerSession)session), method.getAutoDelete(), method.getDurable(), - method.getExclusive(), false, method.getPassive(), new AMQShortString(queueName))) - { - ExecutionErrorCode errorCode = ExecutionErrorCode.NOT_ALLOWED; - String description = "permission denied: queue-name '" + queueName + "'"; - - exception(session, method, errorCode, description); - - // TODO control flow - return; - } - } - - AMQQueue queue; QueueRegistry queueRegistry = getQueueRegistry(session); //TODO: do we need to check that the queue already exists with exactly the same "configuration"? @@ -879,34 +900,32 @@ public class ServerSessionDelegate extends SessionDelegate { final AMQQueue q = queue; final ServerSession.Task deleteQueueTask = new ServerSession.Task() - { - - public void doTask(ServerSession session) { - try + public void doTask(ServerSession session) { - q.delete(); + try + { + q.delete(); + } + catch (AMQException e) + { + // TODO + throw new RuntimeException(e); + } } - catch (AMQException e) - { - throw new RuntimeException(e); - } - } - }; + }; final ServerSession s = (ServerSession) session; s.addSessionCloseTask(deleteQueueTask); queue.addQueueDeleteTask(new AMQQueue.Task() - { - - public void doTask(AMQQueue queue) throws AMQException { - s.removeSessionCloseTask(deleteQueueTask); - } - }); + public void doTask(AMQQueue queue) throws AMQException + { + s.removeSessionCloseTask(deleteQueueTask); + } + }); } else if(method.getExclusive()) { - { final AMQQueue q = queue; final ServerSession.Task removeExclusive = new ServerSession.Task() { @@ -928,31 +947,34 @@ public class ServerSessionDelegate extends SessionDelegate } }); } - } + } + catch (AMQSecurityException e) + { + String description = "Cannot declare queue('" + queueName + "'), permission denied"; + ExecutionErrorCode errorCode = ExecutionErrorCode.NOT_ALLOWED; + exception(session, method, errorCode, description); } catch (AMQException e) { + // TODO throw new RuntimeException(e); } } } else if (method.getExclusive() && (queue.getPrincipalHolder() != null && !queue.getPrincipalHolder().equals(session))) { - String description = "Cannot declare queue('" + queueName + "')," + " as exclusive queue with same name " + "declared on another session"; ExecutionErrorCode errorCode = ExecutionErrorCode.RESOURCE_LOCKED; - + exception(session, method, errorCode, description); - + return; } - } } - protected AMQQueue createQueue(final String queueName, QueueDeclare body, VirtualHost virtualHost, @@ -963,15 +985,14 @@ public class ServerSessionDelegate extends SessionDelegate String owner = body.getExclusive() ? session.getClientID() : null; - final AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueName, body.getDurable(), owner, body.getAutoDelete(), + final AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueName, body.getDurable(), owner, body.getAutoDelete(), body.getExclusive(), virtualHost, body.getArguments()); - if (body.getExclusive() && !body.getDurable()) { final ServerSession.Task deleteQueueTask = new ServerSession.Task() - { + { public void doTask(ServerSession session) { if (registry.getQueue(queueName) == queue) @@ -1006,7 +1027,6 @@ public class ServerSessionDelegate extends SessionDelegate @Override public void queueDelete(Session session, QueueDelete method) { - String queueName = method.getQueue(); if(queueName == null || queueName.length()==0) { @@ -1041,36 +1061,28 @@ public class ServerSessionDelegate extends SessionDelegate else { VirtualHost virtualHost = getVirtualHost(session); - - //Perform ACLs - if (!virtualHost.getAccessManager().authoriseDelete(((ServerSession)session), queue)) - { - exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Cannot delete queue " + queueName); - } - else + + try { - try - { - int purged = queue.delete(); - if (queue.isDurable() && !queue.isAutoDelete()) - { - DurableConfigurationStore store = virtualHost.getDurableConfigurationStore(); - store.removeQueue(queue); - } - - } - catch (AMQException e) + queue.delete(); + if (queue.isDurable() && !queue.isAutoDelete()) { - //TODO - throw new RuntimeException(e); + DurableConfigurationStore store = virtualHost.getDurableConfigurationStore(); + store.removeQueue(queue); } - } - + catch (AMQSecurityException e) + { + exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Permission denied: " + queueName); + } + catch (AMQException e) + { + // TODO + throw new RuntimeException(e); + } } } } - } @Override @@ -1080,24 +1092,32 @@ public class ServerSessionDelegate extends SessionDelegate if(queueName == null || queueName.length()==0) { exception(session, method, ExecutionErrorCode.ILLEGAL_ARGUMENT, "No queue name supplied"); - } else { AMQQueue queue = getQueue(session, queueName); - if (queue == null) { exception(session, method, ExecutionErrorCode.NOT_FOUND, "No queue " + queueName + " found"); } else { - //TODO - queue.clearQueue(); + try + { + queue.clearQueue(); + } + catch (AMQSecurityException e) + { + exception(session,method, ExecutionErrorCode.NOT_ALLOWED, "Permission denied: " + queueName); + } + catch (AMQException e) + { + // TODO + throw new RuntimeException(e); + } } } - } @Override diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java index 675a6f6a91..98d231a7ea 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java @@ -102,7 +102,7 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa if (q == null) { - q = AMQQueueFactory.createAMQQueueImpl(queueNameShortString, true, owner == null ? null : new AMQShortString(owner), false, _virtualHost, + q = AMQQueueFactory.createAMQQueueImpl(queueNameShortString, true, owner == null ? null : new AMQShortString(owner), false, false, _virtualHost, arguments); _virtualHost.getQueueRegistry().registerQueue(q); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java index 44d178602f..d104209e98 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java @@ -20,16 +20,25 @@ */ package org.apache.qpid.server.virtualhost; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.TimerTask; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import javax.management.NotCompliantMBeanException; + import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; import org.apache.log4j.Logger; - import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.AMQBrokerManagerMBean; -import org.apache.qpid.server.virtualhost.plugins.VirtualHostPluginFactory; -import org.apache.qpid.server.virtualhost.plugins.VirtualHostHouseKeepingPlugin; import org.apache.qpid.server.binding.BindingFactory; import org.apache.qpid.server.configuration.BrokerConfig; import org.apache.qpid.server.configuration.ConfigStore; @@ -58,29 +67,17 @@ import org.apache.qpid.server.queue.DefaultQueueRegistry; import org.apache.qpid.server.queue.QueueRegistry; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; -import org.apache.qpid.server.security.access.ACLManager; -import org.apache.qpid.server.security.access.Accessable; +import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager; import org.apache.qpid.server.store.ConfigurationRecoveryHandler; import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.TransactionLog; +import org.apache.qpid.server.virtualhost.plugins.VirtualHostPlugin; +import org.apache.qpid.server.virtualhost.plugins.VirtualHostPluginFactory; -import javax.management.NotCompliantMBeanException; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.TimerTask; -import java.util.UUID; -import java.util.Map; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.FutureTask; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -public class VirtualHostImpl implements Accessable, VirtualHost +public class VirtualHostImpl implements VirtualHost { private static final Logger _logger = Logger.getLogger(VirtualHostImpl.class); @@ -102,7 +99,7 @@ public class VirtualHostImpl implements Accessable, VirtualHost private AuthenticationManager _authenticationManager; - private ACLManager _accessManager; + private SecurityManager _securityManager; private final ScheduledThreadPoolExecutor _houseKeepingTasks; private final IApplicationRegistry _appRegistry; @@ -117,17 +114,6 @@ public class VirtualHostImpl implements Accessable, VirtualHost private final ConcurrentHashMap<BrokerLink,BrokerLink> _links = new ConcurrentHashMap<BrokerLink, BrokerLink>(); private static final int HOUSEKEEPING_SHUTDOWN_TIMEOUT = 5; - public void setAccessableName(String name) - { - _logger.warn("Setting Accessable Name for VirualHost is not allowed. (" - + name + ") ignored remains :" + getAccessableName()); - } - - public String getAccessableName() - { - return _name; - } - public IConnectionRegistry getConnectionRegistry() { return _connectionRegistry; @@ -140,7 +126,7 @@ public class VirtualHostImpl implements Accessable, VirtualHost public UUID getId() { - return _id; //To change body of implemented methods use File | Settings | File Templates. + return _id; } public VirtualHostConfigType getConfigType() @@ -200,12 +186,17 @@ public class VirtualHostImpl implements Accessable, VirtualHost private VirtualHostImpl(IApplicationRegistry appRegistry, VirtualHostConfiguration hostConfig, MessageStore store) throws Exception { + if (hostConfig == null) + { + throw new IllegalAccessException("HostConfig and MessageStore cannot be null"); + } + _appRegistry = appRegistry; - _broker = appRegistry.getBroker(); + _broker = _appRegistry.getBroker(); _configuration = hostConfig; - _name = hostConfig.getName(); + _name = _configuration.getName(); - _id = appRegistry.getConfigStore().createId(); + _id = _appRegistry.getConfigStore().createId(); CurrentActor.get().message(VirtualHostMessages.VHT_CREATED(_name)); @@ -214,6 +205,9 @@ public class VirtualHostImpl implements Accessable, VirtualHost throw new IllegalArgumentException("Illegal name (" + _name + ") for virtualhost."); } + _securityManager = new SecurityManager(_appRegistry.getSecurityManager()); + _securityManager.configureHostPlugins(_configuration); + _virtualHostMBean = new VirtualHostMBean(); _connectionRegistry = new ConnectionRegistry(); @@ -223,15 +217,10 @@ public class VirtualHostImpl implements Accessable, VirtualHost _queueRegistry = new DefaultQueueRegistry(this); _exchangeFactory = new DefaultExchangeFactory(this); - _exchangeFactory.initialise(hostConfig); + _exchangeFactory.initialise(_configuration); _exchangeRegistry = new DefaultExchangeRegistry(this); - - //Create a temporary RT to store the durable entries from the config file - // so we can replay them in to the real _RT after it has been loaded. - /// This should be removed after the _RT has been fully split from the the TL - StartupRoutingTable configFileRT = new StartupRoutingTable(); _durableConfigurationStore = configFileRT; @@ -241,7 +230,7 @@ public class VirtualHostImpl implements Accessable, VirtualHost _bindingFactory = new BindingFactory(this); - initialiseModel(hostConfig); + initialiseModel(_configuration); if (store != null) { @@ -250,36 +239,10 @@ public class VirtualHostImpl implements Accessable, VirtualHost } else { - if (hostConfig == null) - { - throw new IllegalAccessException("HostConfig and MessageStore cannot be null"); - } - initialiseMessageStore(hostConfig); - } - - - - //Now that the RT has been initialised loop through the persistent queues/exchanges created from the config - // file and write them in to the new routing Table. -/* for (StartupRoutingTable.CreateQueueTuple cqt : configFileRT.queue) - { - getDurableConfigurationStore().createQueue(cqt.queue, cqt.arguments); + initialiseMessageStore(hostConfig); } - - for (Exchange exchange : configFileRT.exchange) - { - getDurableConfigurationStore().createExchange(exchange); - } - - for (StartupRoutingTable.CreateBindingTuple cbt : configFileRT.bindings) - { - getDurableConfigurationStore().bindQueue(cbt.exchange, cbt.routingKey, cbt.queue, cbt.arguments); - }*/ - - _authenticationManager = new PrincipalDatabaseAuthenticationManager(_name, hostConfig); - - _accessManager = ApplicationRegistry.getInstance().getAccessManager(); - _accessManager.configureHostPlugins(hostConfig.getSecurityConfiguration()); + + _authenticationManager = new PrincipalDatabaseAuthenticationManager(_name, _configuration); _brokerMBean = new AMQBrokerManagerMBean(_virtualHostMBean); _brokerMBean.register(); @@ -330,8 +293,7 @@ public class VirtualHostImpl implements Accessable, VirtualHost } Map<String, VirtualHostPluginFactory> plugins = - ApplicationRegistry.getInstance(). - getPluginManager().getVirtualHostPlugins(); + ApplicationRegistry.getInstance().getPluginManager().getVirtualHostPlugins(); if (plugins != null) { @@ -340,24 +302,6 @@ public class VirtualHostImpl implements Accessable, VirtualHost try { VirtualHostPlugin plugin = plugins.get(pluginName).newInstance(this); - - TimeUnit units = TimeUnit.MILLISECONDS; - - if (plugin.getTimeUnit() != null) - { - try - { - units = TimeUnit.valueOf(plugin.getTimeUnit()); - } - catch (IllegalArgumentException iae) - { - _logger.warn("Plugin:" + pluginName + - " provided an illegal TimeUnit value:" - + plugin.getTimeUnit()); - // Warn and use default of millseconds - // Should not occur in a well behaved plugin - } - } _houseKeepingTasks.scheduleAtFixedRate(plugin, plugin.getDelay() / 2, plugin.getDelay(), plugin.getTimeUnit()); @@ -600,9 +544,9 @@ public class VirtualHostImpl implements Accessable, VirtualHost return _authenticationManager; } - public ACLManager getAccessManager() + public SecurityManager getSecurityManager() { - return _accessManager; + return _securityManager; } public void close() diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/plugins/VirtualHostPlugin.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/plugins/VirtualHostPlugin.java index 59ab7ec673..26eb5bbd7f 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/plugins/VirtualHostPlugin.java +++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/plugins/VirtualHostPlugin.java @@ -20,6 +20,8 @@ */ package org.apache.qpid.server.virtualhost.plugins; +import java.util.concurrent.TimeUnit; + import org.apache.qpid.server.plugins.Plugin; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -39,5 +41,5 @@ public interface VirtualHostPlugin extends Runnable, Plugin * @see java.util.concurrent.TimeUnit for valid value. * @return */ - public String getTimeUnit(); + public TimeUnit getTimeUnit(); } diff --git a/java/broker/src/test/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBeanTest.java b/java/broker/src/test/java/org/apache/qpid/server/management/AMQUserManagementMBeanTest.java index fe35cfa3aa..8bced58b7b 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBeanTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/management/AMQUserManagementMBeanTest.java @@ -19,24 +19,20 @@ * */ -package org.apache.qpid.server.security.access.management; +package org.apache.qpid.server.management; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; -import javax.management.MalformedObjectNameException; -import javax.management.ObjectName; +import junit.framework.TestCase; import org.apache.commons.configuration.ConfigurationException; -import org.apache.qpid.server.management.MBeanInvocationHandlerImpl; import org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase; - -import junit.framework.TestCase; +import org.apache.qpid.server.security.auth.management.AMQUserManagementMBean; /* Note: The main purpose is to test the jmx access rights file manipulation * within AMQUserManagementMBean. The Principal Databases are tested by their own tests, diff --git a/java/broker/src/test/java/org/apache/qpid/server/plugins/PluginTest.java b/java/broker/src/test/java/org/apache/qpid/server/plugins/PluginTest.java new file mode 100644 index 0000000000..325a4e6464 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/plugins/PluginTest.java @@ -0,0 +1,74 @@ +/* + * 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 junit.framework.TestCase; +import org.apache.commons.configuration.PropertiesConfiguration; +import org.apache.qpid.server.configuration.ServerConfiguration; +import org.apache.qpid.server.exchange.ExchangeType; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.util.TestApplicationRegistry; + +import java.util.Map; + +public class PluginTest extends TestCase +{ + private static final String TEST_EXCHANGE_CLASS = "org.apache.qpid.extras.exchanges.example.TestExchangeType"; + + private static final String PLUGIN_DIRECTORY = System.getProperty("example.plugin.target"); + private static final String CACHE_DIRECTORY = System.getProperty("example.cache.target"); + + IApplicationRegistry _registry; + + @Override + public void setUp() throws Exception + { + PropertiesConfiguration properties = new PropertiesConfiguration(); + properties.addProperty("plugin-directory", PLUGIN_DIRECTORY); + properties.addProperty("cache-directory", CACHE_DIRECTORY); + ServerConfiguration config = new ServerConfiguration(properties); + + // This Test requries an application Registry + ApplicationRegistry.initialise(new TestApplicationRegistry(config)); + _registry = ApplicationRegistry.getInstance(); + } + + @Override + public void tearDown() throws Exception + { + ApplicationRegistry.remove(); + } + + public void disabled_testLoadExchanges() throws Exception + { + PluginManager manager = _registry.getPluginManager(); + Map<String, ExchangeType<?>> exchanges = manager.getExchanges(); + assertNotNull("No exchanges found in " + PLUGIN_DIRECTORY, exchanges); + assertEquals("Wrong number of exchanges found in " + PLUGIN_DIRECTORY, 2, exchanges.size()); + assertNotNull("Wrong exchange found in " + PLUGIN_DIRECTORY, exchanges.get(TEST_EXCHANGE_CLASS)); + } + + public void testNoExchanges() throws Exception + { + PluginManager manager = new PluginManager("/path/to/nowhere", "/tmp"); + Map<String, ExchangeType<?>> exchanges = manager.getExchanges(); + assertTrue("Exchanges found", exchanges.isEmpty()); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java index 3d3d8f93a8..393ffdeaac 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java @@ -49,7 +49,7 @@ public class AMQQueueFactoryTest extends TestCase } - public void testPriorityQueueRegistration() + public void testPriorityQueueRegistration() throws Exception { FieldTable fieldTable = new FieldTable(); fieldTable.put(new AMQShortString(AMQQueueFactory.X_QPID_PRIORITIES), 5); @@ -62,7 +62,7 @@ public class AMQQueueFactoryTest extends TestCase } - public void testSimpleQueueRegistration() + public void testSimpleQueueRegistration() throws Exception { AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("owner"), false, false, _virtualHost, null); diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java index fe45be3f0e..e1852a0a22 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java @@ -25,6 +25,7 @@ import junit.framework.TestCase; import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.qpid.AMQException; +import org.apache.qpid.AMQSecurityException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicContentHeaderProperties; import org.apache.qpid.framing.ContentHeaderBody; @@ -144,7 +145,7 @@ public class SimpleAMQQueueTest extends TestCase assertEquals("Virtual host was wrong", _virtualHost, _queue.getVirtualHost()); } - public void testBinding() + public void testBinding() throws AMQSecurityException { _virtualHost.getBindingFactory().addBinding(String.valueOf(_routingKey), _queue, _exchange, Collections.EMPTY_MAP); diff --git a/java/broker/src/test/java/org/apache/qpid/server/security/access/ACLManagerTest.java b/java/broker/src/test/java/org/apache/qpid/server/security/access/ACLManagerTest.java deleted file mode 100644 index 44f9861e8d..0000000000 --- a/java/broker/src/test/java/org/apache/qpid/server/security/access/ACLManagerTest.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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.ConfigurationException; -import org.apache.commons.configuration.PropertiesConfiguration; -import org.apache.commons.configuration.XMLConfiguration; -import org.apache.qpid.server.configuration.SecurityConfiguration; -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.protocol.InternalTestProtocolSession; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.MockAMQQueue; -import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.registry.ApplicationRegistry; - -public class ACLManagerTest extends TestCase -{ - - private ACLManager _authzManager; - private AMQProtocolSession _session; - private SecurityConfiguration _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("<security><queueDenier>notyet</queueDenier><exchangeDenier>yes</exchangeDenier></security>"); - out.close(); - - _conf = new SecurityConfiguration(new XMLConfiguration(tmpFile)); - - // Create ACLManager - - _pluginManager = new MockPluginManager(""); - _authzManager = new ACLManager(_conf, _pluginManager); - - - VirtualHost virtualHost = ApplicationRegistry.getInstance(). - getVirtualHostRegistry().getVirtualHosts().iterator().next(); - - // Create a single session for this test. - _session = new InternalTestProtocolSession(virtualHost); - } - - @Override - public void tearDown() throws Exception - { - // Correctly Close the AR we created - ApplicationRegistry.remove(); - super.tearDown(); - } - - 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() throws ConfigurationException - { - _authzManager = new ACLManager(_conf, _pluginManager, ExchangeDenier.FACTORY); - - Exchange exchange = null; - assertFalse(_authzManager.authoriseDelete(_session, exchange)); - } - - public void testConfigurePlugins() throws ConfigurationException - { - Configuration hostConfig = new PropertiesConfiguration(); - hostConfig.setProperty("queueDenier", "thisoneneither"); - _authzManager.configureHostPlugins(new SecurityConfiguration(hostConfig)); - AMQQueue queue = new MockAMQQueue("thisoneneither"); - assertFalse(_authzManager.authoriseDelete(_session, queue)); - } -} diff --git a/java/broker/src/test/java/org/apache/qpid/server/security/access/QueueDenier.java b/java/broker/src/test/java/org/apache/qpid/server/security/access/QueueDenier.java deleted file mode 100644 index 3f9c776aa2..0000000000 --- a/java/broker/src/test/java/org/apache/qpid/server/security/access/QueueDenier.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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.queue.AMQQueue; -import org.apache.qpid.server.security.access.plugins.AllowAll; -import org.apache.qpid.server.security.PrincipalHolder; - -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(PrincipalHolder session, AMQQueue queue) - { - if (!(queue.getNameShortString().toString().equals(_queueName))) - { - return AuthzResult.ALLOWED; - } - else - { - return AuthzResult.DENIED; - } - } - - @Override - public void setConfiguration(Configuration config) - { - _queueName = config.getString("queueDenier"); - } -} diff --git a/java/common/src/main/java/org/apache/qpid/AMQSecurityException.java b/java/common/src/main/java/org/apache/qpid/AMQSecurityException.java new file mode 100644 index 0000000000..d145d2c21d --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/AMQSecurityException.java @@ -0,0 +1,54 @@ +/* + * + * 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; + +import org.apache.qpid.protocol.AMQConstant; + +/** + * SecurityException encapsulates error code 403, or {@link AMQConstant#ACCESS_REFUSED} exceptions relating to the + * AMQ protocol. It is used to report authorisation failures and security errors. + */ +public class AMQSecurityException extends AMQException +{ + /** serialVersionUID */ + private static final long serialVersionUID = 8862069852716968394L; + + /** + * Creates an exception with an optional message and optional underlying cause. + * + * @param msg The exception message. May be null if not to be set. + * @param cause The underlying cause of the exception. May be null if not to be set. + */ + public AMQSecurityException(String msg, Throwable cause) + { + super(AMQConstant.ACCESS_REFUSED, ((msg == null) ? "Permission denied" : msg), cause); + } + + public AMQSecurityException(String msg) + { + this(msg, null); + } + + public AMQSecurityException() + { + this(null); + } +} diff --git a/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedBroker.java b/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedBroker.java index d02b9b89f4..c0e7293611 100644 --- a/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedBroker.java +++ b/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedBroker.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.util.List; import javax.management.JMException; +import javax.management.MBeanException; import javax.management.MBeanOperationInfo; import org.apache.qpid.management.common.mbeans.annotations.MBeanAttribute; @@ -71,55 +72,63 @@ public interface ManagedBroker /** * Creates a new Exchange. + * * @param name * @param type * @param durable * @throws IOException * @throws JMException + * @throws MBeanException */ @MBeanOperation(name="createNewExchange", description="Creates a new Exchange", impact= MBeanOperationInfo.ACTION) void createNewExchange(@MBeanOperationParameter(name="name", description="Name of the new exchange")String name, @MBeanOperationParameter(name="ExchangeType", description="Type of the exchange")String type, @MBeanOperationParameter(name="durable", description="true if the Exchang should be durable")boolean durable) - throws IOException, JMException; + throws IOException, JMException, MBeanException; /** * unregisters all the channels, queuebindings etc and unregisters * this exchange from managed objects. + * * @param exchange * @throws IOException * @throws JMException + * @throws MBeanException */ @MBeanOperation(name="unregisterExchange", description="Unregisters all the related channels and queuebindings of this exchange", impact= MBeanOperationInfo.ACTION) void unregisterExchange(@MBeanOperationParameter(name= ManagedExchange.TYPE, description="Exchange Name")String exchange) - throws IOException, JMException; + throws IOException, JMException, MBeanException; /** - * Create a new Queue on the Broker server + * Create a new Queue on the Broker server. + * * @param queueName * @param durable * @param owner * @throws IOException * @throws JMException + * @throws MBeanException */ @MBeanOperation(name="createNewQueue", description="Create a new Queue on the Broker server", impact= MBeanOperationInfo.ACTION) void createNewQueue(@MBeanOperationParameter(name="queue name", description="Name of the new queue")String queueName, @MBeanOperationParameter(name="owner", description="Owner name")String owner, @MBeanOperationParameter(name="durable", description="true if the queue should be durable")boolean durable) - throws IOException, JMException; + throws IOException, JMException, MBeanException; /** * Unregisters the Queue bindings, removes the subscriptions and unregisters * from the managed objects. + * * @param queueName * @throws IOException * @throws JMException + * @throws MBeanException */ @MBeanOperation(name="deleteQueue", description="Unregisters the Queue bindings, removes the subscriptions and deletes the queue", impact= MBeanOperationInfo.ACTION) void deleteQueue(@MBeanOperationParameter(name= ManagedQueue.TYPE, description="Queue Name")String queueName) - throws IOException, JMException; + throws IOException, JMException, MBeanException; } diff --git a/java/systests/etc/config-systests-aclv2-settings.xml b/java/systests/etc/config-systests-aclv2-settings.xml new file mode 100644 index 0000000000..a7248766db --- /dev/null +++ b/java/systests/etc/config-systests-aclv2-settings.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + - + - 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. + - + --> +<broker> + <security> + <access> + <class>org.apache.qpid.server.security.access.plugins.AccessControl</class> + </access> + <aclv2>${QPID_HOME}/etc/global-default.txt</aclv2> + </security> + + <virtualhosts>${QPID_HOME}/etc/virtualhosts-systests-aclv2.xml</virtualhosts> +</broker> + + diff --git a/java/systests/etc/config-systests-aclv2.xml b/java/systests/etc/config-systests-aclv2.xml new file mode 100644 index 0000000000..33563e7891 --- /dev/null +++ b/java/systests/etc/config-systests-aclv2.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + - + - 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. + - + --> +<configuration> + <system/> + <override> + <xml fileName="${test.config}" optional="true"/> + <xml fileName="${QPID_HOME}/etc/config-systests-aclv2-settings.xml"/> + <xml fileName="${QPID_HOME}/etc/config-systests-settings.xml"/> + <xml fileName="${QPID_HOME}/etc/config.xml"/> + </override> +</configuration> diff --git a/java/systests/etc/global-default.txt b/java/systests/etc/global-default.txt new file mode 100644 index 0000000000..01b2c41809 --- /dev/null +++ b/java/systests/etc/global-default.txt @@ -0,0 +1,31 @@ +# +# 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. +# + +CONFIG expand=true + +# This section grants the admin user access to all management methods +ACL ALLOW admin ALL METHOD + +# This section grants the client user access to all management methods except logging +ACL DENY client ALL METHOD component="LoggingManagement" +ACL ALLOW client ALL METHOD + +# This section grants the server user access to all management methods except configuration +ACL DENY server ALL METHOD component="ConfigurationManagement" +ACL ALLOW server ALL METHOD diff --git a/java/systests/etc/global-externaladminacl-changeloggerleveldenied.txt b/java/systests/etc/global-externaladminacl-changeloggerleveldenied.txt new file mode 100644 index 0000000000..a59b3176cb --- /dev/null +++ b/java/systests/etc/global-externaladminacl-changeloggerleveldenied.txt @@ -0,0 +1,24 @@ +# +# 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. +# + +# This section denies the admin user access to logging +ACL DENY admin UPDATE METHOD component="LoggingManagement" name="setRuntimeRootLoggerLevel" + +# This section grants the admin user access to management methods +ACL ALLOW admin ALL METHOD diff --git a/java/systests/etc/global-externaladminacl-getallloggerlevelsdenied.txt b/java/systests/etc/global-externaladminacl-getallloggerlevelsdenied.txt new file mode 100644 index 0000000000..ff024b5ee8 --- /dev/null +++ b/java/systests/etc/global-externaladminacl-getallloggerlevelsdenied.txt @@ -0,0 +1,25 @@ +# +# 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. +# + +# This section denies the admin user access to logging methods +ACL DENY admin ACCESS METHOD component="LoggingManagement" name="getAvailableLoggerLevels" + +# This section grants the admin user access to all management methods +ACL ALLOW admin ALL METHOD + diff --git a/java/systests/etc/test-default.txt b/java/systests/etc/test-default.txt new file mode 100644 index 0000000000..95e733d077 --- /dev/null +++ b/java/systests/etc/test-default.txt @@ -0,0 +1,73 @@ +# +# 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. +# + +# This section grants virtualhost access rights +ACL ALLOW client ACCESS VIRTUALHOST +ACL ALLOW server ACCESS VIRTUALHOST + +# This section grants publish rights to an exchange + routing key pair + +# Allow clients to publish requests +ACL ALLOW client PUBLISH EXCHANGE name="amq.direct" routingKey="example.RequestQueue" + +# Allow the processor to respond to a client on their Temporary Topic +ACL ALLOW server PUBLISH EXCHANGE name="amq.direct" routingKey="tmp_*" +ACL ALLOW server PUBLISH EXCHANGE name="amq.direct" routingKey="TempQueue*" + +# This section grants users the ability to consume from the broker + +# Allow client to consume from temporary queues +ACL ALLOW client CONSUME QUEUE temporary=true + +# Only allow the server to consume from the Request Queue +ACL ALLOW server CONSUME QUEUE name="example.RequestQueue" + +# Allow client and server to consume from kipper queues +ACL ALLOW client CONSUME QUEUE name="clientid:kipper" +ACL ALLOW server CONSUME QUEUE name="clientid:kipper" + +# This section grants users the ability to create/delete queues and exchanges + +# Allow clients to create and delete temporary and kipper queue on this exchange +ACL ALLOW client CREATE QUEUE temporary=true +ACL ALLOW client DELETE QUEUE temporary=true +ACL ALLOW client CREATE QUEUE durable="true" +ACL ALLOW client DELETE QUEUE durable="true" + +# Allow the server to create the Request Queue and kipper queue +ACL ALLOW server CREATE QUEUE name="example.RequestQueue" +ACL ALLOW server CREATE QUEUE name="clientid:kipper" + +## Allow client and server exchange access for the relevant queues +ACL ALLOW client BIND EXCHANGE name="amq.direct" temporary=true +ACL ALLOW client UNBIND EXCHANGE name="amq.direct" temporary=true +ACL ALLOW client BIND EXCHANGE name="amq.direct" durable=true +ACL ALLOW client UNBIND EXCHANGE name="amq.direct" durable=true +ACL ALLOW server BIND EXCHANGE name="amq.direct" queueName="example.RequestQueue" + +## Allow client and server exchange access for the relevant topics +ACL ALLOW client BIND EXCHANGE name="amq.topic" durable=true routingKey=kipper +ACL ALLOW client UNBIND EXCHANGE name="amq.topic" durable=true routingKey=kipper +ACL ALLOW server BIND EXCHANGE name="amq.topic" durable=true routingKey=kipper + +# Action[operation=BIND,objectType=EXCHANGE,properties={OWNER=client, DURABLE=true, QUEUE_NAME=IllegalQueue, AUTO_DELETE=false, ROUTING_KEY=IllegalQueue, NAME=amq.direct, TEMPORARY=false, EXCLUSIVE=false}] + + +ACL ALLOW client CREATE EXCHANGE +ACL ALLOW server CREATE EXCHANGE diff --git a/java/systests/etc/test-externalacljmx-deleteexchangefailure.txt b/java/systests/etc/test-externalacljmx-deleteexchangefailure.txt new file mode 100644 index 0000000000..197fe9dabe --- /dev/null +++ b/java/systests/etc/test-externalacljmx-deleteexchangefailure.txt @@ -0,0 +1,26 @@ +# +# 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. +# + +# This section grants virtualhost management rights +ACL ALLOW admin ALL METHOD + +# testDeleteExchangeFailure() +ACL ALLOW admin CREATE EXCHANGE name="amq.kipper.delete" +ACL DENY admin DELETE EXCHANGE name="amq.kipper.delete" + diff --git a/java/systests/etc/test-externalacljmx.txt b/java/systests/etc/test-externalacljmx.txt new file mode 100644 index 0000000000..f8a94bd44a --- /dev/null +++ b/java/systests/etc/test-externalacljmx.txt @@ -0,0 +1,35 @@ +# +# 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. +# + +# This section grants management access to the virtualhost +ACL ALLOW admin ALL METHOD +ACL ALLOW client ALL METHOD +ACL ALLOW server ALL METHOD + +# Allow create kipper queue +ACL ALLOW admin CREATE QUEUE name="kipper" owner = client # kipper +ACL ALLOW admin BIND EXCHANGE name="amq.direct" + +# testCreateExchangeSuccess(), testDeleteExchangeSuccess() +ACL ALLOW admin CREATE EXCHANGE name="amq.kipper.success" +ACL ALLOW admin DELETE EXCHANGE name="amq.kipper.success" + +# testCreateExchangeFailure() +ACL DENY admin CREATE EXCHANGE name="amq.kipper.failure" + diff --git a/java/systests/etc/test2-default.txt b/java/systests/etc/test2-default.txt new file mode 100644 index 0000000000..0855e631d7 --- /dev/null +++ b/java/systests/etc/test2-default.txt @@ -0,0 +1,21 @@ +# +# 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. +# + +# This section grants all access rights +ACL ALLOW guest ALL ALL
\ No newline at end of file diff --git a/java/systests/etc/virtualhosts-systests-acl-settings.xml b/java/systests/etc/virtualhosts-systests-acl-settings.xml index 50fda4e999..ffbace569f 100644 --- a/java/systests/etc/virtualhosts-systests-acl-settings.xml +++ b/java/systests/etc/virtualhosts-systests-acl-settings.xml @@ -35,9 +35,6 @@ <security> - <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> @@ -45,7 +42,6 @@ <exchange> <name>amq.direct</name> <routing_keys> - <!-- Allow clients to publish requests --> <routing_key> <value>example.RequestQueue</value> @@ -68,7 +64,6 @@ </users> </routing_key> </routing_keys> - </exchange> </exchanges> </publish> @@ -90,7 +85,14 @@ </users> </queue> - + <!-- Allow client and server to consume from the kipper Queue--> + <queue> + <name>clientid:kipper</name> + <users> + <user>client</user> + <user>server</user> + </users> + </queue> </queues> </consume> @@ -110,10 +112,28 @@ <name>amq.direct</name> <users> <user>client</user> + <user>server</user> + </users> + </exchange> + <exchange> + <name>amq.topic</name> + <users> + <user>client</user> + <user>server</user> </users> </exchange> </exchanges> </queue> + + <!-- everyone can create the kipper queue --> + <queue> + <name>clientid:kipper</name> + <users> + <user>client</user> + <user>server</user> + </users> + </queue> + <!-- Allow the server to create the Request Queue--> <queue> <name>example.RequestQueue</name> @@ -121,13 +141,21 @@ <user>server</user> </users> </queue> - </queues> </create> - + <delete> + <queues> + <!-- only client can delete the kipper queue --> + <queue> + <name>clientid:kipper</name> + <users> + <user>client</user> + </users> + </queue> + </queues> + </delete> </access_control_list> - </security> </test> </virtualhost> @@ -136,10 +164,6 @@ <name>test2</name> <test2> <security> - <access> - <class>org.apache.qpid.server.security.access.plugins.SimpleXML</class> - </access> - <access_control_list> <!-- This section grants specific users full permissions to all artifacts in this virtualhost --> <access> diff --git a/java/systests/etc/virtualhosts-systests-aclv2-settings.xml b/java/systests/etc/virtualhosts-systests-aclv2-settings.xml new file mode 100644 index 0000000000..db1ad33a39 --- /dev/null +++ b/java/systests/etc/virtualhosts-systests-aclv2-settings.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + - + - 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. + - + --> +<virtualhosts> + <virtualhost> + <name>test</name> + <test> + <queues> + <exchange>amq.direct</exchange> + <!-- 4Mb --> + <maximumQueueDepth>4235264</maximumQueueDepth> + <!-- 2Mb --> + <maximumMessageSize>2117632</maximumMessageSize> + <!-- 10 mins --> + <maximumMessageAge>600000</maximumMessageAge> + </queues> + + <security> + <aclv2>${QPID_HOME}/etc/test-default.txt</aclv2> + </security> + </test> + </virtualhost> + + <virtualhost> + <name>test2</name> + <test2 /> + </virtualhost> +</virtualhosts> + + diff --git a/java/systests/etc/virtualhosts-systests-aclv2.xml b/java/systests/etc/virtualhosts-systests-aclv2.xml new file mode 100644 index 0000000000..eb96577487 --- /dev/null +++ b/java/systests/etc/virtualhosts-systests-aclv2.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + - + - 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. + - + --> +<configuration> + <system/> + <override> + <xml fileName="${test.virtualhosts}" optional="true"/> + <xml fileName="${QPID_HOME}/etc/virtualhosts-systests-aclv2-settings.xml"/> + <xml fileName="${QPID_HOME}/etc/virtualhosts.xml"/> + </override> +</configuration> diff --git a/java/systests/etc/virtualhosts-systests-firewall-2.xml b/java/systests/etc/virtualhosts-systests-firewall-2.xml index b85ab83676..7990483348 100644 --- a/java/systests/etc/virtualhosts-systests-firewall-2.xml +++ b/java/systests/etc/virtualhosts-systests-firewall-2.xml @@ -26,8 +26,7 @@ <name>test</name> <test> <store> - <class>org.apache.qpid.server.store.MemoryMessageStore - </class> + <class>org.apache.qpid.server.store.MemoryMessageStore</class> </store> <security> <firewall default-action="allow"/> @@ -39,8 +38,7 @@ <name>test2</name> <test2> <store> - <class>org.apache.qpid.server.store.MemoryMessageStore - </class> + <class>org.apache.qpid.server.store.MemoryMessageStore</class> </store> </test2> </virtualhost> diff --git a/java/systests/etc/virtualhosts-systests-firewall-3.xml b/java/systests/etc/virtualhosts-systests-firewall-3.xml index ebfc2bad20..45a2ae5ef4 100644 --- a/java/systests/etc/virtualhosts-systests-firewall-3.xml +++ b/java/systests/etc/virtualhosts-systests-firewall-3.xml @@ -26,8 +26,7 @@ <name>test</name> <test> <store> - <class>org.apache.qpid.server.store.MemoryMessageStore - </class> + <class>org.apache.qpid.server.store.MemoryMessageStore</class> </store> </test> </virtualhost> @@ -36,8 +35,7 @@ <name>test2</name> <test2> <store> - <class>org.apache.qpid.server.store.MemoryMessageStore - </class> + <class>org.apache.qpid.server.store.MemoryMessageStore</class> </store> <security> <firewall default-action="deny"/> diff --git a/java/systests/src/main/java/org/apache/qpid/server/failover/MessageDisappearWithIOExceptionTest.java b/java/systests/src/main/java/org/apache/qpid/server/failover/MessageDisappearWithIOExceptionTest.java index 863aa43d22..4050637bb2 100644 --- a/java/systests/src/main/java/org/apache/qpid/server/failover/MessageDisappearWithIOExceptionTest.java +++ b/java/systests/src/main/java/org/apache/qpid/server/failover/MessageDisappearWithIOExceptionTest.java @@ -263,7 +263,7 @@ public class MessageDisappearWithIOExceptionTest extends FailoverBaseCase implem throws Exception { //Create Connection using the default connection URL. i.e. not the Failover URL that would be used by default - _connection = (AMQConnection) getConnection(getConnectionFactory("default").getConnectionURL()); + _connection = (AMQConnection) getConnectionFactory("default").createConnection("guest", "guest"); // The default connection does not have any retries configured so // Allow this connection to retry so that we can block on the failover. // The alternative would be to use the getConnection() default. However, diff --git a/java/systests/src/main/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java b/java/systests/src/main/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java new file mode 100644 index 0000000000..21c121ff2c --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java @@ -0,0 +1,285 @@ +/* + * 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.acl; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import javax.jms.Connection; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.naming.NamingException; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.lang.StringUtils; +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQConnectionURL; +import org.apache.qpid.jms.ConnectionListener; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.test.utils.QpidTestCase; +import org.apache.qpid.url.URLSyntaxException; + +/** + * Abstract test case for ACLs. + * + * This base class contains convenience methods to mange ACL files and implements a mechanism that allows each + * test method to run its own setup code before the broker starts. + * + * TODO move the pre broker-startup setup method invocation code to {@link QpidTestCase} + * + * @see SimpleACLTest + * @see ExternalACLTest + * @see ExternalACLFileTest + * @see ExternalACLJMXTest + * @see ExternalAdminACLTest + * @see ExhaustiveACLTest + */ +public abstract class AbstractACLTestCase extends QpidTestCase implements ConnectionListener +{ + /** Used to synchronise {@link #tearDown()} when exceptions are thrown */ + protected CountDownLatch _exceptionReceived; + + /** Override this to return the name of the configuration XML file. */ + public String getConfig() + { + return "config-systests-acl.xml"; + } + + /** Override this to setup external ACL files for virtual hosts. */ + public List<String> getHostList() + { + return Collections.emptyList(); + } + + /** + * This setup method checks {@link #getConfig()} and {@link #getHostList()} to initialise the broker with specific + * ACL configurations and then runs an optional per-test setup method, which is simply a method with the same name + * as the test, but starting with {@code setUp} rather than {@code test}. + * + * @see #setUpACLFile(String) + * @see org.apache.qpid.test.utils.QpidTestCase#setUp() + */ + @Override + public void setUp() throws Exception + { + if (QpidHome == null) + { + fail("QPID_HOME not set"); + } + + // Initialise ACLs. + _configFile = new File(QpidHome, "etc" + File.separator + getConfig()); + + // Initialise ACL files + for (String virtualHost : getHostList()) + { + setUpACLFile(virtualHost); + } + + // run test specific setup + String testSetup = StringUtils.replace(getName(), "test", "setUp"); + try + { + Method setup = getClass().getDeclaredMethod(testSetup); + setup.invoke(this); + } + catch (NoSuchMethodException e) + { + // Ignore + } + catch (InvocationTargetException e) + { + throw (Exception) e.getTargetException(); + } + + super.setUp(); + } + + @Override + public void tearDown() throws Exception + { + try + { + super.tearDown(); + } + catch (JMSException e) + { + //we're throwing this away as it can happen in this test as the state manager remembers exceptions + //that we provoked with authentication failures, where the test passes - we can ignore on con close + } + } + + /** + * Configures specific ACL files for a virtual host. + * + * This method checks for ACL files that exist on the filesystem. If dynamically generatyed ACL files are required in a test, + * then it is easier to use the {@code setUp} prefix on a method to generate the ACL file. In order, this method looks + * for three files: + * <ol> + * <li><em>virtualhost</em>-<em>class</em>-<em>test</em>.txt + * <li><em>virtualhost</em>-<em>class</em>.txt + * <li><em>virtualhost</em>-default.txt + * </ol> + * The <em>class</em> and <em>test</em> parts are the test class and method names respectively, with the word {@code test} + * removed and the rest of the text converted to lowercase. For example, the test class and method named + * {@code org.apache.qpid.test.AccessExampleTest#testExampleMethod} on the {@code testhost} virtualhost would use + * one of the following files: + * <ol> + * <li>testhost-accessexample-examplemethod.txt + * <li>testhost-accessexample.txt + * <li>testhost-default.txt + * </ol> + * These files should be copied to the <em>${QPID_HOME}/etc</em> directory when the test is run. + * + * @see #writeACLFile(String, String...) + */ + public void setUpACLFile(String virtualHost) throws IOException, ConfigurationException + { + String path = QpidHome + File.separator + "etc"; + String className = StringUtils.substringBeforeLast(getClass().getSimpleName().toLowerCase(), "test"); + String testName = StringUtils.substringAfter(getName(), "test").toLowerCase(); + + File aclFile = new File(path, virtualHost + "-" + className + "-" + testName + ".txt"); + if (!aclFile.exists()) + { + aclFile = new File(path, virtualHost + "-" + className + ".txt"); + if (!aclFile.exists()) + { + aclFile = new File(path, virtualHost + "-" + "default.txt"); + } + } + + // Set the ACL file configuration property + if (virtualHost.equals("global")) + { + setConfigurationProperty("security.aclv2", aclFile.getAbsolutePath()); + } + else + { + setConfigurationProperty("virtualhosts.virtualhost." + virtualHost + ".security.aclv2", aclFile.getAbsolutePath()); + } + } + + public void writeACLFile(String vhost, String...rules) throws ConfigurationException, IOException + { + File aclFile = File.createTempFile(getClass().getSimpleName(), getName()); + aclFile.deleteOnExit(); + + if ("global".equals(vhost)) + { + setConfigurationProperty("security.aclv2", aclFile.getAbsolutePath()); + } + else + { + setConfigurationProperty("virtualhosts.virtualhost." + vhost + ".security.aclv2", aclFile.getAbsolutePath()); + } + + PrintWriter out = new PrintWriter(new FileWriter(aclFile)); + out.println(String.format("# %s", _testName)); + for (String line : rules) + { + out.println(line); + } + out.close(); + } + + /** + * Creates a connection to the broker, and sets a connection listener to prevent failover and an exception listener + * with a {@link CountDownLatch} to synchronise in the {@link #check403Exception(Throwable)} method and allow the + * {@link #tearDown()} method to complete properly. + */ + public Connection getConnection(String vhost, String username, String password) throws NamingException, JMSException, URLSyntaxException + { + AMQConnection connection = (AMQConnection) getConnection(createConnectionURL(vhost, username, password)); + + //Prevent Failover + connection.setConnectionListener(this); + + //QPID-2081: use a latch to sync on exception causing connection close, to work + //around the connection close race during tearDown() causing sporadic failures + _exceptionReceived = new CountDownLatch(1); + + connection.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException e) + { + _exceptionReceived.countDown(); + } + }); + + return (Connection) connection; + } + + // Connection Listener Interface - Used here to block failover + + public void bytesSent(long count) + { + } + + public void bytesReceived(long count) + { + } + + public boolean preFailover(boolean redirect) + { + //Prevent failover. + return false; + } + + public boolean preResubscribe() + { + return false; + } + + public void failoverComplete() + { + } + + /** + * Convenience method to build an {@link AMQConnectionURL} with the right parameters. + */ + public AMQConnectionURL createConnectionURL(String vhost, String username, String password) throws URLSyntaxException + { + String url = "amqp://" + username + ":" + password + "@clientid/" + vhost + "?brokerlist='" + getBroker() + "?retries='0''"; + return new AMQConnectionURL(url); + } + + /** + * Convenience method to validate a JMS exception with a linked {@link AMQConstant#ACCESS_REFUSED} 403 error code exception. + */ + public void check403Exception(Throwable t) throws Exception + { + assertNotNull("There was no linked exception", t); + assertTrue("Wrong linked exception type", t instanceof AMQException); + assertEquals("Incorrect error code received", 403, ((AMQException) t).getErrorCode().getCode()); + + //use the latch to ensure the control thread waits long enough for the exception thread + //to have done enough to mark the connection closed before teardown commences + assertTrue("Timed out waiting for conneciton to report close", _exceptionReceived.await(2, TimeUnit.SECONDS)); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExhaustiveACLTest.java b/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExhaustiveACLTest.java new file mode 100644 index 0000000000..1b2c98d30a --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExhaustiveACLTest.java @@ -0,0 +1,195 @@ +/* + * 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.acl; + +import java.util.Arrays; +import java.util.List; + +import javax.jms.Connection; +import javax.jms.Session; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.protocol.AMQConstant; + +/** + * ACL version 2/3 file testing to verify that ACL entries control queue creation with specific properties. + * + * Tests have their own ACL files that setup specific permissions, and then try to create queues with every possible combination + * of properties to show that rule matching works correctly. For example, a rule that specified {@code autodelete="true"} for + * queues with {@link name="temp.true.*"} as well should not affect queues that have names that do not match, or queues that + * are not autodelete, or both. Also checks that ACL entries only affect the specified users and virtual hosts. + */ +public class ExhaustiveACLTest extends AbstractACLTestCase +{ + @Override + public String getConfig() + { + return "config-systests-aclv2.xml"; + } + + @Override + public List<String> getHostList() + { + return Arrays.asList("test", "test2"); + } + + /** + * Creates a queue. + * + * Connects to the broker as a particular user and create the named queue on a virtual host, with the provided + * parameters. Uses a new {@link Connection} and {@link Session} and closes them afterwards. + */ + private void createQueue(String vhost, String user, String name, boolean autoDelete, boolean durable) throws Exception + { + Connection conn = getConnection(vhost, user, "guest"); + Session sess = conn.createSession(true, Session.SESSION_TRANSACTED); + conn.start(); + ((AMQSession<?, ?>) sess).createQueue(new AMQShortString(name), autoDelete, durable, false); + sess.commit(); + conn.close(); + } + + /** + * Calls {@link #createQueue(String, String, String, boolean, boolean)} with the provided parameters and checks that + * no exceptions were thrown. + */ + private void createQueueSuccess(String vhost, String user, String name, boolean autoDelete, boolean durable) throws Exception + { + try + { + createQueue(vhost, user, name, autoDelete, durable); + } + catch (AMQException e) + { + fail(String.format("Create queue should have worked for \"%s\" for user %s@%s, autoDelete=%s, durable=%s", + name, user, vhost, Boolean.toString(autoDelete), Boolean.toString(durable))); + } + } + + /** + * Calls {@link #createQueue(String, String, String, boolean, boolean)} with the provided parameters and checks that + * the exception thrown was an {@link AMQConstant#ACCESS_REFUSED} or 403 error code. + */ + private void createQueueFailure(String vhost, String user, String name, boolean autoDelete, boolean durable) throws Exception + { + try + { + createQueue(vhost, user, name, autoDelete, durable); + fail(String.format("Create queue should have failed for \"%s\" for user %s@%s, autoDelete=%s, durable=%s", + name, user, vhost, Boolean.toString(autoDelete), Boolean.toString(durable))); + } + catch (AMQException e) + { + assertEquals("Should be an ACCESS_REFUSED error", 403, e.getErrorCode().getCode()); + } + } + + public void setUpAuthoriseCreateQueueAutodelete() throws Exception + { + writeACLFile("test", + "acl allow client access virtualhost", + "acl allow server access virtualhost", + "acl allow client create queue name=\"temp.true.*\" autodelete=true", + "acl allow client create queue name=\"temp.false.*\" autodelete=false", + "acl deny client create queue", + "acl allow client delete queue", + "acl deny all create queue" + ); + } + + /** + * Test creation of temporary queues, with the autodelete property set to true. + */ + public void testAuthoriseCreateQueueAutodelete() throws Exception + { + createQueueSuccess("test", "client", "temp.true.00", true, false); + createQueueSuccess("test", "client", "temp.true.01", true, false); + createQueueSuccess("test", "client", "temp.true.02", true, true); + createQueueSuccess("test", "client", "temp.false.03", false, false); + createQueueSuccess("test", "client", "temp.false.04", false, false); + createQueueSuccess("test", "client", "temp.false.05", false, true); + createQueueFailure("test", "client", "temp.true.06", false, false); + createQueueFailure("test", "client", "temp.false.07", true, false); + createQueueFailure("test", "server", "temp.true.08", true, false); + createQueueFailure("test", "client", "temp.other.09", false, false); + createQueueSuccess("test2", "guest", "temp.true.01", false, false); + createQueueSuccess("test2", "guest", "temp.false.02", true, false); + createQueueSuccess("test2", "guest", "temp.true.03", true, false); + createQueueSuccess("test2", "guest", "temp.false.04", false, false); + createQueueSuccess("test2", "guest", "temp.other.05", false, false); + } + + public void setUpAuthoriseCreateQueue() throws Exception + { + writeACLFile("test", + "acl allow client access virtualhost", + "acl allow server access virtualhost", + "acl allow client create queue name=\"create.*\"" + ); + } + + /** + * Tests creation of named queues. + * + * If a named queue is specified + */ + public void testAuthoriseCreateQueue() throws Exception + { + createQueueSuccess("test", "client", "create.00", true, true); + createQueueSuccess("test", "client", "create.01", true, false); + createQueueSuccess("test", "client", "create.02", false, true); + createQueueSuccess("test", "client", "create.03", true, false); + createQueueFailure("test", "server", "create.04", true, true); + createQueueFailure("test", "server", "create.05", true, false); + createQueueFailure("test", "server", "create.06", false, true); + createQueueFailure("test", "server", "create.07", true, false); + createQueueSuccess("test2", "guest", "create.00", true, true); + createQueueSuccess("test2", "guest", "create.01", true, false); + createQueueSuccess("test2", "guest", "create.02", false, true); + createQueueSuccess("test2", "guest", "create.03", true, false); + } + + public void setUpAuthoriseCreateQueueBoth() throws Exception + { + writeACLFile("test", + "acl allow all access virtualhost", + "acl allow client create queue name=\"create.*\"", + "acl allow all create queue temporary=true" + ); + } + + /** + * Tests creation of named queues. + * + * If a named queue is specified + */ + public void testAuthoriseCreateQueueBoth() throws Exception + { + createQueueSuccess("test", "client", "create.00", true, false); + createQueueSuccess("test", "client", "create.01", false, false); + createQueueFailure("test", "server", "create.02", false, false); + createQueueFailure("test", "guest", "create.03", false, false); + createQueueSuccess("test", "client", "tmp.00", true, false); + createQueueSuccess("test", "server", "tmp.01", true, false); + createQueueSuccess("test", "guest", "tmp.02", true, false); + createQueueSuccess("test2", "guest", "create.02", false, false); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLFileTest.java b/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLFileTest.java new file mode 100644 index 0000000000..1d08015669 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLFileTest.java @@ -0,0 +1,184 @@ +/* + * 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.acl; + +import java.util.Arrays; +import java.util.List; + +import javax.jms.Connection; +import javax.jms.Session; + +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.framing.AMQShortString; + +/** + * Tests that ACL version 2/3 files following the specification work correctly. + * + * ACL lines that are identical in meaning apart from differences allowed by the specification, such as whitespace or case + * of tokens are set up for numbered queues and the queues are then created to show that the meaning is correctly parsed by + * the plugin. + * + * TODO move this to the access-control plugin unit tests instead + */ +public class ExternalACLFileTest extends AbstractACLTestCase +{ + @Override + public String getConfig() + { + return "config-systests-aclv2.xml"; + } + + @Override + public List<String> getHostList() + { + return Arrays.asList("test"); + } + + private void createQueuePrefixList(String prefix, int count) + { + try + { + Connection conn = getConnection("test", "client", "guest"); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + conn.start(); + + //Create n queues + for (int n = 0; n < count; n++) + { + AMQShortString queueName = new AMQShortString(String.format("%s.%03d", prefix, n)); + ((AMQSession<?, ?>) sess).createQueue(queueName, false, false, false); + } + + conn.close(); + } + catch (Exception e) + { + fail("Test failed due to:" + e.getMessage()); + } + } + + private void createQueueNameList(String...queueNames) + { + try + { + Connection conn = getConnection("test", "client", "guest"); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + conn.start(); + + //Create all queues + for (String queueName : queueNames) + { + ((AMQSession<?, ?>) sess).createQueue(new AMQShortString(queueName), false, false, false); + } + + conn.close(); + } + catch (Exception e) + { + fail("Test failed due to:" + e.getMessage()); + } + } + + public void setUpCreateQueueMixedCase() throws Exception + { + writeACLFile( + "test", + "acl allow client create queue name=mixed.000", + "ACL ALLOW client CREATE QUEUE NAME=mixed.001", + "Acl Allow client Create Queue Name=mixed.002", + "aCL aLLOW client cREATE qUEUE nAME=mixed.003", + "aCl AlLoW client cReAtE qUeUe NaMe=mixed.004" + ); + } + + public void testCreateQueueMixedCase() + { + createQueuePrefixList("mixed", 5); + } + + public void setUpCreateQueueContinuation() throws Exception + { + writeACLFile( + "test", + "acl allow client create queue name=continuation.000", + "acl allow client create queue \\", + " name=continuation.001", + "acl allow client \\", + " create queue \\", + " name=continuation.002", + "acl allow \\", + " client \\", + " create queue \\", + " name=continuation.003", + "acl \\", + " allow \\", + " client \\", + " create queue \\", + " name=continuation.004" + ); + } + + public void testCreateQueueContinuation() + { + createQueuePrefixList("continuation", 5); + } + + public void setUpCreateQueueWhitespace() throws Exception + { + writeACLFile( + "test", + "acl allow client create queue name=whitespace.000", + "acl\tallow\tclient\tcreate\tqueue\tname=whitespace.001", + "acl allow client create queue name = whitespace.002", + "acl\tallow\tclient\tcreate\tqueue\tname\t=\twhitespace.003", + "acl allow\t\tclient\t \tcreate\t\t queue\t \t name \t =\t \twhitespace.004" + ); + } + + public void testCreateQueueWhitespace() + { + createQueuePrefixList("whitespace", 5); + } + + public void setUpCreateQueueQuoting() throws Exception + { + writeACLFile( + "test", + "acl allow client create queue name='quoting.ABC.000'", + "acl allow client create queue name='quoting.*.000'", + "acl allow client create queue name='quoting.#.000'", + "acl allow client create queue name='quoting. .000'", + "acl allow client create queue name='quoting.!@$%.000'" + ); + } + + public void testCreateQueueQuoting() + { + createQueueNameList( + "quoting.ABC.000", + "quoting.*.000", + "quoting.#.000", + "quoting. .000", + "quoting.!@$%.000" + ); + } +} + + + diff --git a/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLJMXTest.java b/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLJMXTest.java new file mode 100644 index 0000000000..b823690002 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLJMXTest.java @@ -0,0 +1,244 @@ +/* + * 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.acl; + +import java.util.Arrays; +import java.util.List; + +import org.apache.qpid.AMQConnectionClosedException; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQSecurityException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.test.utils.JMXTestUtils; + +/** + * Tests that ACL entries that apply to AMQP objects also apply when those objects are accessed via JMX. + */ +public class ExternalACLJMXTest extends AbstractACLTestCase +{ + private JMXTestUtils _jmx; + + private static final String QUEUE_NAME = "kipper"; + private static final String EXCHANGE_NAME = "amq.kipper"; + + @Override + public String getConfig() + { + return "config-systests-aclv2.xml"; + } + + @Override + public List<String> getHostList() + { + return Arrays.asList("test"); + } + + @Override + public void setUp() throws Exception + { + _jmx = new JMXTestUtils(this, "admin", "admin"); + _jmx.setUp(); + super.setUp(); + _jmx.open(); + } + + @Override + public void tearDown() throws Exception + { + _jmx.close(); + super.tearDown(); + } + + // test-externalacljmx.txt + // create queue owner=client # success + public void testCreateClientQueueSuccess() throws Exception + { + //Queue Parameters + String queueOwner = "client"; + + _jmx.createQueue("test", QUEUE_NAME, queueOwner, true); + } + + // test-externalacljmx.txt + // create queue owner=client # failure + public void testCreateServerQueueFailure() throws Exception + { + //Queue Parameters + String queueOwner = "server"; + + try + { + _jmx.createQueue("test", QUEUE_NAME, queueOwner, true); + + fail("Queue create should fail"); + } + catch (Exception e) + { + assertNotNull("Cause is not set", e.getCause()); + assertEquals("Cause message incorrect", + "org.apache.qpid.AMQSecurityException: Permission denied: queue-name 'kipper' [error code 403: access refused]", e.getCause().getMessage()); + } + } + + // no create queue acl in file # failure + public void testCreateQueueFailure() throws Exception + { + //Queue Parameters + String queueOwner = "guest"; + + try + { + _jmx.createQueue("test", QUEUE_NAME, queueOwner, true); + + fail("Queue create should fail"); + } + catch (Exception e) + { + assertNotNull("Cause is not set", e.getCause()); + assertEquals("Cause message incorrect", + "org.apache.qpid.AMQSecurityException: Permission denied: queue-name 'kipper' [error code 403: access refused]", e.getCause().getMessage()); + } + } + + // test-externalacljmx.txt + // allow create exchange name=amq.kipper.success + public void testCreateExchangeSuccess() throws Exception + { + _jmx.createExchange("test", EXCHANGE_NAME + ".success", "direct", true); + } + + // test-externalacljmx.txt + // deny create exchange name=amq.kipper.failure + public void testCreateExchangeFailure() throws Exception + { + try + { + _jmx.createExchange("test", EXCHANGE_NAME + ".failure", "direct", true); + + fail("Exchange create should fail"); + } + catch (Exception e) + { + assertNotNull("Cause is not set", e.getCause()); + assertEquals("Cause message incorrect", + "org.apache.qpid.AMQSecurityException: Permission denied: exchange-name 'amq.kipper.failure' [error code 403: access refused]", e.getCause().getMessage()); + } + } + + // test-externalacljmx.txt + // allow create exchange name=amq.kipper.success + // allow delete exchange name=amq.kipper.success + public void testDeleteExchangeSuccess() throws Exception + { + _jmx.createExchange("test", EXCHANGE_NAME + ".success", "direct", true); + _jmx.unregisterExchange("test", EXCHANGE_NAME + ".success"); + } + + // test-externalacljmx-deleteexchangefailure.txt + // allow create exchange name=amq.kipper.delete + // deny delete exchange name=amq.kipper.delete + public void testDeleteExchangeFailure() throws Exception + { + _jmx.createExchange("test", EXCHANGE_NAME + ".delete", "direct", true); + try + { + _jmx.unregisterExchange("test", EXCHANGE_NAME + ".delete"); + + fail("Exchange delete should fail"); + } + catch (Exception e) + { + assertNotNull("Cause is not set", e.getCause()); + assertEquals("Cause message incorrect", + "org.apache.qpid.AMQSecurityException: Permission denied [error code 403: access refused]", e.getCause().getMessage()); + } + } + + /** + * admin user has JMX right but not AMQP + */ + public void setUpCreateQueueJMXRights() throws Exception + { + writeACLFile("test", + "ACL ALLOW admin EXECUTE METHOD component=\"VirtualHost.VirtualHostManager\" name=\"createNewQueue\"", + "ACL DENY admin CREATE QUEUE"); + } + + public void testCreateQueueJMXRights() throws Exception + { + try + { + _jmx.createQueue("test", QUEUE_NAME, "admin", true); + + fail("Queue create should fail"); + } + catch (Exception e) + { + assertNotNull("Cause is not set", e.getCause()); + assertEquals("Cause message incorrect", + "org.apache.qpid.AMQSecurityException: Permission denied: queue-name 'kipper' [error code 403: access refused]", e.getCause().getMessage()); + } + } + + /** + * admin user has AMQP right but not JMX + */ + public void setUpCreateQueueAMQPRights() throws Exception + { + writeACLFile("test", + "ACL DENY admin EXECUTE METHOD component=\"VirtualHost.VirtualHostManager\" name=\"createNewQueue\"", + "ACL ALLOW admin CREATE QUEUE"); + } + + public void testCreateQueueAMQPRights() throws Exception + { + try + { + _jmx.createQueue("test", QUEUE_NAME, "admin", true); + + fail("Queue create should fail"); + } + catch (Exception e) + { + assertEquals("Cause message incorrect", "Permission denied: Execute createNewQueue", e.getMessage()); + } + } + + /** + * admin has both JMX and AMQP rights + */ + public void setUpCreateQueueJMXAMQPRights() throws Exception + { + writeACLFile("test", + "ACL ALLOW admin EXECUTE METHOD component=\"VirtualHost.VirtualHostManager\" name=\"createNewQueue\"", + "ACL ALLOW admin CREATE QUEUE"); + } + + public void testCreateQueueJMXAMQPRights() throws Exception + { + try + { + _jmx.createQueue("test", QUEUE_NAME, "admin", true); + } + catch (Exception e) + { + fail("Queue create should succeed: " + e.getCause().getMessage()); + } + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/Accessable.java b/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLTest.java index f51cf24caa..4603cc1862 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/access/Accessable.java +++ b/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLTest.java @@ -4,7 +4,7 @@ * 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 +* "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 @@ -14,14 +14,24 @@ * "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. - * - * + * under the License. */ -package org.apache.qpid.server.security.access; +package org.apache.qpid.server.security.acl; + +import java.util.Arrays; +import java.util.List; -public interface Accessable +public class ExternalACLTest extends SimpleACLTest { - void setAccessableName(String name); - String getAccessableName(); + @Override + public String getConfig() + { + return "config-systests-aclv2.xml"; + } + + @Override + public List<String> getHostList() + { + return Arrays.asList("test", "test2"); + } } diff --git a/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalAdminACLTest.java b/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalAdminACLTest.java new file mode 100644 index 0000000000..290cbfdc14 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalAdminACLTest.java @@ -0,0 +1,186 @@ +/* + * 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.acl; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import org.apache.qpid.server.logging.management.LoggingManagementMBean; +import org.apache.qpid.test.utils.JMXTestUtils; + +/** + * Tests that ACLs can be applied to mangement operations that do not correspond to a specific AMQP object. + * + * Theses tests use the logging component, exposed as the {@link LoggingManagementMBean}, to get and set properties. + */ +public class ExternalAdminACLTest extends AbstractACLTestCase +{ + private static final String CATEGORY_PRIORITY = "LogManMBeanTest.category.priority"; + private static final String CATEGORY_LEVEL = "LogManMBeanTest.category.level"; + private static final String LOGGER_LEVEL = "LogManMBeanTest.logger.level"; + + private static final String NEWLINE = System.getProperty("line.separator"); + + private JMXTestUtils _jmx; + private File _testConfigFile; + + @Override + public String getConfig() + { + return "config-systests-aclv2.xml"; + } + + @Override + public List<String> getHostList() + { + return Arrays.asList("global"); + } + + @Override + public void setUp() throws Exception + { + _testConfigFile = createTempTestLog4JConfig(); + + _jmx = new JMXTestUtils(this, "admin", "admin"); + _jmx.setUp(); + super.setUp(); + _jmx.open(); + } + + @Override + public void tearDown() throws Exception + { + _jmx.close(); + super.tearDown(); + } + + private File createTempTestLog4JConfig() + { + File tmpFile = null; + try + { + tmpFile = File.createTempFile("LogManMBeanTestLog4jConfig", ".tmp"); + tmpFile.deleteOnExit(); + + FileWriter fstream = new FileWriter(tmpFile); + BufferedWriter writer = new BufferedWriter(fstream); + + writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+NEWLINE); + writer.write("<!DOCTYPE log4j:configuration SYSTEM \"log4j.dtd\">"+NEWLINE); + + writer.write("<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\" debug=\"null\" " + + "threshold=\"null\">"+NEWLINE); + + writer.write(" <appender class=\"org.apache.log4j.ConsoleAppender\" name=\"STDOUT\">"+NEWLINE); + writer.write(" <layout class=\"org.apache.log4j.PatternLayout\">"+NEWLINE); + writer.write(" <param name=\"ConversionPattern\" value=\"%d %-5p [%t] %C{2} (%F:%L) - %m%n\"/>"+NEWLINE); + writer.write(" </layout>"+NEWLINE); + writer.write(" </appender>"+NEWLINE); + + //Example of a 'category' with a 'priority' + writer.write(" <category additivity=\"true\" name=\"" + CATEGORY_PRIORITY +"\">"+NEWLINE); + writer.write(" <priority value=\"info\"/>"+NEWLINE); + writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE); + writer.write(" </category>"+NEWLINE); + + //Example of a 'category' with a 'level' + writer.write(" <category additivity=\"true\" name=\"" + CATEGORY_LEVEL +"\">"+NEWLINE); + writer.write(" <level value=\"warn\"/>"+NEWLINE); + writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE); + writer.write(" </category>"+NEWLINE); + + //Example of a 'logger' with a 'level' + writer.write(" <logger additivity=\"true\" name=\"" + LOGGER_LEVEL + "\">"+NEWLINE); + writer.write(" <level value=\"error\"/>"+NEWLINE); + writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE); + writer.write(" </logger>"+NEWLINE); + + //'root' logger + writer.write(" <root>"+NEWLINE); + writer.write(" <priority value=\"info\"/>"+NEWLINE); + writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE); + writer.write(" </root>"+NEWLINE); + + writer.write("</log4j:configuration>"+NEWLINE); + + writer.flush(); + writer.close(); + } + catch (IOException e) + { + fail("Unable to create temporary test log4j configuration"); + } + + return tmpFile; + } + + public void testGetAllLoggerLevels() throws Exception + { + String[] levels = _jmx.getAvailableLoggerLevels(); + for (int i = 0; i < levels.length; i++) + { + System.out.println(levels[i]); + } + assertEquals("Got incorrect number of log levels", 9, levels.length); + } + + public void testGetAllLoggerLevelsDenied() throws Exception + { + try + { + _jmx.getAvailableLoggerLevels(); + fail("Got list of log levels"); + } + catch (Exception e) + { + // Exception throws + e.printStackTrace(); + assertEquals("Permission denied: Access getAvailableLoggerLevels", e.getMessage()); + } + } + + public void testChangeLoggerLevel() throws Exception + { + String oldLevel = _jmx.getRuntimeRootLoggerLevel(); + System.out.println("old level = " + oldLevel); + _jmx.setRuntimeRootLoggerLevel("DEBUG"); + String newLevel = _jmx.getRuntimeRootLoggerLevel(); + System.out.println("new level = " + newLevel); + assertEquals("Logging level was not changed", "DEBUG", newLevel); + } + + public void testChangeLoggerLevelDenied() throws Exception + { + try + { + _jmx.setRuntimeRootLoggerLevel("DEBUG"); + fail("Logging level was changed"); + } + catch (Exception e) + { + assertEquals("Permission denied: Update setRuntimeRootLoggerLevel", e.getMessage()); + } + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java b/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java index 08fc047533..fc9b07eadd 100644 --- a/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java +++ b/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java @@ -4,7 +4,7 @@ * 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 + * "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 @@ -15,28 +15,13 @@ * 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.acl; -import org.apache.commons.configuration.ConfigurationException; -import org.apache.qpid.AMQException; -import org.apache.qpid.AMQConnectionFailureException; -import org.apache.qpid.client.AMQAuthenticationException; -import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.AMQConnectionURL; -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.jms.ConnectionListener; -import org.apache.qpid.jms.ConnectionURL; -import org.apache.qpid.test.utils.QpidTestCase; -import org.apache.qpid.url.URLSyntaxException; +import java.io.IOException; import javax.jms.Connection; import javax.jms.DeliveryMode; -import javax.jms.ExceptionListener; import javax.jms.IllegalStateException; import javax.jms.JMSException; import javax.jms.Message; @@ -45,71 +30,45 @@ import javax.jms.MessageProducer; import javax.jms.Queue; import javax.jms.Session; import javax.jms.TextMessage; +import javax.jms.Topic; +import javax.jms.TopicSubscriber; import javax.naming.NamingException; -import java.io.File; -import java.io.IOException; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -public class SimpleACLTest extends QpidTestCase implements ConnectionListener -{ - public void setUp() throws Exception - { - //Performing setUp here would result in a broker with the default ACL test config - - //Each test now calls the private setUpACLTest to allow them to make - //individual customisations to the base ACL settings - } - - public void tearDown() throws Exception - { - try - { - super.tearDown(); - } - catch (JMSException e) - { - //we're throwing this away as it can happen in this test as the state manager remembers exceptions - //that we provoked with authentication failures, where the test passes - we can ignore on con close - } - } - - private void setUpACLTest() throws Exception - { - final String QPID_HOME = System.getProperty("QPID_HOME"); - - if (QPID_HOME == null) - { - fail("QPID_HOME not set"); - } - - // Initialise ACLs. - _configFile = new File(QPID_HOME, "etc/config-systests-acl.xml"); - - super.setUp(); - } - - public String createConnectionString(String username, String password) - { - - return "amqp://" + username + ":" + password + "@clientid/test?brokerlist='" + getBroker() + "?retries='0''"; - } +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.url.URLSyntaxException; +/** + * Basic access control list tests. + * + * These tests require an access control security plugin to be configured in the broker, and carry out various broker + * operations that will succeed or fail depending on the user and virtual host. See the {@code config-systests-acl-setup.xml} + * configuration file for the {@link SimpleXML} version of the ACLs used by the Java broker only, or the various {@code .txt} + * files in the system tests directory for the external version 3 ACL files used by both the Java and C++ brokers. + * <p> + * This class can be extended and the {@link #getConfig()} method overridden to run the same tests with a different type + * of access control mechanism. Extension classes should differ only in the configuration file used, but extra tests can be + * added that are specific to a particular configuration. + * <p> + * The tests perform basic AMQP operations like creating queues or excahnges and publishing and consuming messages, using + * JMS to contact the broker. + * + * @see ExternalACLTest + */ +public class SimpleACLTest extends AbstractACLTestCase +{ public void testAccessAuthorized() throws AMQException, URLSyntaxException, Exception { - setUpACLTest(); - try { - Connection conn = getConnection("client", "guest"); - - Session sesh = conn.createSession(true, Session.SESSION_TRANSACTED); - + Connection conn = getConnection("test", "client", "guest"); + Session sess = conn.createSession(true, Session.SESSION_TRANSACTED); conn.start(); //Do something to show connection is active. - sesh.rollback(); + sess.rollback(); conn.close(); } @@ -125,8 +84,6 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener //is unable to perform actions such as connecting (and by extension, creating a queue, and consuming //from a queue etc). In order to test the vhost-wide 'access' ACL right, the 'guest' user has been given //this right in the 'test2' vhost. - - setUpACLTest(); try { @@ -141,16 +98,16 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener conn.start(); //create Queues and consumers for each - Queue namedQueue = sesh.createQueue("vhostAccessCreatedQueue" + getTestQueueName()); - Queue tempQueue = sesh.createTemporaryQueue(); - MessageConsumer consumer = sesh.createConsumer(namedQueue); - MessageConsumer tempConsumer = sesh.createConsumer(tempQueue); + Queue namedQueue = sess.createQueue("vhostAccessCreatedQueue" + getTestQueueName()); + Queue tempQueue = sess.createTemporaryQueue(); + MessageConsumer consumer = sess.createConsumer(namedQueue); + MessageConsumer tempConsumer = sess.createConsumer(tempQueue); //send a message to each queue (also causing an exchange declare) - MessageProducer sender = ((AMQSession)sesh).createProducer(null); - ((org.apache.qpid.jms.MessageProducer) sender).send(namedQueue, sesh.createTextMessage("test"), + MessageProducer sender = ((AMQSession<?, ?>) sess).createProducer(null); + ((org.apache.qpid.jms.MessageProducer) sender).send(namedQueue, sess.createTextMessage("test"), DeliveryMode.NON_PERSISTENT, 0, 0L, false, false, true); - ((org.apache.qpid.jms.MessageProducer) sender).send(tempQueue, sesh.createTextMessage("test"), + ((org.apache.qpid.jms.MessageProducer) sender).send(tempQueue, sess.createTextMessage("test"), DeliveryMode.NON_PERSISTENT, 0, 0L, false, false, true); //consume the messages from the queues @@ -165,47 +122,93 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener } } + // XXX one public void testAccessNoRights() throws Exception { - setUpACLTest(); - try { - Connection conn = getConnection("guest", "guest"); - - //Attempt to do do things to test connection. - Session sesh = conn.createSession(true, Session.SESSION_TRANSACTED); + Connection conn = getConnection("test", "guest", "guest"); + Session sess = conn.createSession(true, Session.SESSION_TRANSACTED); conn.start(); - sesh.rollback(); - + sess.rollback(); + fail("Connection was created."); } - catch (JMSException jmse) + catch (JMSException e) { - Throwable linkedException = jmse.getLinkedException(); - assertNotNull("Cause was null", linkedException); + // XXX JMSException -> linkedException -> cause = AMQException.403 + Exception linkedException = e.getLinkedException(); + assertNotNull("There was no linked exception", linkedException); + Throwable cause = linkedException.getCause(); + assertNotNull("Cause was null", cause); + assertTrue("Wrong linked exception type",cause instanceof AMQException); + assertEquals("Incorrect error code received", 403, ((AMQException) cause).getErrorCode().getCode()); + } + } + + public void testClientDeleteQueueSuccess() throws Exception + { + try + { + Connection conn = getConnection("test", "client", "guest"); + Session sess = conn.createSession(true, Session.SESSION_TRANSACTED); + conn.start(); - assertEquals("Linked Exception was wrong type", AMQConnectionFailureException.class, linkedException.getClass()); + // create kipper + Topic kipper = sess.createTopic("kipper"); + TopicSubscriber subscriber = sess.createDurableSubscriber(kipper, "kipper"); - Throwable cause = linkedException.getCause(); - assertEquals("Cause was wrong type", AMQAuthenticationException.class, cause.getClass()); - assertEquals("Incorrect error code thrown", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode()); + subscriber.close(); + sess.unsubscribe("kipper"); + + //Do something to show connection is active. + sess.rollback(); + conn.close(); + } + catch (Exception e) + { + fail("Test failed due to:" + e.getMessage()); + } + } + + // XXX two + public void testServerDeleteQueueFailure() throws Exception + { + try + { + Connection conn = getConnection("test", "server", "guest"); + Session sess = conn.createSession(true, Session.SESSION_TRANSACTED); + conn.start(); + + // create kipper + Topic kipper = sess.createTopic("kipper"); + TopicSubscriber subscriber = sess.createDurableSubscriber(kipper, "kipper"); + + subscriber.close(); + sess.unsubscribe("kipper"); + + //Do something to show connection is active. + sess.rollback(); + conn.close(); + } + catch (JMSException e) + { + // XXX JMSException -> linedException = AMQException.403 + check403Exception(e.getLinkedException()); } } public void testClientConsumeFromTempQueueValid() throws AMQException, URLSyntaxException, Exception { - setUpACLTest(); - try { - Connection conn = getConnection("client", "guest"); + Connection conn = getConnection("test", "client", "guest"); - Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); - sesh.createConsumer(sesh.createTemporaryQueue()); + sess.createConsumer(sess.createTemporaryQueue()); conn.close(); } @@ -217,64 +220,38 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener public void testClientConsumeFromNamedQueueInvalid() throws NamingException, Exception { - setUpACLTest(); - - //QPID-2081: use a latch to sync on exception causing connection close, to work - //around the connection close race during tearDown() causing sporadic failures - final CountDownLatch exceptionReceived = new CountDownLatch(1); - try { - Connection conn = getConnection("client", "guest"); - - //Prevent Failover - ((AMQConnection) conn).setConnectionListener(this); - - conn.setExceptionListener(new ExceptionListener() - { - public void onException(JMSException e) - { - exceptionReceived.countDown(); - } - }); + Connection conn = getConnection("test", "client", "guest"); - Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); - sesh.createConsumer(sesh.createQueue("IllegalQueue")); + sess.createConsumer(sess.createQueue("IllegalQueue")); + + conn.close(); + fail("Test failed as consumer was created."); - //conn will be automatically closed } catch (JMSException e) { - Throwable cause = e.getLinkedException(); - - assertNotNull("There was no liked exception", cause); - assertEquals("Wrong linked exception type", AMQAuthenticationException.class, cause.getClass()); - assertEquals("Incorrect error code received", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode()); - - //use the latch to ensure the control thread waits long enough for the exception thread - //to have done enough to mark the connection closed before teardown commences - assertTrue("Timed out waiting for conneciton to report close", - exceptionReceived.await(2, TimeUnit.SECONDS)); + check403Exception(e.getLinkedException()); } } public void testClientCreateTemporaryQueue() throws JMSException, URLSyntaxException, Exception { - setUpACLTest(); - try { - Connection conn = getConnection("client", "guest"); + Connection conn = getConnection("test", "client", "guest"); - Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); //Create Temporary Queue - can't use the createTempQueue as QueueName is null. - ((AMQSession) sesh).createQueue(new AMQShortString("doesnt_matter_as_autodelete_means_tmp"), + ((AMQSession<?, ?>) sess).createQueue(new AMQShortString("doesnt_matter_as_autodelete_means_tmp"), true, false, false); conn.close(); @@ -287,66 +264,43 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener public void testClientCreateNamedQueue() throws NamingException, JMSException, AMQException, Exception { - setUpACLTest(); - - //QPID-2081: use a latch to sync on exception causing connection close, to work - //around the connection close race during tearDown() causing sporadic failures - final CountDownLatch exceptionReceived = new CountDownLatch(1); - try { - Connection conn = getConnection("client", "guest"); + Connection conn = getConnection("test", "client", "guest"); - Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); - - conn.setExceptionListener(new ExceptionListener() - { - public void onException(JMSException e) - { - exceptionReceived.countDown(); - } - }); //Create a Named Queue - ((AMQSession) sesh).createQueue(new AMQShortString("IllegalQueue"), false, false, false); - + ((AMQSession<?, ?>) sess).createQueue(new AMQShortString("IllegalQueue"), false, false, false); + fail("Test failed as Queue creation succeded."); //conn will be automatically closed } - catch (AMQAuthenticationException amqe) + catch (AMQException e) { - amqe.printStackTrace(); - assertEquals("Incorrect error code thrown", 403, ((AMQAuthenticationException) amqe).getErrorCode().getCode()); - - //use the latch to ensure the control thread waits long enough for the exception thread - //to have done enough to mark the connection closed before teardown commences - assertTrue("Timed out waiting for conneciton to report close", - exceptionReceived.await(2, TimeUnit.SECONDS)); + // XXX AMQException.403 + check403Exception(e); } } public void testClientPublishUsingTransactionSuccess() throws AMQException, URLSyntaxException, Exception { - setUpACLTest(); - try { - Connection conn = getConnection("client", "guest"); - - ((AMQConnection) conn).setConnectionListener(this); + Connection conn = getConnection("test", "client", "guest"); - Session sesh = conn.createSession(true, Session.SESSION_TRANSACTED); + Session sess = conn.createSession(true, Session.SESSION_TRANSACTED); conn.start(); - MessageProducer sender = sesh.createProducer(sesh.createQueue("example.RequestQueue")); + MessageProducer sender = sess.createProducer(sess.createQueue("example.RequestQueue")); - sender.send(sesh.createTextMessage("test")); + sender.send(sess.createTextMessage("test")); //Send the message using a transaction as this will allow us to retrieve any errors that occur on the broker. - sesh.commit(); + sess.commit(); conn.close(); } @@ -358,26 +312,22 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener public void testClientPublishValidQueueSuccess() throws AMQException, URLSyntaxException, Exception { - setUpACLTest(); - try { - Connection conn = getConnection("client", "guest"); + Connection conn = getConnection("test", "client", "guest"); - ((AMQConnection) conn).setConnectionListener(this); - - Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); - MessageProducer sender = ((AMQSession) sesh).createProducer(null); + MessageProducer sender = ((AMQSession<?, ?>) sess).createProducer(null); - Queue queue = sesh.createQueue("example.RequestQueue"); + Queue queue = sess.createQueue("example.RequestQueue"); // Send a message that we will wait to be sent, this should give the broker time to process the msg // before we finish this test. Message is set !immed !mand as the queue is invalid so want to test ACLs not // queue existence. - ((org.apache.qpid.jms.MessageProducer) sender).send(queue, sesh.createTextMessage("test"), + ((org.apache.qpid.jms.MessageProducer) sender).send(queue, sess.createTextMessage("test"), DeliveryMode.NON_PERSISTENT, 0, 0L, false, false, true); conn.close(); @@ -390,31 +340,15 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener public void testClientPublishInvalidQueueSuccess() throws AMQException, URLSyntaxException, JMSException, NamingException, Exception { - setUpACLTest(); - - //QPID-2081: use a latch to sync on exception causing connection close, to work - //around the connection close race during tearDown() causing sporadic failures - final CountDownLatch exceptionReceived = new CountDownLatch(1); - try { - Connection conn = getConnection("client", "guest"); - - ((AMQConnection) conn).setConnectionListener(this); - - conn.setExceptionListener(new ExceptionListener() - { - public void onException(JMSException e) - { - exceptionReceived.countDown(); - } - }); + Connection conn = getConnection("test", "client", "guest"); Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); - MessageProducer sender = ((AMQSession) session).createProducer(null); + MessageProducer sender = ((AMQSession<?, ?>) session).createProducer(null); Queue queue = session.createQueue("Invalid"); @@ -435,53 +369,27 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener fail("Close is not expected to succeed."); } + catch (IllegalStateException e) + { + _logger.info("QPID-2345: Session became closed and we got that error rather than the authentication error."); + } catch (JMSException e) { - Throwable cause = e.getLinkedException(); - - if (cause == null) - { - e.printStackTrace(System.out); - if (e instanceof IllegalStateException) - { - System.out.println("QPID-2345: Session became closed and we got that error rather than the authentication error."); - } - else - { - fail("JMS Exception of did not have cause.:" + e.getMessage()); - } - } - else - { - if (!(cause instanceof AMQAuthenticationException)) - { - e.printStackTrace(); - } - assertEquals("Incorrect exception", AMQAuthenticationException.class, cause.getClass()); - assertEquals("Incorrect error code thrown", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode()); - - //use the latch to ensure the control thread waits long enough for the exception thread - //to have done enough to mark the connection closed before teardown commences - assertTrue("Timed out waiting for connection to report close", - exceptionReceived.await(2, TimeUnit.SECONDS)); - } - + check403Exception(e.getLinkedException()); } } public void testServerConsumeFromNamedQueueValid() throws AMQException, URLSyntaxException, Exception { - setUpACLTest(); - try { - Connection conn = getConnection("server", "guest"); + Connection conn = getConnection("test", "server", "guest"); - Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); - sesh.createConsumer(sesh.createQueue("example.RequestQueue")); + sess.createConsumer(sess.createQueue("example.RequestQueue")); conn.close(); } @@ -493,116 +401,58 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener public void testServerConsumeFromNamedQueueInvalid() throws AMQException, URLSyntaxException, NamingException, Exception { - setUpACLTest(); - - //QPID-2081: use a latch to sync on exception causing connection close, to work - //around the connection close race during tearDown() causing sporadic failures - final CountDownLatch exceptionReceived = new CountDownLatch(1); - try { - Connection conn = getConnection("client", "guest"); - - conn.setExceptionListener(new ExceptionListener() - { - public void onException(JMSException e) - { - exceptionReceived.countDown(); - } - }); + Connection conn = getConnection("test", "client", "guest"); - Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); - sesh.createConsumer(sesh.createQueue("Invalid")); + sess.createConsumer(sess.createQueue("Invalid")); + + conn.close(); fail("Test failed as consumer was created."); - //conn will be automatically closed } catch (JMSException e) { - Throwable cause = e.getLinkedException(); - - assertNotNull("There was no liked exception", cause); - assertEquals("Wrong linked exception type", AMQAuthenticationException.class, cause.getClass()); - assertEquals("Incorrect error code received", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode()); - - //use the latch to ensure the control thread waits long enough for the exception thread - //to have done enough to mark the connection closed before teardown commences - assertTrue("Timed out waiting for conneciton to report close", - exceptionReceived.await(2, TimeUnit.SECONDS)); + check403Exception(e.getLinkedException()); } } public void testServerConsumeFromTemporaryQueue() throws AMQException, URLSyntaxException, NamingException, Exception { - setUpACLTest(); - - //QPID-2081: use a latch to sync on exception causing connection close, to work - //around the connection close race during tearDown() causing sporadic failures - final CountDownLatch exceptionReceived = new CountDownLatch(1); - try { - Connection conn = getConnection("server", "guest"); - - conn.setExceptionListener(new ExceptionListener() - { - public void onException(JMSException e) - { - exceptionReceived.countDown(); - } - }); + Connection conn = getConnection("test", "server", "guest"); - Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); - sesh.createConsumer(sesh.createTemporaryQueue()); + sess.createConsumer(sess.createTemporaryQueue()); + fail("Test failed as consumer was created."); - //conn will be automatically closed } catch (JMSException e) { - Throwable cause = e.getLinkedException(); - - assertNotNull("There was no liked exception", cause); - assertEquals("Wrong linked exception type", AMQAuthenticationException.class, cause.getClass()); - assertEquals("Incorrect error code received", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode()); - - //use the latch to ensure the control thread waits long enough for the exception thread - //to have done enough to mark the connection closed before teardown commences - assertTrue("Timed out waiting for conneciton to report close", - exceptionReceived.await(2, TimeUnit.SECONDS)); + check403Exception(e.getLinkedException()); } } - @Override - public Connection getConnection(String username, String password) throws NamingException, JMSException - { - AMQConnection connection = (AMQConnection) super.getConnection(username, password); - - //Prevent Failover - connection.setConnectionListener(this); - - return (Connection) connection; - } - public void testServerCreateNamedQueueValid() throws JMSException, URLSyntaxException, Exception { - setUpACLTest(); - try { - Connection conn = getConnection("server", "guest"); + Connection conn = getConnection("test", "server", "guest"); - Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); //Create Temporary Queue - ((AMQSession) sesh).createQueue(new AMQShortString("example.RequestQueue"), false, false, false); + ((AMQSession<?, ?>) sess).createQueue(new AMQShortString("example.RequestQueue"), false, false, false); conn.close(); } @@ -614,65 +464,30 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener public void testServerCreateNamedQueueInvalid() throws JMSException, URLSyntaxException, AMQException, NamingException, Exception { - setUpACLTest(); - - //QPID-2081: use a latch to sync on exception causing connection close, to work - //around the connection close race during tearDown() causing sporadic failures - final CountDownLatch exceptionReceived = new CountDownLatch(1); - try { - Connection conn = getConnection("server", "guest"); - - conn.setExceptionListener(new ExceptionListener() - { - public void onException(JMSException e) - { - exceptionReceived.countDown(); - } - }); + Connection conn = getConnection("test", "server", "guest"); - Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); //Create a Named Queue - ((AMQSession) sesh).createQueue(new AMQShortString("IllegalQueue"), false, false, false); + ((AMQSession<?, ?>) sess).createQueue(new AMQShortString("IllegalQueue"), false, false, false); fail("Test failed as creation succeded."); - //conn will be automatically closed } - catch (AMQAuthenticationException amqe) + catch (Exception e) { - assertEquals("Incorrect error code thrown", 403, amqe.getErrorCode().getCode()); - - //use the latch to ensure the control thread waits long enough for the exception thread - //to have done enough to mark the connection closed before teardown commences - assertTrue("Timed out waiting for conneciton to report close", - exceptionReceived.await(2, TimeUnit.SECONDS)); + check403Exception(e); } } public void testServerCreateTemporaryQueueInvalid() throws NamingException, Exception { - setUpACLTest(); - - //QPID-2081: use a latch to sync on exception causing connection close, to work - //around the connection close race during tearDown() causing sporadic failures - final CountDownLatch exceptionReceived = new CountDownLatch(1); - - Connection conn = getConnection("server", "guest"); try { - - conn.setExceptionListener(new ExceptionListener() - { - public void onException(JMSException e) - { - exceptionReceived.countDown(); - } - }); - + Connection conn = getConnection("test", "server", "guest"); Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); @@ -683,69 +498,28 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener } catch (JMSException e) { - Throwable cause = e.getLinkedException(); - - assertNotNull("There was no liked exception", cause); - assertEquals("Wrong linked exception type", AMQAuthenticationException.class, cause.getClass()); - assertEquals("Incorrect error code received", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode()); - - //use the latch to ensure the control thread waits long enough for the exception thread - //to have done enough to mark the connection closed before teardown commences - assertTrue("Timed out waiting for conneciton to report close", - exceptionReceived.await(2, TimeUnit.SECONDS)); - } - finally - { - try - { - conn.close(); - } - catch (Exception e) - { - // This normally fails because we are denied - } + check403Exception(e.getLinkedException()); } } - + public void testServerCreateAutoDeleteQueueInvalid() throws NamingException, JMSException, AMQException, Exception { - setUpACLTest(); - - //QPID-2081: use a latch to sync on exception causing connection close, to work - //around the connection close race during tearDown() causing sporadic failures - final CountDownLatch exceptionReceived = new CountDownLatch(1); - - Connection connection = null; try { - connection = getConnection("server", "guest"); - - connection.setExceptionListener(new ExceptionListener() - { - public void onException(JMSException e) - { - exceptionReceived.countDown(); - } - }); + Connection connection = getConnection("test", "server", "guest"); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); connection.start(); - ((AMQSession) session).createQueue(new AMQShortString("again_ensure_auto_delete_queue_for_temporary"), + ((AMQSession<?, ?>) session).createQueue(new AMQShortString("again_ensure_auto_delete_queue_for_temporary"), true, false, false); fail("Test failed as creation succeded."); - //connection will be automatically closed } - catch (AMQAuthenticationException amqe) + catch (Exception e) { - assertEquals("Incorrect error code thrown", 403, amqe.getErrorCode().getCode()); - - //use the latch to ensure the control thread waits long enough for the exception thread - //to have done enough to mark the connection closed before teardown commences - assertTrue("Timed out waiting for conneciton to report close", - exceptionReceived.await(2, TimeUnit.SECONDS)); + check403Exception(e); } } @@ -759,12 +533,8 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener */ public void testServerPublishUsingTransactionSuccess() throws AMQException, URLSyntaxException, JMSException, NamingException, Exception { - setUpACLTest(); - //Set up the Server - Connection serverConnection = getConnection("server", "guest"); - - ((AMQConnection) serverConnection).setConnectionListener(this); + Connection serverConnection = getConnection("test", "server", "guest"); Session serverSession = serverConnection.createSession(true, Session.SESSION_TRANSACTED); @@ -775,7 +545,7 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener serverConnection.start(); //Set up the consumer - Connection clientConnection = getConnection("client", "guest"); + Connection clientConnection = getConnection("test", "client", "guest"); //Send a test mesage Session clientSession = clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -841,23 +611,9 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener public void testServerPublishInvalidQueueSuccess() throws AMQException, URLSyntaxException, JMSException, NamingException, Exception { - setUpACLTest(); - - //QPID-2081: use a latch to sync on exception causing connection close, to work - //around the connection close race during tearDown() causing sporadic failures - final CountDownLatch exceptionReceived = new CountDownLatch(1); - try { - Connection conn = getConnection("server", "guest"); - - conn.setExceptionListener(new ExceptionListener() - { - public void onException(JMSException e) - { - exceptionReceived.countDown(); - } - }); + Connection conn = getConnection("test", "server", "guest"); ((AMQConnection) conn).setConnectionListener(this); @@ -865,7 +621,7 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener conn.start(); - MessageProducer sender = ((AMQSession) session).createProducer(null); + MessageProducer sender = ((AMQSession<?, ?>) session).createProducer(null); Queue queue = session.createQueue("Invalid"); @@ -886,63 +642,13 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener fail("Close is not expected to succeed."); } + catch (IllegalStateException e) + { + _logger.info("QPID-2345: Session became closed and we got that error rather than the authentication error."); + } catch (JMSException e) { - Throwable cause = e.getLinkedException(); - - if (cause == null) - { - e.printStackTrace(System.out); - if (e instanceof IllegalStateException) - { - System.out.println("QPID-2345: Session became closed and we got that error rather than the authentication error."); - } - else - { - fail("JMS Exception of did not have cause.:" + e.getMessage()); - } - } - else if (!(cause instanceof AMQAuthenticationException)) - { - cause.printStackTrace(System.out); - assertEquals("Incorrect exception", IllegalStateException.class, cause.getClass()); - System.out.println("QPID-2345: Session became closed and we got that error rather than the authentication error."); - } - else - { - assertEquals("Incorrect exception", AMQAuthenticationException.class, cause.getClass()); - assertEquals("Incorrect error code thrown", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode()); - } - - //use the latch to ensure the control thread waits long enough for the exception thread - //to have done enough to mark the connection closed before teardown commences - assertTrue("Timed out waiting for conneciton to report close", - exceptionReceived.await(2, TimeUnit.SECONDS)); + check403Exception(e.getLinkedException()); } } - - // Connection Listener Interface - Used here to block failover - - public void bytesSent(long count) - { - } - - public void bytesReceived(long count) - { - } - - public boolean preFailover(boolean redirect) - { - //Prevent failover. - return false; - } - - public boolean preResubscribe() - { - return false; - } - - public void failoverComplete() - { - } } diff --git a/java/systests/src/main/java/org/apache/qpid/server/security/firewall/FirewallConfigTest.java b/java/systests/src/main/java/org/apache/qpid/server/security/firewall/FirewallConfigTest.java index ec9a297047..874a0c37e2 100644 --- a/java/systests/src/main/java/org/apache/qpid/server/security/firewall/FirewallConfigTest.java +++ b/java/systests/src/main/java/org/apache/qpid/server/security/firewall/FirewallConfigTest.java @@ -1,6 +1,4 @@ -package org.apache.qpid.server.security.firewall; /* - * * 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 @@ -17,9 +15,8 @@ package org.apache.qpid.server.security.firewall; * 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.firewall; import java.io.File; import java.io.FileWriter; @@ -85,7 +82,7 @@ public class FirewallConfigTest extends QpidTestCase } out.close(); } - + public void testVhostAllowBrokerDeny() throws Exception { if (_broker.equals(VM)) @@ -104,8 +101,7 @@ public class FirewallConfigTest extends QpidTestCase { //Try to get a connection to the 'test2' vhost //This is expected to fail as it is denied at the broker level - conn = getConnection(new AMQConnectionURL( - "amqp://username:password@clientid/test2?brokerlist='" + getBroker() + "'")); + conn = getConnection(new AMQConnectionURL("amqp://username:password@clientid/test2?brokerlist='" + getBroker() + "'")); fail("We expected the connection to fail"); } catch (JMSException e) @@ -235,7 +231,7 @@ public class FirewallConfigTest extends QpidTestCase } ); } - + public void testDenyOnReloadInVhost() throws Exception { testDeny(true, new Runnable() { @@ -259,7 +255,7 @@ public class FirewallConfigTest extends QpidTestCase { testFirewall(true, inVhost, restartOrReload); } - + /* * Check we can get a connection */ @@ -278,7 +274,7 @@ public class FirewallConfigTest extends QpidTestCase return conn != null; } - + private void testFirewall(boolean initial, boolean inVhost, Runnable restartOrReload) throws Exception { if (_broker.equals(VM)) diff --git a/java/systests/src/main/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java b/java/systests/src/main/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java index b0484e20d7..a6b7e20a42 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java +++ b/java/systests/src/main/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java @@ -147,13 +147,13 @@ public class TopicSessionTest extends QpidTestCase public void testUnsubscriptionAfterConnectionClose() throws Exception { - AMQConnection con1 = (AMQConnection) getConnection("guest", "guest", "clientid"); + AMQConnection con1 = (AMQConnection) getClientConnection("guest", "guest", "clientid"); AMQTopic topic = new AMQTopic(con1, "MyTopic3"); TopicSession session1 = con1.createTopicSession(true, AMQSession.AUTO_ACKNOWLEDGE); TopicPublisher publisher = session1.createPublisher(topic); - AMQConnection con2 = (AMQConnection) getConnection("guest", "guest", "clientid"); + AMQConnection con2 = (AMQConnection) getClientConnection("guest", "guest", "clientid"); TopicSession session2 = con2.createTopicSession(true, AMQSession.AUTO_ACKNOWLEDGE); TopicSubscriber sub = session2.createDurableSubscriber(topic, "subscription0"); @@ -167,7 +167,7 @@ public class TopicSessionTest extends QpidTestCase con2.close(); publisher.publish(session1.createTextMessage("Hello2")); session1.commit(); - con2 = (AMQConnection) getConnection("guest", "guest", "clientid"); + con2 = (AMQConnection) getClientConnection("guest", "guest", "clientid"); session2 = con2.createTopicSession(true, AMQSession.NO_ACKNOWLEDGE); sub = session2.createDurableSubscriber(topic, "subscription0"); con2.start(); @@ -367,7 +367,7 @@ public class TopicSessionTest extends QpidTestCase m = (TextMessage) noLocal.receive(100); assertNull(m); - AMQConnection con2 = (AMQConnection) getConnection("guest", "guest", "foo"); + AMQConnection con2 = (AMQConnection) getClientConnection("guest", "guest", "foo"); TopicSession session2 = con2.createTopicSession(true, AMQSession.AUTO_ACKNOWLEDGE); TopicPublisher publisher2 = session2.createPublisher(topic); diff --git a/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java b/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java index 9b786a5c62..18e373b8d7 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java +++ b/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java @@ -20,23 +20,28 @@ */ package org.apache.qpid.test.utils; -import org.apache.commons.configuration.ConfigurationException; -import org.apache.qpid.commands.objects.AllObjects; -import org.apache.qpid.management.common.JMXConnnectionFactory; -import org.apache.qpid.management.common.mbeans.ManagedBroker; -import org.apache.qpid.management.common.mbeans.ManagedExchange; +import java.io.IOException; +import java.util.Set; import javax.management.JMException; import javax.management.MBeanException; import javax.management.MBeanServerConnection; import javax.management.MBeanServerInvocationHandler; import javax.management.ObjectName; +import javax.management.MalformedObjectNameException; import javax.management.remote.JMXConnector; -import java.io.IOException; -import java.util.Set; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.commands.objects.AllObjects; +import org.apache.qpid.management.common.JMXConnnectionFactory; +import org.apache.qpid.management.common.mbeans.ManagedBroker; +import org.apache.qpid.management.common.mbeans.ManagedExchange; +import org.apache.qpid.management.common.mbeans.LoggingManagement; +import org.apache.qpid.management.common.mbeans.ConfigurationManagement; +import org.apache.qpid.management.common.mbeans.UserManagement; /** - * + * JMX access for tests. */ public class JMXTestUtils { @@ -61,9 +66,8 @@ public class JMXTestUtils public void open() throws Exception { - _jmxc = JMXConnnectionFactory.getJMXConnection( - 5000, "127.0.0.1", - _test.getManagementPort(_test.getPort()), USER, PASSWORD); + _jmxc = JMXConnnectionFactory.getJMXConnection(5000, "127.0.0.1", + _test.getManagementPort(_test.getPort()), USER, PASSWORD); _mbsc = _jmxc.getMBeanServerConnection(); } @@ -77,12 +81,11 @@ public class JMXTestUtils } /** - * Create a non-durable test exchange with the current test name + * Create a non-durable exchange with the requested name * - * @throws javax.management.JMException - is thrown if a exchange with this testName already exists - * @throws java.io.IOException - if there is a problem with the JMX Connection - * @throws javax.management.MBeanException - * - if there is another problem creating the exchange + * @throws JMException if a exchange with this name already exists + * @throws IOException if there is a problem with the JMX Connection + * @throws MBeanException if there is another problem creating the exchange */ public void createExchange(String virtualHostName, String name, String type, boolean durable) throws JMException, IOException, MBeanException @@ -96,24 +99,134 @@ public class JMXTestUtils * Create a non-durable queue (with no owner) that is named after the * creating test. * - * @throws JMException - is thrown if a queue with this testName already exists - * @throws IOException - if there is a problem with the JMX Connection + * @throws JMException if a queue with this name already exists + * @throws IOException if there is a problem with the JMX Connection + * @throws MBeanException if there is another problem creating the exchange */ public void createQueue(String virtualHostName, String name, String owner, boolean durable) - throws JMException, IOException + throws JMException, MBeanException, IOException { ManagedBroker managedBroker = getManagedBroker(virtualHostName); managedBroker.createNewQueue(name, owner, durable); } + + /** + * Unregisters all the channels, queuebindings etc and unregisters + * this exchange from managed objects. + * + * @throws JMException if an exchange with this name does not exist + * @throws IOException if there is a problem with the JMX Connection + * @throws MBeanException if there is another problem creating the exchange + */ + public void unregisterExchange(String virtualHostName, String exchange) + throws IOException, JMException, MBeanException + { + ManagedBroker managedBroker = getManagedBroker(virtualHostName); + + managedBroker.unregisterExchange(exchange); + } + + /** + * Unregisters the Queue bindings, removes the subscriptions and unregisters + * from the managed objects. + * + * @throws JMException if a queue with this name does not exist + * @throws IOException if there is a problem with the JMX Connection + * @throws MBeanException if there is another problem creating the exchange + */ + public void deleteQueue(String virtualHostName, String queueName) + throws IOException, JMException, MBeanException + { + ManagedBroker managedBroker = getManagedBroker(virtualHostName); + managedBroker.deleteQueue(queueName); + } + + /** + * Sets the logging level. + * + * @throws JMException + * @throws IOException if there is a problem with the JMX Connection + * @throws MBeanException + */ + public void setRuntimeLoggerLevel(String logger, String level) + throws IOException, JMException, MBeanException + { + LoggingManagement loggingManagement = getLoggingManagement(); + + loggingManagement.setRuntimeLoggerLevel(logger, level); + } + + /** + * Reload logging config file. + * + * @throws JMException + * @throws IOException if there is a problem with the JMX Connection + * @throws MBeanException + */ + public void reloadConfigFile() + throws IOException, JMException, MBeanException + { + LoggingManagement loggingManagement = getLoggingManagement(); + + loggingManagement.reloadConfigFile(); + } + + /** + * Get list of available logger levels. + * + * @throws JMException + * @throws IOException if there is a problem with the JMX Connection + * @throws MBeanException + */ + public String[] getAvailableLoggerLevels() + throws IOException, JMException, MBeanException + { + LoggingManagement loggingManagement = getLoggingManagement(); + + return loggingManagement.getAvailableLoggerLevels(); + } + + /** + * Set root logger level. + * + * @throws JMException + * @throws IOException if there is a problem with the JMX Connection + * @throws MBeanException + */ + public void setRuntimeRootLoggerLevel(String level) + throws IOException, JMException, MBeanException + { + LoggingManagement loggingManagement = getLoggingManagement(); + + loggingManagement.setRuntimeRootLoggerLevel(level); + } + + /** + * Get root logger level. + * + * @throws JMException + * @throws IOException if there is a problem with the JMX Connection + * @throws MBeanException + */ + public String getRuntimeRootLoggerLevel() + throws IOException, JMException, MBeanException + { + LoggingManagement loggingManagement = getLoggingManagement(); + + return loggingManagement.getRuntimeRootLoggerLevel(); + } + /** - * Retrive the ObjectName for the test Virtualhost. + * Retrive the ObjectName for a Virtualhost. * - * This is then use to create aproxy to the ManagedBroker MBean. + * This is then used to create a proxy to the ManagedBroker MBean. * - * @return the ObjectName for the 'test' VirtualHost. + * @param virtualHostName the VirtualHost to retrieve + * @return the ObjectName for the VirtualHost */ + @SuppressWarnings("static-access") public ObjectName getVirtualHostManagerObjectName(String vhostName) { // Get the name of the test manager @@ -126,18 +239,21 @@ public class JMXTestUtils _test.assertEquals("Incorrect number test vhosts returned", 1, objectNames.size()); // We have verified we have only one value in objectNames so return it - return objectNames.iterator().next(); + ObjectName objectName = objectNames.iterator().next(); + _test.getLogger().info("Loading: " + objectName); + return objectName; } /** - * Retrive the ObjectName for the given Exchange on the test Virtualhost. + * Retrive the ObjectName for the given Queue on a Virtualhost. * - * This is then use to create aproxy to the ManagedExchange MBean. + * This is then used to create a proxy to the ManagedQueue MBean. * - * @param queue The exchange to retireve e.g. 'direct' - * - * @return the ObjectName for the given exchange on the test VirtualHost. + * @param virtualHostName the VirtualHost the Queue is on + * @param queue The Queue to retireve + * @return the ObjectName for the given queue on the VirtualHost */ + @SuppressWarnings("static-access") public ObjectName getQueueObjectName(String virtualHostName, String queue) { // Get the name of the test manager @@ -151,19 +267,21 @@ public class JMXTestUtils "' returned", 1, objectNames.size()); // We have verified we have only one value in objectNames so return it - return objectNames.iterator().next(); + ObjectName objectName = objectNames.iterator().next(); + _test.getLogger().info("Loading: " + objectName); + return objectName; } /** - * Retrive the ObjectName for the given Exchange on the test Virtualhost. + * Retrive the ObjectName for the given Exchange on a VirtualHost. * - * This is then use to create aproxy to the ManagedExchange MBean. + * This is then used to create a proxy to the ManagedExchange MBean. * - * @param virtualHostName - * @param exchange The exchange to retireve e.g. 'direct' - * - * @return the ObjectName for the given exchange on the test VirtualHost. + * @param virtualHostName the VirtualHost the Exchange is on + * @param exchange the Exchange to retireve e.g. 'direct' + * @return the ObjectName for the given Exchange on the VirtualHost */ + @SuppressWarnings("static-access") public ObjectName getExchangeObjectName(String virtualHostName, String exchange) { // Get the name of the test manager @@ -173,13 +291,15 @@ public class JMXTestUtils Set<ObjectName> objectNames = allObject.returnObjects(); _test.assertNotNull("Null ObjectName Set returned", objectNames); - _test.assertEquals("Incorrect number of exchange with name '" + exchange + - "' returned", 1, objectNames.size()); + _test.assertEquals("Incorrect number of exchange with name '" + exchange + "' returned", 1, objectNames.size()); // We have verified we have only one value in objectNames so return it - return objectNames.iterator().next(); + ObjectName objectName = objectNames.iterator().next(); + _test.getLogger().info("Loading: " + objectName); + return objectName; } + @SuppressWarnings("static-access") public <T> T getManagedObject(Class<T> managedClass, String queryString) { AllObjects allObject = new AllObjects(_mbsc); @@ -191,25 +311,41 @@ public class JMXTestUtils _test.assertEquals("More than one " + managedClass + " returned", 1, objectNames.size()); ObjectName objectName = objectNames.iterator().next(); - + _test.getLogger().info("Loading: " + objectName); return getManagedObject(managedClass, objectName); } public <T> T getManagedObject(Class<T> managedClass, ObjectName objectName) { - return MBeanServerInvocationHandler. - newProxyInstance(_mbsc, objectName, managedClass, false); + return MBeanServerInvocationHandler.newProxyInstance(_mbsc, objectName, managedClass, false); } public ManagedBroker getManagedBroker(String virtualHost) { - return getManagedObject(ManagedBroker.class, getVirtualHostManagerObjectName(virtualHost).toString()); + return getManagedObject(ManagedBroker.class, getVirtualHostManagerObjectName(virtualHost)); } - + public ManagedExchange getManagedExchange(String exchangeName) { - return MBeanServerInvocationHandler. - newProxyInstance(_mbsc, getExchangeObjectName("test", exchangeName), - ManagedExchange.class, false); + ObjectName objectName = getExchangeObjectName("test", exchangeName); + return MBeanServerInvocationHandler.newProxyInstance(_mbsc, objectName, ManagedExchange.class, false); + } + + public LoggingManagement getLoggingManagement() throws MalformedObjectNameException + { + ObjectName objectName = new ObjectName("org.apache.qpid:type=LoggingManagement,name=LoggingManagement"); + return getManagedObject(LoggingManagement.class, objectName); + } + + public ConfigurationManagement getConfigurationManagement() throws MalformedObjectNameException + { + ObjectName objectName = new ObjectName("org.apache.qpid:type=ConfigurationManagement,name=ConfigurationManagement"); + return getManagedObject(ConfigurationManagement.class, objectName); + } + + public UserManagement getUserManagement() throws MalformedObjectNameException + { + ObjectName objectName = new ObjectName("org.apache.qpid:type=UserManagement,name=UserManagement"); + return getManagedObject(UserManagement.class, objectName); } } diff --git a/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java b/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java index 5b7670eaa3..57a7659782 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java +++ b/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java @@ -56,6 +56,7 @@ import junit.framework.TestResult; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.XMLConfiguration; import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; import org.apache.log4j.Level; import org.apache.qpid.AMQException; import org.apache.qpid.client.AMQConnection; @@ -71,26 +72,23 @@ import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; import org.apache.qpid.server.store.DerbyMessageStore; import org.apache.qpid.url.URLSyntaxException; import org.apache.qpid.util.LogMonitor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** - * - * + * Qpid base class for system testing test cases. */ public class QpidTestCase extends TestCase { protected final String QpidHome = System.getProperty("QPID_HOME"); protected File _configFile = new File(System.getProperty("broker.config")); - protected static final Logger _logger = LoggerFactory.getLogger(QpidTestCase.class); + protected static final Logger _logger = Logger.getLogger(QpidTestCase.class); protected static final int LOGMONITOR_TIMEOUT = 5000; protected long RECEIVE_TIMEOUT = 1000l; private Map<String, String> _propertiesSetForTestOnly = new HashMap<String, String>(); private Map<String, String> _propertiesSetForBroker = new HashMap<String, String>(); - private Map<org.apache.log4j.Logger, Level> _loggerLevelSetForTest = new HashMap<org.apache.log4j.Logger, Level>(); + private Map<Logger, Level> _loggerLevelSetForTest = new HashMap<Logger, Level>(); private XMLConfiguration _testConfiguration = new XMLConfiguration(); private XMLConfiguration _testVirtualhosts = new XMLConfiguration(); @@ -98,7 +96,6 @@ public class QpidTestCase extends TestCase protected static final String INDEX = "index"; protected static final String CONTENT = "content"; - /** * Some tests are excluded when the property test.excludes is set to true. * An exclusion list is either a file (prop test.excludesfile) which contains one test name @@ -198,10 +195,10 @@ public class QpidTestCase extends TestCase private String _brokerClean = System.getProperty(BROKER_CLEAN, null); private Boolean _brokerCleanBetweenTests = Boolean.getBoolean(BROKER_CLEAN_BETWEEN_TESTS); private String _brokerVersion = System.getProperty(BROKER_VERSION, VERSION_08); - private String _output = System.getProperty(TEST_OUTPUT); + protected String _output = System.getProperty(TEST_OUTPUT); protected Boolean _brokerPersistent = Boolean.getBoolean(BROKER_PERSITENT); - private static String _brokerLogPrefix = System.getProperty(BROKER_LOG_PREFIX,"BROKER: "); + protected static String _brokerLogPrefix = System.getProperty(BROKER_LOG_PREFIX,"BROKER: "); protected static boolean _interleaveBrokerLog = Boolean.getBoolean(BROKER_LOG_INTERLEAVE); protected File _outputFile; @@ -210,10 +207,10 @@ public class QpidTestCase extends TestCase protected Map<Integer, Process> _brokers = new HashMap<Integer, Process>(); - private InitialContext _initialContext; + protected InitialContext _initialContext; protected AMQConnectionFactory _connectionFactory; - private String _testName; + protected String _testName; // the connections created for a given test protected List<Connection> _connections = new ArrayList<Connection>(); @@ -248,6 +245,11 @@ public class QpidTestCase extends TestCase { this("QpidTestCase"); } + + public Logger getLogger() + { + return QpidTestCase._logger; + } public void runBare() throws Throwable { @@ -287,6 +289,11 @@ public class QpidTestCase extends TestCase { super.runBare(); } + catch (Exception e) + { + _logger.error("exception", e); + throw e; + } finally { try @@ -935,7 +942,7 @@ public class QpidTestCase extends TestCase * @param logger the logger to change * @param level the level to set */ - protected void setLoggerLevel(org.apache.log4j.Logger logger, Level level) + protected void setLoggerLevel(Logger logger, Level level) { assertNotNull("Cannot set level of null logger", logger); assertNotNull("Cannot set Logger("+logger.getName()+") to null level.",level); @@ -954,7 +961,7 @@ public class QpidTestCase extends TestCase */ protected void revertLoggingLevels() { - for (org.apache.log4j.Logger logger : _loggerLevelSetForTest.keySet()) + for (Logger logger : _loggerLevelSetForTest.keySet()) { logger.setLevel(_loggerLevelSetForTest.get(logger)); } @@ -1079,7 +1086,8 @@ public class QpidTestCase extends TestCase public Connection getConnection(ConnectionURL url) throws JMSException { - Connection connection = new AMQConnectionFactory(url).createConnection("guest", "guest"); + _logger.info(url.getURL()); + Connection connection = new AMQConnectionFactory(url).createConnection(url.getUsername(), url.getPassword()); _connections.add(connection); @@ -1098,14 +1106,14 @@ public class QpidTestCase extends TestCase */ public Connection getConnection(String username, String password) throws JMSException, NamingException { - _logger.info("get Connection"); + _logger.info("get connection"); Connection con = getConnectionFactory().createConnection(username, password); //add the connection in the lis of connections _connections.add(con); return con; } - public Connection getConnection(String username, String password, String id) throws JMSException, URLSyntaxException, AMQException, NamingException + public Connection getClientConnection(String username, String password, String id) throws JMSException, URLSyntaxException, AMQException, NamingException { _logger.info("get Connection"); Connection con; @@ -1382,10 +1390,19 @@ public class QpidTestCase extends TestCase /* * Sigh, this is going to get messy. grep for BRKR and the port number */ - - Process p = Runtime.getRuntime().exec("/usr/bin/pgrep -f " + getPort(port)); + String osName = System.getProperty("os.name"); + boolean osx = osName.equals("Mac OS X"); + + String cmd = osx ? "/usr/sbin/lsof -i TCP:%d -Fp" : "/usr/bin/pgrep -f %d"; + Process p = Runtime.getRuntime().exec(String.format(cmd, getPort(port))); BufferedReader reader = new BufferedReader (new InputStreamReader(p.getInputStream())); - String cmd = "/bin/kill -SIGHUP " + reader.readLine(); + String pid = reader.readLine(); + while (reader.ready()) + { + pid = reader.readLine(); + } + + cmd = "/bin/kill -SIGHUP " + (osx ? pid.substring(1) : pid); p = Runtime.getRuntime().exec(cmd); LogMonitor _monitor = new LogMonitor(_outputFile); diff --git a/java/test-profiles/CPPExcludes b/java/test-profiles/CPPExcludes index c2983d8675..255c52c94d 100755 --- a/java/test-profiles/CPPExcludes +++ b/java/test-profiles/CPPExcludes @@ -1,9 +1,14 @@ org.apache.qpid.test.unit.client.channelclose.ChannelCloseTest#* org.apache.qpid.client.ResetMessageListenerTest#* +//These tests will not work until 0-10 exception halding is fixed +org.apache.qpid.server.security.acl.* + //These tests are for the java broker -org.apache.qpid.server.security.acl.SimpleACLTest#* +org.apache.qpid.server.security.access.plugins.* +org.apache.qpid.server.security.access.* org.apache.qpid.server.security.firewall.FirewallConfigTest#* +org.apache.qpid.server.security.firewall.FirewallConfigurationTest#* org.apache.qpid.server.plugins.PluginTest#* org.apache.qpid.server.BrokerStartupTest#* diff --git a/java/test-profiles/JavaInVMExcludes b/java/test-profiles/JavaInVMExcludes index 8d36ed6773..b81df15f1a 100644 --- a/java/test-profiles/JavaInVMExcludes +++ b/java/test-profiles/JavaInVMExcludes @@ -16,3 +16,6 @@ org.apache.qpid.test.unit.ack.RecoverTest#testAcknowledgePerConsumer // related to QPID-2471. These are new test cases and fail with the Java broker. org.apache.qpid.test.unit.ack.RecoverTest#testOderingWithAsyncConsumer org.apache.qpid.test.unit.ack.RecoverTest#testOderingWithSyncConsumer + +//The VM broker does not export the logging management JMX MBean +org.apache.qpid.server.security.acl.ExternalAdminACLTest#* |