diff options
| author | Alex Rudyy <orudyy@apache.org> | 2013-05-04 22:17:08 +0000 |
|---|---|---|
| committer | Alex Rudyy <orudyy@apache.org> | 2013-05-04 22:17:08 +0000 |
| commit | 8c52b5056d0e70491ca2629843c88ee2ba5bf63f (patch) | |
| tree | 38c5c07aa043f57aef10d586be227caf56cbfea5 /java | |
| parent | f93a0693e031a20948e0647d48b28e8895c1d26e (diff) | |
| download | qpid-python-8c52b5056d0e70491ca2629843c88ee2ba5bf63f.tar.gz | |
QPID-4813: Protect operations to update queue and exchange attributes with ACL
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk/qpid@1479200 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'java')
11 files changed, 643 insertions, 4 deletions
diff --git a/java/broker/etc/broker_example.acl b/java/broker/etc/broker_example.acl index bb85ef5e57..6cab707a89 100644 --- a/java/broker/etc/broker_example.acl +++ b/java/broker/etc/broker_example.acl @@ -57,6 +57,7 @@ ACL ALLOW webadmins ACCESS MANAGEMENT # All rules below are required for console admin users # to perform create/update/delete operations ACL ALLOW-LOG webadmins CREATE QUEUE +ACL ALLOW-LOG webadmins UPDATE QUEUE ACL ALLOW-LOG webadmins DELETE QUEUE ACL ALLOW-LOG webadmins PURGE QUEUE ACL ALLOW-LOG webadmins CREATE EXCHANGE diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BindingAdapter.java b/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BindingAdapter.java index eb2d0dd7e2..39e979174a 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BindingAdapter.java +++ b/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BindingAdapter.java @@ -222,4 +222,19 @@ final class BindingAdapter extends AbstractAdapter implements Binding } return false; } + + @Override + public Object setAttribute(final String name, final Object expected, final Object desired) throws IllegalStateException, + AccessControlException, IllegalArgumentException + { + throw new UnsupportedOperationException("Changing attributes on binding is not supported."); + } + + @Override + public void setAttributes(final Map<String, Object> attributes) throws IllegalStateException, AccessControlException, + IllegalArgumentException + { + throw new UnsupportedOperationException("Changing attributes on binding is not supported."); + } + } diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/adapter/ExchangeAdapter.java b/java/broker/src/main/java/org/apache/qpid/server/model/adapter/ExchangeAdapter.java index 5d5f3f7378..a081f03f09 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/model/adapter/ExchangeAdapter.java +++ b/java/broker/src/main/java/org/apache/qpid/server/model/adapter/ExchangeAdapter.java @@ -384,6 +384,30 @@ final class ExchangeAdapter extends AbstractAdapter implements Exchange, org.apa return false; } + @Override + protected void changeAttributes(Map<String, Object> attributes) + { + throw new UnsupportedOperationException("Changing attributes on exchange is not supported."); + } + + @Override + protected void authoriseSetAttribute(String name, Object expected, Object desired) throws AccessControlException + { + if (!_vhost.getSecurityManager().authoriseUpdate(_exchange)) + { + throw new AccessControlException("Setting of exchange attribute is denied"); + } + } + + @Override + protected void authoriseSetAttributes(Map<String, Object> attributes) throws AccessControlException + { + if (!_vhost.getSecurityManager().authoriseUpdate(_exchange)) + { + throw new AccessControlException("Setting of exchange attributes is denied"); + } + } + private class ExchangeStatistics implements Statistics { diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/adapter/QueueAdapter.java b/java/broker/src/main/java/org/apache/qpid/server/model/adapter/QueueAdapter.java index 8ac869900c..0916c4e730 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/model/adapter/QueueAdapter.java +++ b/java/broker/src/main/java/org/apache/qpid/server/model/adapter/QueueAdapter.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server.model.adapter; +import java.lang.reflect.Type; import java.security.AccessControlException; import java.util.ArrayList; import java.util.Collection; @@ -31,16 +32,15 @@ import java.util.Map; import org.apache.qpid.AMQException; import org.apache.qpid.AMQStoreException; import org.apache.qpid.server.binding.Binding; +import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.model.ConfiguredObjectFinder; import org.apache.qpid.server.model.Consumer; -import org.apache.qpid.server.model.Connection; import org.apache.qpid.server.model.Exchange; import org.apache.qpid.server.model.IllegalStateTransitionException; import org.apache.qpid.server.model.LifetimePolicy; import org.apache.qpid.server.model.Queue; import org.apache.qpid.server.model.QueueNotificationListener; -import org.apache.qpid.server.model.Session; import org.apache.qpid.server.model.State; import org.apache.qpid.server.model.Statistics; import org.apache.qpid.server.protocol.AMQConnectionModel; @@ -51,6 +51,19 @@ import org.apache.qpid.server.util.MapValueConverter; final class QueueAdapter extends AbstractAdapter implements Queue, AMQQueue.SubscriptionRegistrationListener, AMQQueue.NotificationListener { + @SuppressWarnings("serial") + static final Map<String, Type> ATTRIBUTE_TYPES = Collections.unmodifiableMap(new HashMap<String, Type>(){{ + put(ALERT_REPEAT_GAP, Long.class); + put(ALERT_THRESHOLD_MESSAGE_AGE, Long.class); + put(ALERT_THRESHOLD_MESSAGE_SIZE, Long.class); + put(ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, Long.class); + put(ALERT_THRESHOLD_QUEUE_DEPTH_BYTES, Long.class); + put(QUEUE_FLOW_CONTROL_SIZE_BYTES, Long.class); + put(QUEUE_FLOW_RESUME_SIZE_BYTES, Long.class); + put(MAXIMUM_DELIVERY_ATTEMPTS, Integer.class); + put(EXCLUSIVE, Boolean.class); + put(DESCRIPTION, String.class); + }}); static final Map<String, String> ATTRIBUTE_MAPPINGS = new HashMap<String, String>(); static @@ -775,4 +788,61 @@ final class QueueAdapter extends AbstractAdapter implements Queue, AMQQueue.Subs return false; } + @Override + protected void authoriseSetAttribute(String name, Object expected, Object desired) throws AccessControlException + { + if (!_vhost.getSecurityManager().authoriseUpdate(_queue)) + { + throw new AccessControlException("Setting of queue attribute is denied"); + } + } + + @Override + protected void authoriseSetAttributes(Map<String, Object> attributes) throws AccessControlException + { + if (!_vhost.getSecurityManager().authoriseUpdate(_queue)) + { + throw new AccessControlException("Setting of queue attributes is denied"); + } + } + + @Override + protected void changeAttributes(final Map<String, Object> attributes) + { + Map<String, Object> convertedAttributes = MapValueConverter.convert(attributes, ATTRIBUTE_TYPES); + validateAttributes(convertedAttributes); + + super.changeAttributes(convertedAttributes); + } + + private void validateAttributes(Map<String, Object> convertedAttributes) + { + Long queueFlowControlSize = (Long) convertedAttributes.get(QUEUE_FLOW_CONTROL_SIZE_BYTES); + Long queueFlowControlResumeSize = (Long) convertedAttributes.get(QUEUE_FLOW_RESUME_SIZE_BYTES); + if (queueFlowControlSize != null || queueFlowControlResumeSize != null ) + { + if (queueFlowControlSize == null) + { + queueFlowControlSize = (Long)getAttribute(QUEUE_FLOW_CONTROL_SIZE_BYTES); + } + if (queueFlowControlResumeSize == null) + { + queueFlowControlResumeSize = (Long)getAttribute(QUEUE_FLOW_RESUME_SIZE_BYTES); + } + if (queueFlowControlResumeSize > queueFlowControlSize) + { + throw new IllegalConfigurationException("Flow resume size can't be greater than flow control size"); + } + } + for (Map.Entry<String, Object> entry: convertedAttributes.entrySet()) + { + Object value = entry.getValue(); + if (value instanceof Number && ((Number)value).longValue() < 0) + { + throw new IllegalConfigurationException("Only positive integer value can be specified for the attribute " + + entry.getKey()); + } + } + } + } 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 index f0570943cf..09e79d3ae9 100755 --- 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 @@ -52,6 +52,7 @@ import static org.apache.qpid.server.security.access.Operation.DELETE; import static org.apache.qpid.server.security.access.Operation.PUBLISH; import static org.apache.qpid.server.security.access.Operation.PURGE; import static org.apache.qpid.server.security.access.Operation.UNBIND; +import static org.apache.qpid.server.security.access.Operation.UPDATE; import javax.security.auth.Subject; import java.net.SocketAddress; @@ -386,6 +387,30 @@ public class SecurityManager implements ConfigurationChangeListener }); } + + public boolean authoriseUpdate(final AMQQueue queue) + { + return checkAllPlugins(new AccessCheck() + { + Result allowed(AccessControl plugin) + { + return plugin.authorise(UPDATE, QUEUE, new ObjectProperties(queue)); + } + }); + } + + + public boolean authoriseUpdate(final Exchange exchange) + { + return checkAllPlugins(new AccessCheck() + { + Result allowed(AccessControl plugin) + { + return plugin.authorise(UPDATE, EXCHANGE, new ObjectProperties(exchange.getName())); + } + }); + } + public boolean authoriseDelete(final Exchange exchange) { return checkAllPlugins(new AccessCheck() 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 index e93f487bdb..048d9a8fc9 100644 --- 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 @@ -43,8 +43,8 @@ public enum ObjectType ALL(Operation.ALL), VIRTUALHOST(Operation.ALL, ACCESS), MANAGEMENT(Operation.ALL, ACCESS), - QUEUE(Operation.ALL, CREATE, DELETE, PURGE, CONSUME), - EXCHANGE(Operation.ALL, ACCESS, CREATE, DELETE, BIND, UNBIND, PUBLISH), + QUEUE(Operation.ALL, CREATE, DELETE, PURGE, CONSUME, UPDATE), + EXCHANGE(Operation.ALL, ACCESS, CREATE, DELETE, BIND, UNBIND, PUBLISH, UPDATE), LINK, // Not allowed in the Java broker ROUTE, // Not allowed in the Java broker METHOD(Operation.ALL, ACCESS, UPDATE), diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/BindingRestTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/BindingRestTest.java index 372db8f560..9a57429062 100644 --- a/java/systests/src/main/java/org/apache/qpid/systest/rest/BindingRestTest.java +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/BindingRestTest.java @@ -127,4 +127,25 @@ public class BindingRestTest extends QpidRestTestCase Asserts.assertBinding(bindingName, "queue", "amq.direct", binding); } + public void testSetBindingAttributesUnsupported() throws Exception + { + String bindingName = getTestName(); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Binding.NAME, bindingName); + attributes.put(Binding.QUEUE, "queue"); + attributes.put(Binding.EXCHANGE, "amq.direct"); + + int responseCode = getRestTestHelper().submitRequest("/rest/binding/test/amq.direct/queue/" + bindingName, "PUT", attributes); + assertEquals("Unexpected response code", 201, responseCode); + + Map<String, Object> binding = getRestTestHelper().getJsonAsSingletonList("/rest/binding/test/amq.direct/queue/" + bindingName); + + Asserts.assertBinding(bindingName, "queue", "amq.direct", binding); + + attributes.put(Binding.ID, binding.get(Binding.ID)); + attributes.put(Binding.ARGUMENTS, null); + + responseCode = getRestTestHelper().submitRequest("/rest/binding/test/amq.direct/queue/" + bindingName, "PUT", attributes); + assertEquals("Update should be unsupported", 409, responseCode); + } } diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/ExchangeRestTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/ExchangeRestTest.java index ec9791db13..1da1c6394e 100644 --- a/java/systests/src/main/java/org/apache/qpid/systest/rest/ExchangeRestTest.java +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/ExchangeRestTest.java @@ -21,6 +21,7 @@ package org.apache.qpid.systest.rest; import java.net.URLDecoder; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -62,6 +63,26 @@ public class ExchangeRestTest extends QpidRestTestCase } } + public void testSetExchangeAttributesUnsupported() throws Exception + { + String exchangeName = getTestName(); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Exchange.NAME, exchangeName); + attributes.put(Exchange.TYPE, "direct"); + int responseCode =getRestTestHelper().submitRequest("/rest/exchange/test/" + exchangeName, "PUT", attributes); + + Map<String, Object> exchange = getRestTestHelper().getJsonAsSingletonList("/rest/exchange/test/" + exchangeName); + assertNotNull("Exchange not found", exchange); + + attributes = new HashMap<String, Object>(); + attributes.put(Exchange.NAME, exchangeName); + attributes.put(Exchange.ALTERNATE_EXCHANGE, "my-alternate-exchange"); + + responseCode = getRestTestHelper().submitRequest("/rest/exchange/test/" + exchangeName, "PUT", attributes); + assertEquals("Exchange update should be unsupported", 409, responseCode); + } + private void assertExchange(String exchangeName, Map<String, Object> exchange) { assertNotNull("Exchange with name " + exchangeName + " is not found", exchange); diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/QueueRestTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/QueueRestTest.java index 1f441e7cbb..d6eae154cf 100644 --- a/java/systests/src/main/java/org/apache/qpid/systest/rest/QueueRestTest.java +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/QueueRestTest.java @@ -127,6 +127,44 @@ public class QueueRestTest extends QpidRestTestCase assertConsumer(consumers.get(0)); } + public void testUpdateQueue() throws Exception + { + String queueName = getTestName(); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Queue.NAME, queueName); + + int responseCode = getRestTestHelper().submitRequest("/rest/queue/test/" + queueName, "PUT", attributes); + + Map<String, Object> queueDetails = getRestTestHelper().getJsonAsSingletonList("/rest/queue/test/" + queueName); + Asserts.assertQueue(queueName, "standard", queueDetails); + + attributes = new HashMap<String, Object>(); + attributes.put(Queue.NAME, queueName); + attributes.put(Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES, 100000); + attributes.put(Queue.QUEUE_FLOW_RESUME_SIZE_BYTES, 80000); + attributes.put(Queue.ALERT_REPEAT_GAP, 10000); + attributes.put(Queue.ALERT_THRESHOLD_MESSAGE_AGE, 20000); + attributes.put(Queue.ALERT_THRESHOLD_MESSAGE_SIZE, 30000); + attributes.put(Queue.ALERT_THRESHOLD_QUEUE_DEPTH_BYTES, 40000); + attributes.put(Queue.ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, 50000); + attributes.put(Queue.MAXIMUM_DELIVERY_ATTEMPTS, 10); + attributes.put(Queue.EXCLUSIVE, true); + + responseCode = getRestTestHelper().submitRequest("/rest/queue/test/" + queueName, "PUT", attributes); + assertEquals("Setting of queue attribites should be allowed", 200, responseCode); + + Map<String, Object> queueData = getRestTestHelper().getJsonAsSingletonList("/rest/queue/test/" + queueName); + assertEquals("Unexpected " + Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES, 100000, queueData.get(Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES) ); + assertEquals("Unexpected " + Queue.QUEUE_FLOW_RESUME_SIZE_BYTES, 80000, queueData.get(Queue.QUEUE_FLOW_RESUME_SIZE_BYTES) ); + assertEquals("Unexpected " + Queue.ALERT_REPEAT_GAP, 10000, queueData.get(Queue.ALERT_REPEAT_GAP) ); + assertEquals("Unexpected " + Queue.ALERT_THRESHOLD_MESSAGE_AGE, 20000, queueData.get(Queue.ALERT_THRESHOLD_MESSAGE_AGE) ); + assertEquals("Unexpected " + Queue.ALERT_THRESHOLD_MESSAGE_SIZE, 30000, queueData.get(Queue.ALERT_THRESHOLD_MESSAGE_SIZE) ); + assertEquals("Unexpected " + Queue.ALERT_THRESHOLD_QUEUE_DEPTH_BYTES, 40000, queueData.get(Queue.ALERT_THRESHOLD_QUEUE_DEPTH_BYTES) ); + assertEquals("Unexpected " + Queue.ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, 50000, queueData.get(Queue.ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES) ); + assertEquals("Unexpected " + Queue.EXCLUSIVE, true, queueData.get(Queue.EXCLUSIVE) ); + } + public void testPutCreateBinding() throws Exception { String queueName = getTestQueueName(); diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/ExchangeRestACLTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/ExchangeRestACLTest.java new file mode 100644 index 0000000000..b39d994198 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/ExchangeRestACLTest.java @@ -0,0 +1,235 @@ +/* + * + * 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.systest.rest.acl; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.server.model.Binding; +import org.apache.qpid.server.model.Exchange; +import org.apache.qpid.server.security.acl.AbstractACLTestCase; +import org.apache.qpid.systest.rest.QpidRestTestCase; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.map.JsonMappingException; + +public class ExchangeRestACLTest extends QpidRestTestCase +{ + private static final String ALLOWED_USER = "user1"; + private static final String DENIED_USER = "user2"; + + @Override + protected void customizeConfiguration() throws ConfigurationException, IOException + { + super.customizeConfiguration(); + getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); + + AbstractACLTestCase.writeACLFileUtil(this, null, + "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + "ACL ALLOW-LOG " + ALLOWED_USER + " CREATE EXCHANGE", + "ACL DENY-LOG " + DENIED_USER + " CREATE EXCHANGE", + "ACL ALLOW-LOG " + ALLOWED_USER + " UPDATE EXCHANGE", + "ACL DENY-LOG " + DENIED_USER + " UPDATE EXCHANGE", + "ACL ALLOW-LOG " + ALLOWED_USER + " DELETE EXCHANGE", + "ACL DENY-LOG " + DENIED_USER + " DELETE EXCHANGE", + "ACL ALLOW-LOG " + ALLOWED_USER + " BIND EXCHANGE", + "ACL DENY-LOG " + DENIED_USER + " BIND EXCHANGE", + "ACL ALLOW-LOG " + ALLOWED_USER + " UNBIND EXCHANGE", + "ACL DENY-LOG " + DENIED_USER + " UNBIND EXCHANGE", + "ACL DENY-LOG ALL ALL"); + + getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, + HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED, true); + } + + public void testCreateExchangeAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String exchangeName = getTestName(); + + int responseCode = createExchange(exchangeName); + assertEquals("Exchange creation should be allowed", 201, responseCode); + + assertExchangeExists(exchangeName); + } + + public void testCreateExchangeDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + String exchangeName = getTestName(); + + int responseCode = createExchange(exchangeName); + assertEquals("Exchange creation should be denied", 403, responseCode); + + assertExchangeDoesNotExist(exchangeName); + } + + public void testDeleteExchangeAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String exchangeName = getTestName(); + + int responseCode = createExchange(exchangeName); + assertEquals("Exchange creation should be allowed", 201, responseCode); + + assertExchangeExists(exchangeName); + + responseCode = getRestTestHelper().submitRequest("/rest/exchange/test/" + exchangeName, "DELETE", null); + assertEquals("Exchange deletion should be allowed", 200, responseCode); + + assertExchangeDoesNotExist(TEST2_VIRTUALHOST); + } + + public void testDeleteExchangeDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String exchangeName = getTestName(); + + int responseCode = createExchange(exchangeName); + assertEquals("Exchange creation should be allowed", 201, responseCode); + + assertExchangeExists(exchangeName); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + responseCode = getRestTestHelper().submitRequest("/rest/exchange/test/" + exchangeName, "DELETE", null); + assertEquals("Exchange deletion should be denied", 403, responseCode); + + assertExchangeExists(exchangeName); + } + + public void testSetExchangeAttributesAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String exchangeName = getTestName(); + + int responseCode = createExchange(exchangeName); + + assertExchangeExists(exchangeName); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Exchange.NAME, exchangeName); + attributes.put(Exchange.ALTERNATE_EXCHANGE, "my-alternate-exchange"); + + responseCode = getRestTestHelper().submitRequest("/rest/exchange/test/" + exchangeName, "PUT", attributes); + assertEquals("Setting of exchange attribites should be allowed but it is currently unsupported", 409, responseCode); + } + + public void testSetExchangeAttributesDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String exchangeName = getTestName(); + + int responseCode = createExchange(exchangeName); + assertExchangeExists(exchangeName); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Exchange.NAME, exchangeName); + attributes.put(Exchange.ALTERNATE_EXCHANGE, "my-alternate-exchange"); + + responseCode = getRestTestHelper().submitRequest("/rest/exchange/test/" + exchangeName, "PUT", attributes); + assertEquals("Setting of exchange attribites should be allowed", 403, responseCode); + } + + public void testBindToExchangeAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String bindingName = getTestName(); + int responseCode = createBinding(bindingName); + assertEquals("Binding creation should be allowed", 201, responseCode); + + assertBindingExists(bindingName); + } + + public void testBindToExchangeDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + String bindingName = getTestName(); + int responseCode = createBinding(bindingName); + assertEquals("Binding creation should be denied", 403, responseCode); + + assertBindingDoesNotExist(bindingName); + } + + private int createExchange(String exchangeName) throws Exception + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Exchange.NAME, exchangeName); + attributes.put(Exchange.TYPE, "direct"); + return getRestTestHelper().submitRequest("/rest/exchange/test/" + exchangeName, "PUT", attributes); + } + + private void assertExchangeDoesNotExist(String exchangeName) throws Exception + { + assertExchangeExistence(exchangeName, false); + } + + private void assertExchangeExists(String exchangeName) throws Exception + { + assertExchangeExistence(exchangeName, true); + } + + private void assertExchangeExistence(String exchangeName, boolean exists) throws Exception + { + List<Map<String, Object>> exchanges = getRestTestHelper().getJsonAsList("/rest/exchange/test/" + exchangeName); + assertEquals("Unexpected result", exists, !exchanges.isEmpty()); + } + + private int createBinding(String bindingName) throws IOException, JsonGenerationException, JsonMappingException + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Binding.NAME, bindingName); + attributes.put(Binding.QUEUE, "queue"); + attributes.put(Binding.EXCHANGE, "amq.direct"); + + int responseCode = getRestTestHelper().submitRequest("/rest/binding/test/amq.direct/queue/" + bindingName, "PUT", attributes); + return responseCode; + } + + private void assertBindingDoesNotExist(String bindingName) throws Exception + { + assertBindingExistence(bindingName, false); + } + + private void assertBindingExists(String bindingName) throws Exception + { + assertBindingExistence(bindingName, true); + } + + private void assertBindingExistence(String bindingName, boolean exists) throws Exception + { + List<Map<String, Object>> bindings = getRestTestHelper().getJsonAsList("/rest/binding/test/amq.direct/queue/" + bindingName); + assertEquals("Unexpected result", exists, !bindings.isEmpty()); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/QueueRestACLTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/QueueRestACLTest.java new file mode 100644 index 0000000000..b187ca955a --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/QueueRestACLTest.java @@ -0,0 +1,189 @@ +/* + * + * 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.systest.rest.acl; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.server.model.Queue; +import org.apache.qpid.server.security.acl.AbstractACLTestCase; +import org.apache.qpid.systest.rest.QpidRestTestCase; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class QueueRestACLTest extends QpidRestTestCase +{ + private static final String ALLOWED_USER = "user1"; + private static final String DENIED_USER = "user2"; + + @Override + protected void customizeConfiguration() throws ConfigurationException, IOException + { + super.customizeConfiguration(); + getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); + + AbstractACLTestCase.writeACLFileUtil(this, null, + "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + "ACL ALLOW-LOG " + ALLOWED_USER + " CREATE QUEUE", + "ACL DENY-LOG " + DENIED_USER + " CREATE QUEUE", + "ACL ALLOW-LOG " + ALLOWED_USER + " UPDATE QUEUE", + "ACL DENY-LOG " + DENIED_USER + " UPDATE QUEUE", + "ACL ALLOW-LOG " + ALLOWED_USER + " DELETE QUEUE", + "ACL DENY-LOG " + DENIED_USER + " DELETE QUEUE", + "ACL DENY-LOG ALL ALL"); + + getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, + HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED, true); + } + + public void testCreateQueueAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String queueName = getTestName(); + + int responseCode = createQueue(queueName); + assertEquals("Queue creation should be allowed", 201, responseCode); + + assertQueueExists(queueName); + } + + public void testCreateQueueDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + String queueName = getTestName(); + + int responseCode = createQueue(queueName); + assertEquals("Queue creation should be denied", 403, responseCode); + + assertQueueDoesNotExist(queueName); + } + + public void testDeleteQueueAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String queueName = getTestName(); + + int responseCode = createQueue(queueName); + assertEquals("Queue creation should be allowed", 201, responseCode); + + assertQueueExists(queueName); + + responseCode = getRestTestHelper().submitRequest("/rest/queue/test/" + queueName, "DELETE", null); + assertEquals("Queue deletion should be allowed", 200, responseCode); + + assertQueueDoesNotExist(TEST2_VIRTUALHOST); + } + + public void testDeleteQueueDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String queueName = getTestName(); + + int responseCode = createQueue(queueName); + assertEquals("Queue creation should be allowed", 201, responseCode); + + assertQueueExists(queueName); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + responseCode = getRestTestHelper().submitRequest("/rest/queue/test/" + queueName, "DELETE", null); + assertEquals("Queue deletion should be denied", 403, responseCode); + + assertQueueExists(queueName); + } + + public void testSetQueueAttributesAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String queueName = getTestName(); + + int responseCode = createQueue(queueName); + + assertQueueExists(queueName); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Queue.NAME, queueName); + attributes.put(Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES, 100000); + attributes.put(Queue.QUEUE_FLOW_RESUME_SIZE_BYTES, 80000); + + responseCode = getRestTestHelper().submitRequest("/rest/queue/test/" + queueName, "PUT", attributes); + assertEquals("Setting of queue attribites should be allowed", 200, responseCode); + + Map<String, Object> queueData = getRestTestHelper().getJsonAsSingletonList("/rest/queue/test/" + queueName); + assertEquals("Unexpected " + Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES, 100000, queueData.get(Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES) ); + assertEquals("Unexpected " + Queue.QUEUE_FLOW_RESUME_SIZE_BYTES, 80000, queueData.get(Queue.QUEUE_FLOW_RESUME_SIZE_BYTES) ); + } + + public void testSetQueueAttributesDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String queueName = getTestName(); + + int responseCode = createQueue(queueName); + assertQueueExists(queueName); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Queue.NAME, queueName); + attributes.put(Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES, 100000); + attributes.put(Queue.QUEUE_FLOW_RESUME_SIZE_BYTES, 80000); + + responseCode = getRestTestHelper().submitRequest("/rest/queue/test/" + queueName, "PUT", attributes); + assertEquals("Setting of queue attribites should be allowed", 403, responseCode); + + Map<String, Object> queueData = getRestTestHelper().getJsonAsSingletonList("/rest/queue/test/" + queueName); + assertEquals("Unexpected " + Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES, 0, queueData.get(Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES) ); + assertEquals("Unexpected " + Queue.QUEUE_FLOW_RESUME_SIZE_BYTES, 0, queueData.get(Queue.QUEUE_FLOW_RESUME_SIZE_BYTES) ); + } + + private int createQueue(String queueName) throws Exception + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Queue.NAME, queueName); + + return getRestTestHelper().submitRequest("/rest/queue/test/" + queueName, "PUT", attributes); + } + + private void assertQueueDoesNotExist(String queueName) throws Exception + { + assertQueueExistence(queueName, false); + } + + private void assertQueueExists(String queueName) throws Exception + { + assertQueueExistence(queueName, true); + } + + private void assertQueueExistence(String queueName, boolean exists) throws Exception + { + List<Map<String, Object>> queues = getRestTestHelper().getJsonAsList("/rest/queue/test/" + queueName); + assertEquals("Unexpected result", exists, !queues.isEmpty()); + } +} |
