diff options
13 files changed, 778 insertions, 110 deletions
diff --git a/java/bdbstore/src/main/java/org/apache/qpid/server/virtualhostnode/berkeleydb/BDBHARemoteReplicationNodeImpl.java b/java/bdbstore/src/main/java/org/apache/qpid/server/virtualhostnode/berkeleydb/BDBHARemoteReplicationNodeImpl.java index 5327997498..64b29b8daf 100644 --- a/java/bdbstore/src/main/java/org/apache/qpid/server/virtualhostnode/berkeleydb/BDBHARemoteReplicationNodeImpl.java +++ b/java/bdbstore/src/main/java/org/apache/qpid/server/virtualhostnode/berkeleydb/BDBHARemoteReplicationNodeImpl.java @@ -24,6 +24,7 @@ package org.apache.qpid.server.virtualhostnode.berkeleydb; import static com.sleepycat.je.rep.ReplicatedEnvironment.State.MASTER; import static com.sleepycat.je.rep.ReplicatedEnvironment.State.REPLICA; +import java.security.AccessControlException; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; @@ -33,11 +34,13 @@ import com.sleepycat.je.rep.MasterStateException; import org.apache.log4j.Logger; import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.model.AbstractConfiguredObject; +import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.model.IllegalStateTransitionException; import org.apache.qpid.server.model.ManagedAttributeField; import org.apache.qpid.server.model.State; import org.apache.qpid.server.model.StateTransition; +import org.apache.qpid.server.security.access.Operation; import org.apache.qpid.server.store.berkeleydb.replication.ReplicatedEnvironmentFacade; public class BDBHARemoteReplicationNodeImpl extends AbstractConfiguredObject<BDBHARemoteReplicationNodeImpl> implements BDBHARemoteReplicationNode<BDBHARemoteReplicationNodeImpl> @@ -46,6 +49,7 @@ public class BDBHARemoteReplicationNodeImpl extends AbstractConfiguredObject<BDB private final ReplicatedEnvironmentFacade _replicatedEnvironmentFacade; private final String _address; + private final Broker _broker; private volatile long _joinTime; private volatile long _lastTransactionId; @@ -59,6 +63,7 @@ public class BDBHARemoteReplicationNodeImpl extends AbstractConfiguredObject<BDB public BDBHARemoteReplicationNodeImpl(BDBHAVirtualHostNode<?> virtualHostNode, Map<String, Object> attributes, ReplicatedEnvironmentFacade replicatedEnvironmentFacade) { super(parentsMap(virtualHostNode), attributes); + _broker = virtualHostNode.getParent(Broker.class); _address = (String)attributes.get(ADDRESS); _replicatedEnvironmentFacade = replicatedEnvironmentFacade; _state = new AtomicReference<State>(State.ACTIVE); @@ -113,6 +118,27 @@ public class BDBHARemoteReplicationNodeImpl extends AbstractConfiguredObject<BDB super.deleted(); } + + @Override + protected void authoriseSetAttributes(final ConfiguredObject<?> proxyForValidation, + final Set<String> modifiedAttributes) + { + _broker.getSecurityManager().authoriseVirtualHostNode(getName(), Operation.UPDATE); + } + + @Override + protected void authoriseSetDesiredState(State desiredState) throws AccessControlException + { + if(desiredState == State.DELETED) + { + _broker.getSecurityManager().authoriseVirtualHostNode(getName(), Operation.DELETE); + } + else + { + _broker.getSecurityManager().authoriseVirtualHostNode(getName(), Operation.UPDATE); + } + } + @Override public String toString() { diff --git a/java/bdbstore/src/test/java/org/apache/qpid/server/virtualhostnode/berkeleydb/BDBHARemoteReplicationNodeTest.java b/java/bdbstore/src/test/java/org/apache/qpid/server/virtualhostnode/berkeleydb/BDBHARemoteReplicationNodeTest.java new file mode 100644 index 0000000000..ffefc65484 --- /dev/null +++ b/java/bdbstore/src/test/java/org/apache/qpid/server/virtualhostnode/berkeleydb/BDBHARemoteReplicationNodeTest.java @@ -0,0 +1,160 @@ +/* + * 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.virtualhostnode.berkeleydb; + +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.security.AccessControlException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.qpid.server.configuration.updater.CurrentThreadTaskExecutor; +import org.apache.qpid.server.configuration.updater.TaskExecutor; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.ConfiguredObjectFactory; +import org.apache.qpid.server.model.RemoteReplicationNode; +import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.security.SecurityManager; +import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.store.DurableConfigurationStore; +import org.apache.qpid.server.store.berkeleydb.replication.ReplicatedEnvironmentFacade; +import org.apache.qpid.server.util.BrokerTestHelper; +import org.apache.qpid.test.utils.QpidTestCase; + +public class BDBHARemoteReplicationNodeTest extends QpidTestCase +{ + private final org.apache.qpid.server.security.SecurityManager _mockSecurityManager = mock(SecurityManager.class); + + private Broker _broker; + private TaskExecutor _taskExecutor; + private BDBHAVirtualHostNode<?> _virtualHostNode; + private DurableConfigurationStore _configStore; + private ReplicatedEnvironmentFacade _facade; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + _facade = mock(ReplicatedEnvironmentFacade.class); + + _broker = BrokerTestHelper.createBrokerMock(); + + _taskExecutor = new CurrentThreadTaskExecutor(); + _taskExecutor.start(); + when(_broker.getTaskExecutor()).thenReturn(_taskExecutor); + + _virtualHostNode = mock(BDBHAVirtualHostNode.class); + _configStore = mock(DurableConfigurationStore.class); + when(_virtualHostNode.getConfigurationStore()).thenReturn(_configStore); + + // Virtualhost needs the EventLogger from the SystemContext. + when(_virtualHostNode.getParent(Broker.class)).thenReturn(_broker); + + ConfiguredObjectFactory objectFactory = _broker.getObjectFactory(); + when(_virtualHostNode.getModel()).thenReturn(objectFactory.getModel()); + when(_virtualHostNode.getTaskExecutor()).thenReturn(_taskExecutor); + } + + public void testUpdateRole() + { + String remoteReplicationName = getName(); + BDBHARemoteReplicationNode remoteReplicationNode = createRemoteReplicationNode(remoteReplicationName); + + remoteReplicationNode.setAttribute(BDBHARemoteReplicationNode.ROLE, null, "MASTER"); + + verify(_facade).transferMasterAsynchronously(remoteReplicationName); + } + + public void testDelete() + { + String remoteReplicationName = getName(); + BDBHARemoteReplicationNode remoteReplicationNode = createRemoteReplicationNode(remoteReplicationName); + + remoteReplicationNode.delete(); + + verify(_facade).removeNodeFromGroup(remoteReplicationName); + } + + // *************** ReplicationNode Access Control Tests *************** + + public void testUpdateDeniedByACL() + { + when(_broker.getSecurityManager()).thenReturn(_mockSecurityManager); + + String remoteReplicationName = getName(); + BDBHARemoteReplicationNode remoteReplicationNode = createRemoteReplicationNode(remoteReplicationName); + + doThrow(new AccessControlException("mocked ACL exception")).when(_mockSecurityManager).authoriseVirtualHostNode( + remoteReplicationName, + Operation.UPDATE); + + assertNull(remoteReplicationNode.getDescription()); + + try + { + remoteReplicationNode.setAttribute(VirtualHost.DESCRIPTION, null, "My description"); + fail("Exception not thrown"); + } + catch (AccessControlException ace) + { + // PASS + } + } + + public void testDeleteDeniedByACL() + { + when(_broker.getSecurityManager()).thenReturn(_mockSecurityManager); + + String remoteReplicationName = getName(); + BDBHARemoteReplicationNode remoteReplicationNode = createRemoteReplicationNode(remoteReplicationName); + + doThrow(new AccessControlException("mocked ACL exception")).when(_mockSecurityManager).authoriseVirtualHostNode( + remoteReplicationName, + Operation.DELETE); + + assertNull(remoteReplicationNode.getDescription()); + + try + { + remoteReplicationNode.delete(); + fail("Exception not thrown"); + } + catch (AccessControlException ace) + { + // PASS + } + } + + private BDBHARemoteReplicationNode createRemoteReplicationNode(final String replicationNodeName) + { + Map<String, Object> attributes = new HashMap<>(); + attributes.put(RemoteReplicationNode.NAME, replicationNodeName); + + BDBHARemoteReplicationNodeImpl node = new BDBHARemoteReplicationNodeImpl(_virtualHostNode, attributes, _facade); + node.create(); + return node; + } + + +}
\ No newline at end of file diff --git a/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java b/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java index 77eda0dc67..2aba33104a 100644 --- a/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java +++ b/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java @@ -895,9 +895,19 @@ public class BrokerAdapter extends AbstractConfiguredObject<BrokerAdapter> imple 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)) + if (childClass == VirtualHostNode.class) { - throw new AccessControlException("Creation of new broker level entity is denied"); + _securityManager.authoriseVirtualHostNode(String.valueOf(attributes.get(NAME)), Operation.CREATE); + + } + else + { + if (!_securityManager.authoriseConfiguringBroker(String.valueOf(attributes.get(NAME)), + childClass, + Operation.CREATE)) + { + throw new AccessControlException("Creation of new broker level entity is denied"); + } } } diff --git a/java/broker-core/src/main/java/org/apache/qpid/server/security/SecurityManager.java b/java/broker-core/src/main/java/org/apache/qpid/server/security/SecurityManager.java index a8a59a317c..2f7acba91d 100755 --- a/java/broker-core/src/main/java/org/apache/qpid/server/security/SecurityManager.java +++ b/java/broker-core/src/main/java/org/apache/qpid/server/security/SecurityManager.java @@ -25,6 +25,7 @@ import static org.apache.qpid.server.security.access.ObjectType.METHOD; 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.ObjectType.VIRTUALHOSTNODE; import static org.apache.qpid.server.security.access.Operation.*; import java.security.AccessControlException; @@ -242,9 +243,24 @@ public class SecurityManager implements ConfigurationChangeListener } } - public void authoriseCreateConnection(final AMQConnectionModel connection) + public void authoriseVirtualHostNode(final String virtualHostNodeName, final Operation operation) + { + if(!checkAllPlugins(new AccessCheck() + { + Result allowed(AccessControl plugin) + { + ObjectProperties properties = new ObjectProperties(virtualHostNodeName); + return plugin.authorise(operation, VIRTUALHOSTNODE, properties); + } + })) + { + throw new AccessControlException(operation + " permission denied for " + VIRTUALHOSTNODE + + " : " + virtualHostNodeName); + } + } + + public void authoriseVirtualHost(final String virtualHostName, final Operation operation) { - final String virtualHostName = connection.getVirtualHostName(); if(!checkAllPlugins(new AccessCheck() { Result allowed(AccessControl plugin) @@ -252,10 +268,24 @@ public class SecurityManager implements ConfigurationChangeListener // We put the name into the properties under both name and virtualhost_name so the user may express predicates using either. ObjectProperties properties = new ObjectProperties(virtualHostName); properties.put(Property.VIRTUALHOST_NAME, virtualHostName); - return plugin.authorise(Operation.ACCESS, VIRTUALHOST, properties); + return plugin.authorise(operation, VIRTUALHOST, properties); } })) { + throw new AccessControlException(operation + " permission denied for " + VIRTUALHOST + + " : " + virtualHostName); + } + } + + public void authoriseCreateConnection(final AMQConnectionModel connection) + { + String virtualHostName = connection.getVirtualHostName(); + try + { + authoriseVirtualHost(virtualHostName, Operation.ACCESS); + } + catch (AccessControlException ace) + { throw new AccessControlException("Permission denied: " + virtualHostName); } } diff --git a/java/broker-core/src/main/java/org/apache/qpid/server/security/access/ObjectType.java b/java/broker-core/src/main/java/org/apache/qpid/server/security/access/ObjectType.java index 9016205d1c..88b1a6b727 100644 --- a/java/broker-core/src/main/java/org/apache/qpid/server/security/access/ObjectType.java +++ b/java/broker-core/src/main/java/org/apache/qpid/server/security/access/ObjectType.java @@ -42,7 +42,8 @@ import java.util.Set; public enum ObjectType { ALL(Operation.ALL), - VIRTUALHOST(Operation.ALL, ACCESS), + VIRTUALHOSTNODE(Operation.ALL, CREATE, DELETE, UPDATE), + VIRTUALHOST(Operation.ALL, ACCESS, CREATE, DELETE, UPDATE), MANAGEMENT(Operation.ALL, ACCESS), QUEUE(Operation.ALL, CREATE, DELETE, PURGE, CONSUME, UPDATE), EXCHANGE(Operation.ALL, ACCESS, CREATE, DELETE, BIND, UNBIND, PUBLISH, UPDATE), diff --git a/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java b/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java index 3c63c5b869..fa6da37d45 100644 --- a/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java +++ b/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java @@ -318,20 +318,18 @@ public abstract class AbstractVirtualHost<X extends AbstractVirtualHost<X>> exte { if(desiredState == State.DELETED) { - if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), org.apache.qpid.server.model.VirtualHost.class, Operation.DELETE)) - { - throw new AccessControlException("Deletion of virtual host is denied"); - } + _broker.getSecurityManager().authoriseVirtualHost(getName(), Operation.DELETE); + } + else + { + _broker.getSecurityManager().authoriseVirtualHost(getName(), Operation.UPDATE); } } @Override protected void authoriseSetAttributes(ConfiguredObject<?> modified, Set<String> attributes) throws AccessControlException { - if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), org.apache.qpid.server.model.VirtualHost.class, Operation.UPDATE)) - { - throw new AccessControlException("Setting of virtual host attributes is denied"); - } + _broker.getSecurityManager().authoriseVirtualHost(getName(), Operation.UPDATE); } public Collection<Connection> getConnections() diff --git a/java/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/AbstractVirtualHostNode.java b/java/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/AbstractVirtualHostNode.java index 0cb69e4cd8..b21fcd704f 100644 --- a/java/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/AbstractVirtualHostNode.java +++ b/java/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/AbstractVirtualHostNode.java @@ -197,20 +197,36 @@ public abstract class AbstractVirtualHostNode<X extends AbstractVirtualHostNode< { if(desiredState == State.DELETED) { - if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), VirtualHostNode.class, Operation.DELETE)) - { - throw new AccessControlException("Deletion of virtual host node is denied"); - } + _broker.getSecurityManager().authoriseVirtualHostNode(getName(), Operation.DELETE); + } + else + { + _broker.getSecurityManager().authoriseVirtualHostNode(getName(), Operation.UPDATE); } } @Override - protected void authoriseSetAttributes(ConfiguredObject<?> modified, Set<String> attributes) throws AccessControlException + protected <C extends ConfiguredObject> void authoriseCreateChild(final Class<C> childClass, + final Map<String, Object> attributes, + final ConfiguredObject... otherParents) + throws AccessControlException { - if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), VirtualHostNode.class, Operation.UPDATE)) + if (childClass == VirtualHost.class) { - throw new AccessControlException("Setting of virtual host node attributes is denied"); + _broker.getSecurityManager().authoriseVirtualHost(String.valueOf(attributes.get(VirtualHost.NAME)), + Operation.CREATE); + } + else + { + super.authoriseCreateChild(childClass, attributes, otherParents); + } + } + + @Override + protected void authoriseSetAttributes(ConfiguredObject<?> modified, Set<String> attributes) throws AccessControlException + { + _broker.getSecurityManager().authoriseVirtualHostNode(getName(), Operation.UPDATE); } private void closeConfigurationStore() diff --git a/java/broker-core/src/test/java/org/apache/qpid/server/model/VirtualHostTest.java b/java/broker-core/src/test/java/org/apache/qpid/server/model/VirtualHostTest.java index b69af13c13..d12f0bc5f9 100644 --- a/java/broker-core/src/test/java/org/apache/qpid/server/model/VirtualHostTest.java +++ b/java/broker-core/src/test/java/org/apache/qpid/server/model/VirtualHostTest.java @@ -22,12 +22,15 @@ package org.apache.qpid.server.model; import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import java.security.AccessControlException; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -36,6 +39,8 @@ import org.mockito.ArgumentMatcher; import org.apache.qpid.server.configuration.updater.CurrentThreadTaskExecutor; import org.apache.qpid.server.configuration.updater.TaskExecutor; +import org.apache.qpid.server.security.SecurityManager; +import org.apache.qpid.server.security.access.Operation; import org.apache.qpid.server.store.ConfiguredObjectRecord; import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.util.BrokerTestHelper; @@ -44,6 +49,7 @@ import org.apache.qpid.test.utils.QpidTestCase; public class VirtualHostTest extends QpidTestCase { + private final SecurityManager _mockSecurityManager = mock(SecurityManager.class); private Broker _broker; private TaskExecutor _taskExecutor; private VirtualHostNode<?> _virtualHostNode; @@ -180,6 +186,82 @@ public class VirtualHostTest extends QpidTestCase verify(_configStore, never()).create(matchesRecord(queue.getId(), queue.getType())); } + // *************** VH Access Control Tests *************** + + public void testUpdateDeniedByACL() + { + when(_broker.getSecurityManager()).thenReturn(_mockSecurityManager); + + String virtualHostName = getName(); + VirtualHost<?,?,?> virtualHost = createVirtualHost(virtualHostName); + + doThrow(new AccessControlException("mocked ACL exception")).when(_mockSecurityManager).authoriseVirtualHost( + virtualHostName, + Operation.UPDATE); + + assertNull(virtualHost.getDescription()); + + try + { + virtualHost.setAttribute(VirtualHost.DESCRIPTION, null, "My description"); + fail("Exception not thrown"); + } + catch (AccessControlException ace) + { + // PASS + } + + verify(_configStore, never()).update(eq(false), matchesRecord(virtualHost.getId(), virtualHost.getType())); + } + + public void testStopDeniedByACL() + { + when(_broker.getSecurityManager()).thenReturn(_mockSecurityManager); + + String virtualHostName = getName(); + VirtualHost<?,?,?> virtualHost = createVirtualHost(virtualHostName); + + doThrow(new AccessControlException("mocked ACL exception")).when(_mockSecurityManager).authoriseVirtualHost( + virtualHostName, + Operation.UPDATE); + + try + { + virtualHost.stop(); + fail("Exception not thrown"); + } + catch (AccessControlException ace) + { + // PASS + } + + verify(_configStore, never()).update(eq(false), matchesRecord(virtualHost.getId(), virtualHost.getType())); + } + + public void testDeleteDeniedByACL() + { + when(_broker.getSecurityManager()).thenReturn(_mockSecurityManager); + + String virtualHostName = getName(); + VirtualHost<?,?,?> virtualHost = createVirtualHost(virtualHostName); + + doThrow(new AccessControlException("mocked ACL exception")).when(_mockSecurityManager).authoriseVirtualHost( + virtualHostName, + Operation.DELETE); + + try + { + virtualHost.delete(); + fail("Exception not thrown"); + } + catch (AccessControlException ace) + { + // PASS + } + + verify(_configStore, never()).remove(matchesRecord(virtualHost.getId(), virtualHost.getType())); + } + private VirtualHost<?,?,?> createVirtualHost(final String virtualHostName) { Map<String, Object> attributes = new HashMap<>(); diff --git a/java/broker-core/src/test/java/org/apache/qpid/server/virtualhostnode/AbstractStandardVirtualHostNodeTest.java b/java/broker-core/src/test/java/org/apache/qpid/server/virtualhostnode/AbstractStandardVirtualHostNodeTest.java index ea66911a89..d7c35ba6a8 100644 --- a/java/broker-core/src/test/java/org/apache/qpid/server/virtualhostnode/AbstractStandardVirtualHostNodeTest.java +++ b/java/broker-core/src/test/java/org/apache/qpid/server/virtualhostnode/AbstractStandardVirtualHostNodeTest.java @@ -21,9 +21,11 @@ package org.apache.qpid.server.virtualhostnode; import static org.apache.qpid.server.virtualhostnode.AbstractStandardVirtualHostNode.*; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.security.AccessControlException; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -39,6 +41,8 @@ import org.apache.qpid.server.model.State; import org.apache.qpid.server.model.SystemContext; import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.model.VirtualHostNode; +import org.apache.qpid.server.security.SecurityManager; +import org.apache.qpid.server.security.access.Operation; import org.apache.qpid.server.store.ConfiguredObjectRecord; import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.store.NullMessageStore; @@ -53,7 +57,8 @@ public class AbstractStandardVirtualHostNodeTest extends QpidTestCase private static final String TEST_VIRTUAL_HOST_NODE_NAME = "testNode"; private static final String TEST_VIRTUAL_HOST_NAME = "testVirtualHost"; - private UUID _nodeId = UUID.randomUUID(); + private final UUID _nodeId = UUID.randomUUID(); + private final SecurityManager _mockSecurityManager = mock(SecurityManager.class); private Broker<?> _broker; private TaskExecutor _taskExecutor; @@ -109,7 +114,6 @@ public class AbstractStandardVirtualHostNodeTest extends QpidTestCase assertEquals("Unexpected virtual host id", virtualHostId, virtualHost.getId()); } - /** * Tests activating a virtualhostnode with a config store which does not specify * a virtualhost. Checks no virtualhost is created. @@ -128,7 +132,6 @@ public class AbstractStandardVirtualHostNodeTest extends QpidTestCase VirtualHost<?, ?, ?> virtualHost = node.getVirtualHost(); assertNull("Virtual host should not be automatically created", virtualHost); - } /** @@ -233,6 +236,125 @@ public class AbstractStandardVirtualHostNodeTest extends QpidTestCase assertEquals("Unexpected virtual host id", virtualHostId, virtualHost.getId()); } + public void testStopStartVHN() throws Exception + { + DurableConfigurationStore configStore = configStoreThatProducesNoRecords(); + + Map<String, Object> nodeAttributes = new HashMap<>(); + nodeAttributes.put(VirtualHostNode.NAME, TEST_VIRTUAL_HOST_NODE_NAME); + nodeAttributes.put(VirtualHostNode.ID, _nodeId); + + VirtualHostNode<?> node = new TestVirtualHostNode(_broker, nodeAttributes, configStore); + node.open(); + node.start(); + + assertEquals("Unexpected virtual host node state", State.ACTIVE, node.getState()); + + node.stop(); + assertEquals("Unexpected virtual host node state after stop", State.STOPPED, node.getState()); + + node.start(); + assertEquals("Unexpected virtual host node state after start", State.ACTIVE, node.getState()); + } + + + // *************** VHN Access Control Tests *************** + + public void testUpdateVHNDeniedByACL() throws Exception + { + when(_broker.getSecurityManager()).thenReturn(_mockSecurityManager); + + DurableConfigurationStore configStore = configStoreThatProducesNoRecords(); + + Map<String, Object> nodeAttributes = new HashMap<>(); + nodeAttributes.put(VirtualHostNode.NAME, TEST_VIRTUAL_HOST_NODE_NAME); + nodeAttributes.put(VirtualHostNode.ID, _nodeId); + + VirtualHostNode<?> node = new TestVirtualHostNode(_broker, nodeAttributes, configStore); + node.open(); + node.start(); + + doThrow(new AccessControlException("mocked ACL exception")).when(_mockSecurityManager).authoriseVirtualHostNode( + TEST_VIRTUAL_HOST_NODE_NAME, + Operation.UPDATE); + + assertNull(node.getDescription()); + try + { + node.setAttribute(VirtualHostNode.DESCRIPTION, null, "My virtualhost node"); + fail("Exception not throws"); + } + catch (AccessControlException ace) + { + // PASS + } + assertNull("Description unexpected updated", node.getDescription()); + } + + public void testDeleteVHNDeniedByACL() throws Exception + { + SecurityManager mockSecurityManager = mock(SecurityManager.class); + when(_broker.getSecurityManager()).thenReturn(mockSecurityManager); + + DurableConfigurationStore configStore = configStoreThatProducesNoRecords(); + + Map<String, Object> nodeAttributes = new HashMap<>(); + nodeAttributes.put(VirtualHostNode.NAME, TEST_VIRTUAL_HOST_NODE_NAME); + nodeAttributes.put(VirtualHostNode.ID, _nodeId); + + VirtualHostNode<?> node = new TestVirtualHostNode(_broker, nodeAttributes, configStore); + node.open(); + node.start(); + + doThrow(new AccessControlException("mocked ACL exception")).when(mockSecurityManager).authoriseVirtualHostNode( + TEST_VIRTUAL_HOST_NODE_NAME, + Operation.DELETE); + + try + { + node.delete(); + fail("Exception not throws"); + } + catch (AccessControlException ace) + { + // PASS + } + + assertEquals("Virtual host node state changed unexpectedly", State.ACTIVE, node.getState()); + } + + public void testStopVHNDeniedByACL() throws Exception + { + SecurityManager mockSecurityManager = mock(SecurityManager.class); + when(_broker.getSecurityManager()).thenReturn(mockSecurityManager); + + DurableConfigurationStore configStore = configStoreThatProducesNoRecords(); + + Map<String, Object> nodeAttributes = new HashMap<>(); + nodeAttributes.put(VirtualHostNode.NAME, TEST_VIRTUAL_HOST_NODE_NAME); + nodeAttributes.put(VirtualHostNode.ID, _nodeId); + + VirtualHostNode<?> node = new TestVirtualHostNode(_broker, nodeAttributes, configStore); + node.open(); + node.start(); + + doThrow(new AccessControlException("mocked ACL exception")).when(mockSecurityManager).authoriseVirtualHostNode( + TEST_VIRTUAL_HOST_NODE_NAME, + Operation.UPDATE); + + try + { + node.stop(); + fail("Exception not throws"); + } + catch (AccessControlException ace) + { + // PASS + } + + assertEquals("Virtual host node state changed unexpectedly", State.ACTIVE, node.getState()); + } + private ConfiguredObjectRecord createVirtualHostConfiguredObjectRecord(UUID virtualHostId) { Map<String, Object> virtualHostAttributes = new HashMap<>(); @@ -263,6 +385,7 @@ public class AbstractStandardVirtualHostNodeTest extends QpidTestCase } }; } + private NullMessageStore configStoreThatProducesNoRecords() { return configStoreThatProduces(null); diff --git a/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java index 0ce2555bcf..a37c0c7858 100644 --- a/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java +++ b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java @@ -108,6 +108,13 @@ public class RuleSetTest extends QpidTestCase assertEquals(_ruleSet.getDefault(), _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); } + public void testVirtualHostNodeCreateAllowPermissionWithVirtualHostName() throws Exception + { + _ruleSet.grant(0, TEST_USER, Permission.ALLOW, Operation.CREATE, ObjectType.VIRTUALHOSTNODE, ObjectProperties.EMPTY); + assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.VIRTUALHOSTNODE, ObjectProperties.EMPTY)); + assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.DELETE, ObjectType.VIRTUALHOSTNODE, ObjectProperties.EMPTY)); + } + public void testVirtualHostAccessAllowPermissionWithVirtualHostName() throws Exception { _ruleSet.grant(0, TEST_USER, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ALLOWED_VH)); diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java index a7874155df..8c4effd685 100644 --- a/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java @@ -27,7 +27,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.apache.qpid.server.virtualhostnode.JsonVirtualHostNode; import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.map.JsonMappingException; import org.apache.qpid.server.management.plugin.HttpManagement; @@ -45,7 +44,6 @@ import org.apache.qpid.server.model.adapter.FileBasedGroupProvider; import org.apache.qpid.server.model.adapter.FileBasedGroupProviderImpl; import org.apache.qpid.server.security.FileKeyStore; import org.apache.qpid.server.security.FileTrustStore; -import org.apache.qpid.server.model.VirtualHostNode; import org.apache.qpid.server.security.access.FileAccessControlProviderConstants; import org.apache.qpid.server.security.acl.AbstractACLTestCase; import org.apache.qpid.server.security.auth.manager.AnonymousAuthenticationManager; @@ -72,7 +70,7 @@ public class BrokerACLTest extends QpidRestTestCase "ACL DENY-LOG " + DENIED_USER + " CONFIGURE BROKER", "ACL DENY-LOG ALL ALL"); - _secondaryAclFileContent = + _secondaryAclFileContent = "ACL ALLOW-LOG ALL ACCESS MANAGEMENT\n" + "ACL ALLOW-LOG " + ALLOWED_USER + " CONFIGURE BROKER\n" + "ACL DENY-LOG " + DENIED_USER + " CONFIGURE BROKER\n" + @@ -190,58 +188,6 @@ public class BrokerACLTest extends QpidRestTestCase provider.get(ExternalFileBasedAuthenticationManager.PATH)); } - /* === VirtualHostNode === */ - - public void testCreateVirtualHostNodeAllowed() throws Exception - { - getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); - - String hostName = getTestName(); - - int responseCode = createVirtualHostNode(hostName); - assertEquals("Host creation should be allowed", 201, responseCode); - - assertVirtualHostNodeExists(hostName); - } - - public void testCreateVirtualHostNodeDenied() throws Exception - { - getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); - - String hostName = getTestName(); - - int responseCode = createVirtualHostNode(hostName); - assertEquals("Virtual host node creation should be denied", 403, responseCode); - - assertVirtualHostNodeDoesNotExist(hostName); - } - - public void testDeleteVirtualHostNodeAllowed() throws Exception - { - getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); - - assertVirtualHostNodeExists(TEST2_VIRTUALHOST); - - int responseCode = getRestTestHelper().submitRequest("virtualhostnode/" + TEST2_VIRTUALHOST, "DELETE"); - assertEquals("Virtual host node deletion should be allowed", 200, responseCode); - - assertVirtualHostNodeDoesNotExist(TEST2_VIRTUALHOST); - } - - public void testDeleteVirtualHostNodeDenied() throws Exception - { - getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); - - assertVirtualHostNodeExists(TEST2_VIRTUALHOST); - - getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); - - int responseCode = getRestTestHelper().submitRequest("virtualhostnode/" + TEST2_VIRTUALHOST, "DELETE"); - assertEquals("Virtual host node deletion should be denied", 403, responseCode); - - assertVirtualHostNodeExists(TEST2_VIRTUALHOST); - } - /* === Port === */ public void testCreatePortAllowed() throws Exception @@ -977,37 +923,6 @@ public class BrokerACLTest extends QpidRestTestCase assertEquals("Unexpected result", exists, !trustStores.isEmpty()); } - private int createVirtualHostNode(String virtualHostNodeName) throws Exception - { - Map<String, Object> data = new HashMap<String, Object>(); - data.put(VirtualHostNode.NAME, virtualHostNodeName); - data.put(VirtualHostNode.TYPE, getTestProfileVirtualHostNodeType()); - data.put(JsonVirtualHostNode.STORE_PATH, getStoreLocation(virtualHostNodeName)); - - return getRestTestHelper().submitRequest("virtualhostnode/" + virtualHostNodeName, "PUT", data); - } - - private void assertVirtualHostNodeDoesNotExist(String name) throws Exception - { - assertVirtualHostNodeExistence(name, false); - } - - private void assertVirtualHostNodeExists(String name) throws Exception - { - assertVirtualHostNodeExistence(name, true); - } - - private void assertVirtualHostNodeExistence(String name, boolean exists) throws Exception - { - List<Map<String, Object>> hosts = getRestTestHelper().getJsonAsList("virtualhostnode/" + name); - assertEquals("Node " + name + (exists ? " does not exist" : " exists" ), 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>(); diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/VirtualHostACLTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/VirtualHostACLTest.java new file mode 100644 index 0000000000..45123325e3 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/VirtualHostACLTest.java @@ -0,0 +1,145 @@ +/* + * 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.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.server.model.Plugin; +import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.model.VirtualHostNode; +import org.apache.qpid.server.security.acl.AbstractACLTestCase; +import org.apache.qpid.server.virtualhost.ProvidedStoreVirtualHostImpl; +import org.apache.qpid.server.virtualhostnode.JsonVirtualHostNode; +import org.apache.qpid.systest.rest.QpidRestTestCase; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class VirtualHostACLTest extends QpidRestTestCase +{ + private static final String VHN_WITHOUT_VH = "myVhnWithoutVh"; + + private static final String ALLOWED_USER = "user1"; + private static final String DENIED_USER = "user2"; + + @Override + protected void customizeConfiguration() throws IOException + { + super.customizeConfiguration(); + getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); + + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + "ACL ALLOW-LOG " + ALLOWED_USER + " ALL VIRTUALHOST", + "ACL DENY-LOG " + DENIED_USER + " ALL VIRTUALHOST", + "ACL DENY-LOG ALL ALL"); + + getBrokerConfiguration().setObjectAttribute(Plugin.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, + HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED, true); + + Map<String, Object> virtualHostNodeAttributes = new HashMap<>(); + virtualHostNodeAttributes.put(VirtualHostNode.NAME, VHN_WITHOUT_VH); + virtualHostNodeAttributes.put(VirtualHostNode.TYPE, getTestProfileVirtualHostNodeType()); + // TODO need better way to determine the VHN's optional attributes + virtualHostNodeAttributes.put(JsonVirtualHostNode.STORE_PATH, getStoreLocation(VHN_WITHOUT_VH)); + + getBrokerConfiguration().addObjectConfiguration(VirtualHostNode.class, virtualHostNodeAttributes); + } + + public void testCreateVirtualHostAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String hostName = getTestName(); + + int responseCode = createVirtualHost(VHN_WITHOUT_VH, hostName); + assertEquals("Virtual host creation should be allowed", HttpServletResponse.SC_CREATED, responseCode); + + assertVirtualHostExists(VHN_WITHOUT_VH, hostName); + } + + public void testCreateVirtualHostDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + String hostName = getTestName(); + + int responseCode = createVirtualHost(VHN_WITHOUT_VH, hostName); + assertEquals("Virtual host creation should be denied", HttpServletResponse.SC_FORBIDDEN, responseCode); + + assertVirtualHostDoesNotExist(VHN_WITHOUT_VH, hostName); + } + + public void testDeleteVirtualHostDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + getRestTestHelper().submitRequest("virtualhost/" + TEST2_VIRTUALHOST + "/" + TEST2_VIRTUALHOST, "DELETE", HttpServletResponse.SC_FORBIDDEN); + + assertVirtualHostExists(TEST2_VIRTUALHOST, TEST2_VIRTUALHOST); + } + + public void testUpdateVirtualHostDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + Map<String, Object> attributes = new HashMap<>(); + attributes.put(VirtualHost.NAME, TEST2_VIRTUALHOST); + attributes.put(VirtualHost.DESCRIPTION, "new description"); + + getRestTestHelper().submitRequest("virtualhost/" + TEST2_VIRTUALHOST + "/" + TEST2_VIRTUALHOST, "PUT", attributes, HttpServletResponse.SC_FORBIDDEN); + } + + /* === Utility Methods === */ + + private int createVirtualHost(final String testVirtualHostNode, String virtualHostName) throws Exception + { + Map<String, Object> data = new HashMap<>(); + data.put(VirtualHost.NAME, virtualHostName); + data.put(VirtualHost.TYPE, ProvidedStoreVirtualHostImpl.VIRTUAL_HOST_TYPE); + + return getRestTestHelper().submitRequest("virtualhost/" + testVirtualHostNode + "/" + virtualHostName, "PUT", data); + } + + private void assertVirtualHostDoesNotExist(final String virtualHostNodeName, String virtualHostName) throws Exception + { + assertVirtualHostExistence(virtualHostNodeName, virtualHostName, false); + } + + private void assertVirtualHostExists(final String virtualHostNodeName, String virtualHostName) throws Exception + { + assertVirtualHostExistence(virtualHostNodeName, virtualHostName, true); + } + + private void assertVirtualHostExistence(final String virtualHostNodeName, String virtualHostName, boolean exists) throws Exception + { + List<Map<String, Object>> hosts = getRestTestHelper().getJsonAsList("virtualhost/" + virtualHostNodeName + "/" + virtualHostName); + assertEquals("Node " + virtualHostName + (exists ? " does not exist" : " exists"), exists, !hosts.isEmpty()); + } + + private String getStoreLocation(String hostName) + { + return new File(TMP_FOLDER, "store-" + hostName + "-" + System.currentTimeMillis()).getAbsolutePath(); + } + +} diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/VirtualHostNodeACLTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/VirtualHostNodeACLTest.java new file mode 100644 index 0000000000..4809962f24 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/VirtualHostNodeACLTest.java @@ -0,0 +1,155 @@ +/* + * 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 javax.servlet.http.HttpServletResponse; + +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.map.JsonMappingException; + +import org.apache.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.server.model.AccessControlProvider; +import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.ExternalFileBasedAuthenticationManager; +import org.apache.qpid.server.model.GroupProvider; +import org.apache.qpid.server.model.KeyStore; +import org.apache.qpid.server.model.Plugin; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.Protocol; +import org.apache.qpid.server.model.TrustStore; +import org.apache.qpid.server.model.VirtualHostNode; +import org.apache.qpid.server.model.adapter.FileBasedGroupProvider; +import org.apache.qpid.server.model.adapter.FileBasedGroupProviderImpl; +import org.apache.qpid.server.security.FileKeyStore; +import org.apache.qpid.server.security.FileTrustStore; +import org.apache.qpid.server.security.access.FileAccessControlProviderConstants; +import org.apache.qpid.server.security.acl.AbstractACLTestCase; +import org.apache.qpid.server.security.auth.manager.AnonymousAuthenticationManager; +import org.apache.qpid.server.security.auth.manager.PlainPasswordDatabaseAuthenticationManager; +import org.apache.qpid.server.virtualhost.memory.MemoryVirtualHost; +import org.apache.qpid.server.virtualhostnode.JsonVirtualHostNode; +import org.apache.qpid.systest.rest.QpidRestTestCase; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.test.utils.TestFileUtils; +import org.apache.qpid.test.utils.TestSSLConstants; + +public class VirtualHostNodeACLTest extends QpidRestTestCase +{ + private static final String TEST_VIRTUAL_HOST_NODE = "myTestVirtualHostNode"; + private static final String ALLOWED_USER = "user1"; + private static final String DENIED_USER = "user2"; + + @Override + protected void customizeConfiguration() throws IOException + { + super.customizeConfiguration(); + getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); + + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + "ACL ALLOW-LOG " + ALLOWED_USER + " ALL VIRTUALHOSTNODE", + "ACL DENY-LOG " + DENIED_USER + " ALL VIRTUALHOSTNODE", + "ACL DENY-LOG ALL ALL"); + + getBrokerConfiguration().setObjectAttribute(Plugin.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, + HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED, true); + + Map<String, Object> virtualHostNodeAttributes = new HashMap<>(); + virtualHostNodeAttributes.put(VirtualHostNode.NAME, TEST_VIRTUAL_HOST_NODE); + virtualHostNodeAttributes.put(VirtualHostNode.TYPE, getTestProfileVirtualHostNodeType()); + // TODO need better way to determine the VHN's optional attributes + virtualHostNodeAttributes.put(JsonVirtualHostNode.STORE_PATH, getStoreLocation(TEST_VIRTUAL_HOST_NODE)); + + + getBrokerConfiguration().addObjectConfiguration(VirtualHostNode.class, virtualHostNodeAttributes); + } + + public void testCreateVirtualHostNodeAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String hostName = getTestName(); + + int responseCode = createVirtualHostNode(hostName); + assertEquals("Virtual host node creation should be allowed", HttpServletResponse.SC_CREATED, responseCode); + + assertVirtualHostNodeExists(hostName); + } + + public void testCreateVirtualHostNodeDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + String hostName = getTestName(); + + int responseCode = createVirtualHostNode(hostName); + assertEquals("Virtual host node creation should be denied", HttpServletResponse.SC_FORBIDDEN, responseCode); + + assertVirtualHostNodeDoesNotExist(hostName); + } + + public void testDeleteVirtualHostNodeDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + getRestTestHelper().submitRequest("virtualhostnode/" + TEST_VIRTUAL_HOST_NODE, "DELETE", HttpServletResponse.SC_FORBIDDEN); + + assertVirtualHostNodeExists(TEST_VIRTUAL_HOST_NODE); + } + + /* === Utility Methods === */ + + private int createVirtualHostNode(String virtualHostNodeName) throws Exception + { + Map<String, Object> data = new HashMap<>(); + data.put(VirtualHostNode.NAME, virtualHostNodeName); + data.put(VirtualHostNode.TYPE, getTestProfileVirtualHostNodeType()); + data.put(JsonVirtualHostNode.STORE_PATH, getStoreLocation(virtualHostNodeName)); + + return getRestTestHelper().submitRequest("virtualhostnode/" + virtualHostNodeName, "PUT", data); + } + + private void assertVirtualHostNodeDoesNotExist(String name) throws Exception + { + assertVirtualHostNodeExistence(name, false); + } + + private void assertVirtualHostNodeExists(String name) throws Exception + { + assertVirtualHostNodeExistence(name, true); + } + + private void assertVirtualHostNodeExistence(String name, boolean exists) throws Exception + { + List<Map<String, Object>> hosts = getRestTestHelper().getJsonAsList("virtualhostnode/" + name); + assertEquals("Node " + name + (exists ? " does not exist" : " exists"), exists, !hosts.isEmpty()); + } + + private String getStoreLocation(String hostName) + { + return new File(TMP_FOLDER, "store-" + hostName + "-" + System.currentTimeMillis()).getAbsolutePath(); + } + +} |