summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Godfrey <rgodfrey@apache.org>2013-04-22 10:36:01 +0000
committerRobert Godfrey <rgodfrey@apache.org>2013-04-22 10:36:01 +0000
commita51d443320d2406dd370db4fef1567b0e4b73c13 (patch)
tree86c7dad9458e422c9f8e4bbe3a3997f2fb30d888
parentba219af8c78c7f6c23590a7b9f3cbc73dd6fd0e0 (diff)
downloadqpid-python-a51d443320d2406dd370db4fef1567b0e4b73c13.tar.gz
QPID-4678 : merged to QPID-4659 branch
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/QPID-4659@1470438 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/PlainConfigurationTest.java7
-rw-r--r--qpid/java/broker/etc/broker_example.acl3
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java24
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java30
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java45
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java31
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java33
-rwxr-xr-xqpid/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java19
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectProperties.java5
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectType.java4
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Operation.java5
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/OperationLoggingDetails.java53
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/QpidRestTestCase.java6
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java455
14 files changed, 711 insertions, 9 deletions
diff --git a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/PlainConfigurationTest.java b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/PlainConfigurationTest.java
index cbfc9003c8..3a324bc571 100644
--- a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/PlainConfigurationTest.java
+++ b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/PlainConfigurationTest.java
@@ -447,6 +447,13 @@ public class PlainConfigurationTest extends TestCase
"user1", Operation.ACCESS, ObjectType.MANAGEMENT, ObjectProperties.EMPTY);
}
+ public void testBrokerRuleParsing() throws Exception
+ {
+ validateRule(writeACLConfig("ACL ALLOW user1 CONFIGURE BROKER"), "user1", Operation.CONFIGURE, ObjectType.BROKER,
+ ObjectProperties.EMPTY);
+ validateRule(writeACLConfig("ACL ALLOW user1 ALL BROKER"), "user1", Operation.ALL, ObjectType.BROKER, ObjectProperties.EMPTY);
+ }
+
private void validateRule(final PlainConfiguration config, String username, Operation operation, ObjectType objectType, ObjectProperties objectProperties)
{
final RuleSet rs = config.getConfiguration();
diff --git a/qpid/java/broker/etc/broker_example.acl b/qpid/java/broker/etc/broker_example.acl
index fc650801c8..bb85ef5e57 100644
--- a/qpid/java/broker/etc/broker_example.acl
+++ b/qpid/java/broker/etc/broker_example.acl
@@ -72,6 +72,9 @@ ACL ALLOW-LOG webadmins UPDATE USER
ACL ALLOW-LOG webadmins UPDATE METHOD
+# authorise operations changing broker model
+ACL ALLOW-LOG webadmins CONFIGURE BROKER
+
# at the moment only the following UPDATE METHOD rules are supported by web management console
#ACL ALLOW-LOG webadmins UPDATE METHOD component="VirtualHost.Queue" name="moveMessages"
#ACL ALLOW-LOG webadmins UPDATE METHOD component="VirtualHost.Queue" name="copyMessages"
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java
index ce8e06bf4f..8ff0b6d9e1 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java
@@ -90,6 +90,7 @@ abstract class AbstractAdapter implements ConfiguredObject
public final State setDesiredState(final State currentState, final State desiredState)
throws IllegalStateTransitionException, AccessControlException
{
+ authoriseSetDesiredState(currentState, desiredState);
if (_taskExecutor.isTaskExecutorThread())
{
if (setState(currentState, desiredState))
@@ -224,6 +225,7 @@ abstract class AbstractAdapter implements ConfiguredObject
public Object setAttribute(final String name, final Object expected, final Object desired)
throws IllegalStateException, AccessControlException, IllegalArgumentException
{
+ authoriseSetAttribute(name, expected, desired);
if (_taskExecutor.isTaskExecutorThread())
{
if (changeAttribute(name, expected, desired))
@@ -302,6 +304,7 @@ abstract class AbstractAdapter implements ConfiguredObject
@Override
public <C extends ConfiguredObject> C createChild(Class<C> childClass, Map<String, Object> attributes, ConfiguredObject... otherParents)
{
+ authoriseCreateChild(childClass, attributes, otherParents);
if (_taskExecutor.isTaskExecutorThread())
{
C child = addChild(childClass, attributes, otherParents);
@@ -331,6 +334,7 @@ abstract class AbstractAdapter implements ConfiguredObject
@Override
public void setAttributes(final Map<String, Object> attributes) throws IllegalStateException, AccessControlException, IllegalArgumentException
{
+ authoriseSetAttributes(attributes);
if (getTaskExecutor().isTaskExecutorThread())
{
changeAttributes(attributes);
@@ -357,4 +361,24 @@ abstract class AbstractAdapter implements ConfiguredObject
}
}
}
+
+ protected void authoriseSetDesiredState(State currentState, State desiredState) throws AccessControlException
+ {
+ // allowed by default
+ }
+
+ protected void authoriseSetAttribute(String name, Object expected, Object desired) throws AccessControlException
+ {
+ // allowed by default
+ }
+
+ protected <C extends ConfiguredObject> void authoriseCreateChild(Class<C> childClass, Map<String, Object> attributes, ConfiguredObject... otherParents) throws AccessControlException
+ {
+ // allowed by default
+ }
+
+ protected void authoriseSetAttributes(Map<String, Object> attributes) throws AccessControlException
+ {
+ // allowed by default
+ }
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java
index 0c17637e2f..594ef7520a 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java
@@ -316,6 +316,36 @@ public abstract class AuthenticationProviderAdapter<T extends AuthenticationMana
return manager;
}
+ @Override
+ protected void authoriseSetDesiredState(State currentState, State desiredState) throws AccessControlException
+ {
+ if(desiredState == State.DELETED)
+ {
+ if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), AuthenticationProvider.class, Operation.DELETE))
+ {
+ throw new AccessControlException("Deletion of authentication provider is denied");
+ }
+ }
+ }
+
+ @Override
+ protected void authoriseSetAttribute(String name, Object expected, Object desired) throws AccessControlException
+ {
+ if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), AuthenticationProvider.class, Operation.UPDATE))
+ {
+ throw new AccessControlException("Setting of authentication provider attributes is denied");
+ }
+ }
+
+ @Override
+ protected void authoriseSetAttributes(Map<String, Object> attributes) throws AccessControlException
+ {
+ if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), AuthenticationProvider.class, Operation.UPDATE))
+ {
+ throw new AccessControlException("Setting of authentication provider attributes is denied");
+ }
+ }
+
public static class SimpleAuthenticationProviderAdapter extends AuthenticationProviderAdapter<AuthenticationManager>
{
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java
index 841dfb8229..2bf7938b47 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java
@@ -59,8 +59,7 @@ import org.apache.qpid.server.model.TrustStore;
import org.apache.qpid.server.model.UUIDGenerator;
import org.apache.qpid.server.model.VirtualHost;
import org.apache.qpid.server.configuration.updater.TaskExecutor;
-import org.apache.qpid.server.security.auth.manager.Base64MD5PasswordFileAuthenticationManagerFactory;
-import org.apache.qpid.server.security.auth.manager.PlainPasswordFileAuthenticationManagerFactory;
+import org.apache.qpid.server.security.access.Operation;
import org.apache.qpid.server.security.group.FileGroupManager;
import org.apache.qpid.server.security.group.GroupManager;
import org.apache.qpid.server.security.SecurityManager;
@@ -390,7 +389,18 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat
final VirtualHostAdapter virtualHostAdapter = new VirtualHostAdapter(UUID.randomUUID(), attributes, this,
_statisticsGatherer, getTaskExecutor());
addVirtualHost(virtualHostAdapter);
- virtualHostAdapter.setDesiredState(State.INITIALISING, State.ACTIVE);
+
+ // permission has already been granted to create the virtual host
+ // disable further access check on other operations, e.g. create exchange
+ _securityManager.setAccessChecksDisabled(true);
+ try
+ {
+ virtualHostAdapter.setDesiredState(State.INITIALISING, State.ACTIVE);
+ }
+ finally
+ {
+ _securityManager.setAccessChecksDisabled(false);
+ }
return virtualHostAdapter;
}
@@ -1031,7 +1041,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat
@Override
protected void changeAttributes(Map<String, Object> attributes)
{
- //TODO: Add ACL check
//TODO: Add management mode check
Map<String, Object> convertedAttributes = MapValueConverter.convert(attributes, ATTRIBUTE_TYPES);
validateAttributes(convertedAttributes);
@@ -1200,4 +1209,32 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat
}
}
}
+
+ @Override
+ protected void authoriseSetAttribute(String name, Object expected, Object desired) throws AccessControlException
+ {
+ if (!_securityManager.authoriseConfiguringBroker(getName(), Broker.class, Operation.UPDATE))
+ {
+ throw new AccessControlException("Setting of broker attributes is denied");
+ }
+ }
+
+ @Override
+ protected <C extends ConfiguredObject> void authoriseCreateChild(Class<C> childClass, Map<String, Object> attributes,
+ ConfiguredObject... otherParents) throws AccessControlException
+ {
+ if (!_securityManager.authoriseConfiguringBroker(String.valueOf(attributes.get(NAME)), childClass, Operation.CREATE))
+ {
+ throw new AccessControlException("Creation of new broker level entity is denied");
+ }
+ }
+
+ @Override
+ protected void authoriseSetAttributes(Map<String, Object> attributes) throws AccessControlException
+ {
+ if (!_securityManager.authoriseConfiguringBroker(getName(), Broker.class, Operation.UPDATE))
+ {
+ throw new AccessControlException("Setting of broker attributes is denied");
+ }
+ }
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java
index 63063ff58c..a37c2dceb7 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java
@@ -45,6 +45,7 @@ import org.apache.qpid.server.model.Statistics;
import org.apache.qpid.server.model.Transport;
import org.apache.qpid.server.model.VirtualHost;
import org.apache.qpid.server.model.VirtualHostAlias;
+import org.apache.qpid.server.security.access.Operation;
import org.apache.qpid.server.util.MapValueConverter;
import org.apache.qpid.server.util.ParameterizedTypeImpl;
import org.apache.qpid.server.configuration.updater.TaskExecutor;
@@ -356,4 +357,34 @@ public class PortAdapter extends AbstractAdapter implements Port
}
super.changeAttributes(MapValueConverter.convert(attributes, ATTRIBUTE_TYPES));
}
+
+ @Override
+ protected void authoriseSetDesiredState(State currentState, State desiredState) throws AccessControlException
+ {
+ if(desiredState == State.DELETED)
+ {
+ if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), Port.class, Operation.DELETE))
+ {
+ throw new AccessControlException("Deletion of port is denied");
+ }
+ }
+ }
+
+ @Override
+ protected void authoriseSetAttribute(String name, Object expected, Object desired) throws AccessControlException
+ {
+ if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), Port.class, Operation.UPDATE))
+ {
+ throw new AccessControlException("Setting of port attributes is denied");
+ }
+ }
+
+ @Override
+ protected void authoriseSetAttributes(Map<String, Object> attributes) throws AccessControlException
+ {
+ if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), Port.class, Operation.UPDATE))
+ {
+ throw new AccessControlException("Setting of port attributes is denied");
+ }
+ }
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java
index 8680911000..50a83997be 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java
@@ -71,6 +71,7 @@ import org.apache.qpid.server.queue.AMQQueueFactory;
import org.apache.qpid.server.queue.QueueEntry;
import org.apache.qpid.server.queue.QueueRegistry;
import org.apache.qpid.server.security.SecurityManager;
+import org.apache.qpid.server.security.access.Operation;
import org.apache.qpid.server.security.auth.AuthenticatedPrincipal;
import org.apache.qpid.server.stats.StatisticsGatherer;
import org.apache.qpid.server.store.MessageStore;
@@ -980,8 +981,6 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
}
else if (desiredState == State.DELETED)
{
- //TODO: add ACL check to authorize the operation
-
String hostName = getName();
if (hostName.equals(_broker.getAttribute(Broker.DEFAULT_VIRTUAL_HOST)))
@@ -1091,4 +1090,34 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
{
throw new UnsupportedOperationException("Changing attributes on virtualhosts is not supported.");
}
+
+ @Override
+ protected void authoriseSetDesiredState(State currentState, State desiredState) throws AccessControlException
+ {
+ if(desiredState == State.DELETED)
+ {
+ if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), VirtualHost.class, Operation.DELETE))
+ {
+ throw new AccessControlException("Deletion of virtual host is denied");
+ }
+ }
+ }
+
+ @Override
+ protected void authoriseSetAttribute(String name, Object expected, Object desired) throws AccessControlException
+ {
+ if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), VirtualHost.class, Operation.UPDATE))
+ {
+ throw new AccessControlException("Setting of virtual host attributes is denied");
+ }
+ }
+
+ @Override
+ protected void authoriseSetAttributes(Map<String, Object> attributes) throws AccessControlException
+ {
+ if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), VirtualHost.class, Operation.UPDATE))
+ {
+ throw new AccessControlException("Setting of virtual host attributes is denied");
+ }
+ }
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java
index 9ef1ae1a3a..3fb3f70f2e 100755
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java
@@ -33,7 +33,9 @@ import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.security.access.ObjectProperties;
import org.apache.qpid.server.security.access.ObjectType;
import org.apache.qpid.server.security.access.Operation;
+import org.apache.qpid.server.security.access.OperationLoggingDetails;
+import static org.apache.qpid.server.security.access.ObjectType.BROKER;
import static org.apache.qpid.server.security.access.ObjectType.EXCHANGE;
import static org.apache.qpid.server.security.access.ObjectType.GROUP;
import static org.apache.qpid.server.security.access.ObjectType.METHOD;
@@ -41,6 +43,7 @@ import static org.apache.qpid.server.security.access.ObjectType.QUEUE;
import static org.apache.qpid.server.security.access.ObjectType.USER;
import static org.apache.qpid.server.security.access.ObjectType.VIRTUALHOST;
import static org.apache.qpid.server.security.access.Operation.BIND;
+import static org.apache.qpid.server.security.access.Operation.CONFIGURE;
import static org.apache.qpid.server.security.access.Operation.CONSUME;
import static org.apache.qpid.server.security.access.Operation.CREATE;
import static org.apache.qpid.server.security.access.Operation.DELETE;
@@ -549,4 +552,20 @@ public class SecurityManager implements ConfigurationChangeListener
}
}
+ public boolean authoriseConfiguringBroker(String configuredObjectName, Class<? extends ConfiguredObject> configuredObjectType, Operation configuredObjectOperation)
+ {
+ String description = String.format("%s %s '%s'",
+ configuredObjectOperation == null? null : configuredObjectOperation.name().toLowerCase(),
+ configuredObjectType == null ? null : configuredObjectType.getSimpleName().toLowerCase(),
+ configuredObjectName);
+ final OperationLoggingDetails properties = new OperationLoggingDetails(description);
+ return checkAllPlugins(new AccessCheck()
+ {
+ Result allowed(AccessControl plugin)
+ {
+ return plugin.authorise(CONFIGURE, BROKER, properties);
+ }
+ });
+ }
+
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectProperties.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectProperties.java
index 8e38681e68..6c631fc360 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectProperties.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectProperties.java
@@ -350,4 +350,9 @@ public class ObjectProperties
{
return _properties.toString();
}
+
+ public boolean isEmpty()
+ {
+ return _properties.isEmpty();
+ }
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectType.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectType.java
index 8bc4b9d278..e93f487bdb 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectType.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectType.java
@@ -20,6 +20,7 @@ package org.apache.qpid.server.security.access;
import static org.apache.qpid.server.security.access.Operation.ACCESS;
import static org.apache.qpid.server.security.access.Operation.BIND;
+import static org.apache.qpid.server.security.access.Operation.CONFIGURE;
import static org.apache.qpid.server.security.access.Operation.CONSUME;
import static org.apache.qpid.server.security.access.Operation.CREATE;
import static org.apache.qpid.server.security.access.Operation.DELETE;
@@ -48,7 +49,8 @@ public enum ObjectType
ROUTE, // Not allowed in the Java broker
METHOD(Operation.ALL, ACCESS, UPDATE),
USER(Operation.ALL, CREATE, DELETE, UPDATE),
- GROUP(Operation.ALL, CREATE, DELETE, UPDATE);
+ GROUP(Operation.ALL, CREATE, DELETE, UPDATE),
+ BROKER(Operation.ALL, CONFIGURE);
private EnumSet<Operation> _actions;
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Operation.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Operation.java
index 21ea042eed..df5289b7bf 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Operation.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Operation.java
@@ -32,8 +32,9 @@ public enum Operation
UNBIND,
DELETE,
PURGE,
- UPDATE;
-
+ UPDATE,
+ CONFIGURE;
+
public static Operation parse(String text)
{
for (Operation operation : values())
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/OperationLoggingDetails.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/OperationLoggingDetails.java
new file mode 100644
index 0000000000..a683199abc
--- /dev/null
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/OperationLoggingDetails.java
@@ -0,0 +1,53 @@
+/*
+ *
+ * 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 OperationLoggingDetails extends ObjectProperties
+{
+ private String _description;
+
+ public OperationLoggingDetails(String description)
+ {
+ super();
+ _description = description;
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder("(");
+ if (!super.isEmpty())
+ {
+ sb.append("properties=").append(super.toString());
+ }
+ if (_description != null)
+ {
+ if (sb.length() > 1)
+ {
+ sb.append(", ");
+ }
+ sb.append(_description);
+ }
+ sb.append(")");
+ return sb.toString();
+ }
+}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/QpidRestTestCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/QpidRestTestCase.java
index 30d5b195f1..f83eb391e7 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/QpidRestTestCase.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/QpidRestTestCase.java
@@ -95,4 +95,10 @@ public class QpidRestTestCase extends QpidBrokerTestCase
{
return _restTestHelper;
}
+
+ protected void restartBrokerInManagementMode() throws Exception
+ {
+ stopBroker();
+ startBroker(0, true);
+ }
}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java
new file mode 100644
index 0000000000..5d23219336
--- /dev/null
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java
@@ -0,0 +1,455 @@
+/*
+ *
+ * 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.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.qpid.server.model.AuthenticationProvider;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.Port;
+import org.apache.qpid.server.model.Protocol;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.security.acl.AbstractACLTestCase;
+import org.apache.qpid.server.security.auth.manager.AnonymousAuthenticationManagerFactory;
+import org.apache.qpid.server.security.auth.manager.PlainPasswordFileAuthenticationManagerFactory;
+import org.apache.qpid.systest.rest.QpidRestTestCase;
+import org.apache.qpid.test.utils.TestBrokerConfiguration;
+import org.apache.qpid.test.utils.TestFileUtils;
+
+public class BrokerACLTest 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 + " CONFIGURE BROKER",
+ "ACL DENY-LOG " + DENIED_USER + " CONFIGURE BROKER",
+ "ACL DENY-LOG ALL ALL");
+
+ getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT,
+ "httpBasicAuthenticationEnabled", true);
+ }
+
+ public void testCreateAuthenticationProviderAllowed() throws Exception
+ {
+ getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER);
+
+ String authenticationProviderName = getTestName();
+
+ int responseCode = createAuthenticationProvider(authenticationProviderName);
+ assertEquals("Provider creation should be allowed", 201, responseCode);
+
+ assertAuthenticationProviderExists(authenticationProviderName);
+ }
+
+ public void testCreateAuthenticationProviderDenied() throws Exception
+ {
+ getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER);
+
+ String authenticationProviderName = getTestName();
+
+ int responseCode = createAuthenticationProvider(authenticationProviderName);
+ assertEquals("Provider creation should be denied", 403, responseCode);
+
+ assertAuthenticationProviderDoesNotExist(authenticationProviderName);
+ }
+
+ public void testDeleteAuthenticationProviderAllowed() throws Exception
+ {
+ getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER);
+
+ String providerName = getTestName();
+
+ int responseCode = createAuthenticationProvider(providerName);
+ assertEquals("Provider creation should be allowed", 201, responseCode);
+
+ assertAuthenticationProviderExists(providerName);
+
+ responseCode = getRestTestHelper().submitRequest("/rest/authenticationprovider/" + providerName, "DELETE", null);
+ assertEquals("Provider deletion should be allowed", 200, responseCode);
+
+ assertAuthenticationProviderDoesNotExist(TEST2_VIRTUALHOST);
+ }
+
+ public void testDeleteAuthenticationProviderDenied() throws Exception
+ {
+ getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER);
+
+ String providerName = getTestName();
+
+ int responseCode = createAuthenticationProvider(providerName);
+ assertEquals("Provider creation should be allowed", 201, responseCode);
+
+ assertAuthenticationProviderExists(providerName);
+
+ getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER);
+ responseCode = getRestTestHelper().submitRequest("/rest/authenticationprovider/" + providerName, "DELETE", null);
+ assertEquals("Provider deletion should be denied", 403, responseCode);
+
+ assertAuthenticationProviderExists(providerName);
+ }
+
+ public void testSetAuthenticationProviderAttributesAllowed() throws Exception
+ {
+ getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER);
+
+ String providerName = TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER;
+
+ assertAuthenticationProviderExists(providerName);
+
+ File file = TestFileUtils.createTempFile(this, ".users", "guest:guest\n" + ALLOWED_USER + ":" + ALLOWED_USER + "\n"
+ + DENIED_USER + ":" + DENIED_USER);
+
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ attributes.put(AuthenticationProvider.NAME, providerName);
+ attributes.put(AuthenticationProvider.TYPE, PlainPasswordFileAuthenticationManagerFactory.PROVIDER_TYPE);
+ attributes.put(PlainPasswordFileAuthenticationManagerFactory.ATTRIBUTE_PATH, file.getAbsolutePath());
+
+ int responseCode = getRestTestHelper().submitRequest("/rest/authenticationprovider/" + providerName, "PUT", attributes);
+ assertEquals("Setting of provider attribites should be allowed", 200, responseCode);
+ }
+
+ public void testSetAuthenticationProviderAttributesDenied() throws Exception
+ {
+ getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER);
+
+ String providerName = TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER;
+
+ Map<String, Object> providerData = getRestTestHelper().getJsonAsSingletonList(
+ "/rest/authenticationprovider/" + providerName);
+
+ File file = TestFileUtils.createTempFile(this, ".users", "guest:guest\n" + ALLOWED_USER + ":" + ALLOWED_USER + "\n"
+ + DENIED_USER + ":" + DENIED_USER);
+
+ getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER);
+
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ attributes.put(AuthenticationProvider.NAME, providerName);
+ attributes.put(AuthenticationProvider.TYPE, AnonymousAuthenticationManagerFactory.PROVIDER_TYPE);
+ attributes.put(PlainPasswordFileAuthenticationManagerFactory.ATTRIBUTE_PATH, file.getAbsolutePath());
+
+ int responseCode = getRestTestHelper().submitRequest("/rest/authenticationprovider/" + providerName, "PUT", attributes);
+ assertEquals("Setting of provider attribites should be allowed", 403, responseCode);
+
+ Map<String, Object> provider = getRestTestHelper().getJsonAsSingletonList("/rest/authenticationprovider/" + providerName);
+ assertEquals("Unexpected PATH attribute value",
+ providerData.get(PlainPasswordFileAuthenticationManagerFactory.ATTRIBUTE_PATH),
+ provider.get(PlainPasswordFileAuthenticationManagerFactory.ATTRIBUTE_PATH));
+ }
+
+ public void testCreateVirtualHostAllowed() throws Exception
+ {
+ getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER);
+
+ String hostName = getTestName();
+
+ int responseCode = createHost(hostName);
+ assertEquals("Host creation should be allowed", 201, responseCode);
+
+ assertVirtualHostExists(hostName);
+ }
+
+ public void testCreateVirtualHostDenied() throws Exception
+ {
+ getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER);
+
+ String hostName = getTestName();
+
+ int responseCode = createHost(hostName);
+ assertEquals("Host creation should be denied", 403, responseCode);
+
+ assertVirtualHostDoesNotExist(hostName);
+ }
+
+ public void testDeleteVirtualHostAllowed() throws Exception
+ {
+ getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER);
+
+ assertVirtualHostExists(TEST2_VIRTUALHOST);
+
+ int responseCode = getRestTestHelper().submitRequest("/rest/virtualhost/" + TEST2_VIRTUALHOST, "DELETE", null);
+ assertEquals("Host deletion should be allowed", 200, responseCode);
+
+ assertVirtualHostDoesNotExist(TEST2_VIRTUALHOST);
+ }
+
+ public void testDeleteVirtualHostDenied() throws Exception
+ {
+ getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER);
+
+ assertVirtualHostExists(TEST2_VIRTUALHOST);
+
+ getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER);
+
+ int responseCode = getRestTestHelper().submitRequest("/rest/virtualhost/" + TEST2_VIRTUALHOST, "DELETE", null);
+ assertEquals("Host deletion should be denied", 403, responseCode);
+
+ assertVirtualHostExists(TEST2_VIRTUALHOST);
+ }
+
+ public void testCreatePortAllowed() throws Exception
+ {
+ getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER);
+
+ String portName = getTestName();
+
+ int responseCode = createPort(portName);
+ assertEquals("Port creation should be allowed", 201, responseCode);
+
+ assertPortExists(portName);
+ }
+
+ public void testCreatePortDenied() throws Exception
+ {
+ getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER);
+
+ String portName = getTestName();
+
+ int responseCode = createPort(portName);
+ assertEquals("Port creation should be denied", 403, responseCode);
+
+ assertPortDoesNotExist(portName);
+ }
+
+ public void testDeletePortDenied() throws Exception
+ {
+ getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER);
+
+ String portName = TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT;
+ assertPortExists(portName);
+
+ restartBrokerInManagementMode();
+
+ getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER);
+
+ int responseCode = getRestTestHelper().submitRequest("/rest/port/" + portName, "DELETE", null);
+ assertEquals("Port deletion should be denied", 403, responseCode);
+
+ assertPortExists(portName);
+ }
+
+ public void testDeletePortAllowed() throws Exception
+ {
+ getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER);
+
+ String portName = TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT;
+ assertPortExists(portName);
+
+ restartBrokerInManagementMode();
+
+ getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER);
+
+ int responseCode = getRestTestHelper().submitRequest("/rest/port/" + portName, "DELETE", null);
+ assertEquals("Port deletion should be allowed", 200, responseCode);
+
+ assertPortDoesNotExist(portName);
+ }
+
+ public void testSetPortAttributesAllowed() throws Exception
+ {
+ getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER);
+
+ String portName = getTestName();
+
+ int responseCode = createPort(portName);
+ assertEquals("Port creation should be allowed", 201, responseCode);
+
+ assertPortExists(portName);
+
+ restartBrokerInManagementMode();
+
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ attributes.put(Port.NAME, portName);
+ attributes.put(Port.AUTHENTICATION_PROVIDER, ANONYMOUS_AUTHENTICATION_PROVIDER);
+ responseCode = getRestTestHelper().submitRequest("/rest/port/" + portName, "PUT", attributes);
+ assertEquals("Setting of port attribites should be allowed", 200, responseCode);
+
+ Map<String, Object> port = getRestTestHelper().getJsonAsSingletonList("/rest/port/" + portName);
+ assertEquals("Unexpected authentication provider attribute value", ANONYMOUS_AUTHENTICATION_PROVIDER,
+ port.get(Port.AUTHENTICATION_PROVIDER));
+ }
+
+ public void testSetPortAttributesDenied() throws Exception
+ {
+ getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER);
+
+ String portName = getTestName();
+
+ int responseCode = createPort(portName);
+ assertEquals("Port creation should be allowed", 201, responseCode);
+
+ assertPortExists(portName);
+
+ restartBrokerInManagementMode();
+
+ getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER);
+
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ attributes.put(Port.NAME, portName);
+ attributes.put(Port.PROTOCOLS, Arrays.asList(Protocol.AMQP_0_9));
+ attributes.put(Port.AUTHENTICATION_PROVIDER, ANONYMOUS_AUTHENTICATION_PROVIDER);
+ responseCode = getRestTestHelper().submitRequest("/rest/port/" + portName, "PUT", attributes);
+ assertEquals("Setting of port attribites should be denied", 403, responseCode);
+
+ Map<String, Object> port = getRestTestHelper().getJsonAsSingletonList("/rest/port/" + portName);
+ assertEquals("Unexpected authentication provider attribute value",
+ TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER, port.get(Port.AUTHENTICATION_PROVIDER));
+ }
+
+ public void testSetBrokerAttributesAllowed() throws Exception
+ {
+ getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER);
+
+ String defaultAuthenticationProvider = TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER;
+ Map<String, Object> brokerAttributes = getRestTestHelper().getJsonAsSingletonList("/rest/broker");
+ assertEquals("Unexpected authentication provider", defaultAuthenticationProvider,
+ brokerAttributes.get(Broker.DEFAULT_AUTHENTICATION_PROVIDER));
+ restartBrokerInManagementMode();
+
+ Map<String, Object> newAttributes = new HashMap<String, Object>();
+ newAttributes.put(Broker.DEFAULT_AUTHENTICATION_PROVIDER, ANONYMOUS_AUTHENTICATION_PROVIDER);
+ int responseCode = getRestTestHelper().submitRequest("/rest/broker", "PUT", newAttributes);
+ assertEquals("Setting of port attribites should be allowed", 200, responseCode);
+
+ brokerAttributes = getRestTestHelper().getJsonAsSingletonList("/rest/broker");
+ assertEquals("Unexpected default authentication provider attribute value", ANONYMOUS_AUTHENTICATION_PROVIDER,
+ brokerAttributes.get(Broker.DEFAULT_AUTHENTICATION_PROVIDER));
+ }
+
+ public void testSetBrokerAttributesDenied() throws Exception
+ {
+ getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER);
+
+ String defaultAuthenticationProvider = TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER;
+
+ Map<String, Object> brokerAttributes = getRestTestHelper().getJsonAsSingletonList("/rest/broker");
+ assertEquals("Unexpected authentication provider", defaultAuthenticationProvider,
+ brokerAttributes.get(Broker.DEFAULT_AUTHENTICATION_PROVIDER));
+ restartBrokerInManagementMode();
+
+ getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER);
+ Map<String, Object> newAttributes = new HashMap<String, Object>();
+ newAttributes.put(Broker.DEFAULT_AUTHENTICATION_PROVIDER, ANONYMOUS_AUTHENTICATION_PROVIDER);
+ int responseCode = getRestTestHelper().submitRequest("/rest/broker", "PUT", newAttributes);
+ assertEquals("Setting of port attribites should be allowed", 403, responseCode);
+
+ brokerAttributes = getRestTestHelper().getJsonAsSingletonList("/rest/broker");
+ assertEquals("Unexpected default authentication provider attribute value", defaultAuthenticationProvider,
+ brokerAttributes.get(Broker.DEFAULT_AUTHENTICATION_PROVIDER));
+ }
+
+ private int createPort(String portName) throws Exception
+ {
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ attributes.put(Port.NAME, portName);
+ attributes.put(Port.PORT, findFreePort());
+ attributes.put(Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER);
+
+ return getRestTestHelper().submitRequest("/rest/port/" + portName, "PUT", attributes);
+ }
+
+ private void assertPortExists(String portName) throws Exception
+ {
+ assertPortExistence(portName, true);
+ }
+
+ private void assertPortDoesNotExist(String portName) throws Exception
+ {
+ assertPortExistence(portName, false);
+ }
+
+ private void assertPortExistence(String portName, boolean exists) throws Exception
+ {
+ List<Map<String, Object>> hosts = getRestTestHelper().getJsonAsList("/rest/port/" + portName);
+ assertEquals("Unexpected result", exists, !hosts.isEmpty());
+ }
+
+ private int createHost(String hostName) throws Exception
+ {
+ Map<String, Object> hostData = new HashMap<String, Object>();
+ hostData.put(VirtualHost.NAME, hostName);
+ hostData.put(VirtualHost.STORE_PATH, getStoreLocation(hostName));
+ hostData.put(VirtualHost.STORE_TYPE, getTestProfileMessageStoreType());
+
+ return getRestTestHelper().submitRequest("/rest/virtualhost/" + hostName, "PUT", hostData);
+ }
+
+ private void assertVirtualHostDoesNotExist(String hostName) throws Exception
+ {
+ assertVirtualHostExistence(hostName, false);
+ }
+
+ private void assertVirtualHostExists(String hostName) throws Exception
+ {
+ assertVirtualHostExistence(hostName, true);
+ }
+
+ private void assertVirtualHostExistence(String hostName, boolean exists) throws Exception
+ {
+ List<Map<String, Object>> hosts = getRestTestHelper().getJsonAsList("/rest/virtualhost/" + hostName);
+ assertEquals("Unexpected result", exists, !hosts.isEmpty());
+ }
+
+ private String getStoreLocation(String hostName)
+ {
+ return new File(TMP_FOLDER, "store-" + hostName + "-" + System.currentTimeMillis()).getAbsolutePath();
+ }
+
+ private int createAuthenticationProvider(String authenticationProviderName) throws Exception
+ {
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ attributes.put(AuthenticationProvider.NAME, authenticationProviderName);
+ attributes.put(AuthenticationProvider.TYPE, AnonymousAuthenticationManagerFactory.PROVIDER_TYPE);
+
+ return getRestTestHelper().submitRequest("/rest/authenticationprovider/" + authenticationProviderName, "PUT", attributes);
+ }
+
+ private void assertAuthenticationProviderDoesNotExist(String authenticationProviderName) throws Exception
+ {
+ assertAuthenticationProviderExistence(authenticationProviderName, false);
+ }
+
+ private void assertAuthenticationProviderExists(String authenticationProviderName) throws Exception
+ {
+ assertAuthenticationProviderExistence(authenticationProviderName, true);
+ }
+
+ private void assertAuthenticationProviderExistence(String authenticationProviderName, boolean exists) throws Exception
+ {
+ String path = "/rest/authenticationprovider/" + authenticationProviderName;
+ List<Map<String, Object>> providers = getRestTestHelper().getJsonAsList(path);
+ assertEquals("Unexpected result", exists, !providers.isEmpty());
+ }
+
+}