diff options
Diffstat (limited to 'java/systests/src/main/java/org/apache/qpid/systest/rest')
22 files changed, 4194 insertions, 0 deletions
diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/Asserts.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/Asserts.java new file mode 100644 index 0000000000..16253139ce --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/Asserts.java @@ -0,0 +1,270 @@ +/* + * + * 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; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; + +import javax.jms.JMSException; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.server.model.Binding; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.Connection; +import org.apache.qpid.server.model.Exchange; +import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.Protocol; +import org.apache.qpid.server.model.Queue; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.VirtualHost; + +public class Asserts +{ + public static final String STATISTICS_ATTRIBUTE = "statistics"; + + public static void assertVirtualHost(String virtualHostName, Map<String, Object> virtualHost) + { + assertNotNull("Virtualhost " + virtualHostName + " data are not found", virtualHost); + assertAttributesPresent(virtualHost, VirtualHost.AVAILABLE_ATTRIBUTES, VirtualHost.TIME_TO_LIVE, + VirtualHost.CREATED, VirtualHost.UPDATED, VirtualHost.SUPPORTED_QUEUE_TYPES, VirtualHost.STORE_PATH, VirtualHost.CONFIG_PATH); + + assertEquals("Unexpected value of attribute " + VirtualHost.NAME, virtualHostName, virtualHost.get(VirtualHost.NAME)); + assertNotNull("Unexpected value of attribute " + VirtualHost.ID, virtualHost.get(VirtualHost.ID)); + assertEquals("Unexpected value of attribute " + VirtualHost.STATE, State.ACTIVE.name(), + virtualHost.get(VirtualHost.STATE)); + assertEquals("Unexpected value of attribute " + VirtualHost.DURABLE, Boolean.TRUE, + virtualHost.get(VirtualHost.DURABLE)); + assertEquals("Unexpected value of attribute " + VirtualHost.LIFETIME_POLICY, LifetimePolicy.PERMANENT.name(), + virtualHost.get(VirtualHost.LIFETIME_POLICY)); + assertEquals("Unexpected value of attribute " + VirtualHost.DEAD_LETTER_QUEUE_ENABLED, Boolean.FALSE, + virtualHost.get(VirtualHost.DEAD_LETTER_QUEUE_ENABLED)); + + @SuppressWarnings("unchecked") + Collection<String> exchangeTypes = (Collection<String>) virtualHost.get(VirtualHost.SUPPORTED_EXCHANGE_TYPES); + assertEquals("Unexpected value of attribute " + VirtualHost.SUPPORTED_EXCHANGE_TYPES, + new HashSet<String>(Arrays.asList("headers", "topic", "direct", "fanout")), + new HashSet<String>(exchangeTypes)); + + @SuppressWarnings("unchecked") + Map<String, Object> statistics = (Map<String, Object>) virtualHost.get(STATISTICS_ATTRIBUTE); + Asserts.assertAttributesPresent(statistics, VirtualHost.AVAILABLE_STATISTICS, VirtualHost.BYTES_RETAINED, + VirtualHost.LOCAL_TRANSACTION_BEGINS, VirtualHost.LOCAL_TRANSACTION_ROLLBACKS, + VirtualHost.MESSAGES_RETAINED, VirtualHost.STATE_CHANGED, VirtualHost.XA_TRANSACTION_BRANCH_ENDS, + VirtualHost.XA_TRANSACTION_BRANCH_STARTS, VirtualHost.XA_TRANSACTION_BRANCH_SUSPENDS); + + } + + public static void assertQueue(String queueName, String queueType, Map<String, Object> queueData) + { + assertQueue(queueName, queueType, queueData, null); + } + + public static void assertQueue(String queueName, String queueType, Map<String, Object> queueData, Map<String, Object> expectedAttributes) + { + assertNotNull("Queue " + queueName + " is not found!", queueData); + Asserts.assertAttributesPresent(queueData, Queue.AVAILABLE_ATTRIBUTES, Queue.CREATED, Queue.UPDATED, + Queue.DESCRIPTION, Queue.TIME_TO_LIVE, Queue.ALTERNATE_EXCHANGE, Queue.OWNER, Queue.NO_LOCAL, Queue.LVQ_KEY, + Queue.SORT_KEY, Queue.MESSAGE_GROUP_KEY, Queue.MESSAGE_GROUP_DEFAULT_GROUP, + Queue.MESSAGE_GROUP_SHARED_GROUPS, Queue.PRIORITIES); + + assertEquals("Unexpected value of queue attribute " + Queue.NAME, queueName, queueData.get(Queue.NAME)); + assertNotNull("Unexpected value of queue attribute " + Queue.ID, queueData.get(Queue.ID)); + assertEquals("Unexpected value of queue attribute " + Queue.STATE, State.ACTIVE.name(), queueData.get(Queue.STATE)); + assertEquals("Unexpected value of queue attribute " + Queue.LIFETIME_POLICY, LifetimePolicy.PERMANENT.name(), + queueData.get(Queue.LIFETIME_POLICY)); + assertEquals("Unexpected value of queue attribute " + Queue.TYPE, queueType, queueData.get(Queue.TYPE)); + if (expectedAttributes == null) + { + assertEquals("Unexpected value of queue attribute " + Queue.EXCLUSIVE, Boolean.FALSE, queueData.get(Queue.EXCLUSIVE)); + assertEquals("Unexpected value of queue attribute " + Queue.MAXIMUM_DELIVERY_ATTEMPTS, 0, + queueData.get(Queue.MAXIMUM_DELIVERY_ATTEMPTS)); + assertEquals("Unexpected value of queue attribute " + Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES, 0, + queueData.get(Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES)); + assertEquals("Unexpected value of queue attribute " + Queue.QUEUE_FLOW_RESUME_SIZE_BYTES, 0, + queueData.get(Queue.QUEUE_FLOW_RESUME_SIZE_BYTES)); + assertEquals("Unexpected value of queue attribute " + Queue.QUEUE_FLOW_STOPPED, Boolean.FALSE, + queueData.get(Queue.QUEUE_FLOW_STOPPED)); + } + else + { + for (Map.Entry<String, Object> attribute : expectedAttributes.entrySet()) + { + assertEquals("Unexpected value of " + queueName + " queue attribute " + attribute.getKey(), + attribute.getValue(), queueData.get(attribute.getKey())); + } + } + + assertNotNull("Unexpected value of queue attribute statistics", queueData.get(Asserts.STATISTICS_ATTRIBUTE)); + @SuppressWarnings("unchecked") + Map<String, Object> statistics = (Map<String, Object>) queueData.get(Asserts.STATISTICS_ATTRIBUTE); + Asserts.assertAttributesPresent(statistics, Queue.AVAILABLE_STATISTICS, Queue.DISCARDS_TTL_BYTES, + Queue.DISCARDS_TTL_MESSAGES, Queue.STATE_CHANGED); + } + + public static void assertAttributesPresent(Map<String, Object> data, String[] attributes) + { + for (String name : attributes) + { + assertNotNull("Attribute " + name + " is not present", data.get(name)); + } + } + + public static void assertAttributesPresent(Map<String, Object> data, Collection<String> attributes, + String... unsupportedAttributes) + { + for (String name : attributes) + { + boolean unsupported = false; + for (String unsupportedAttribute : unsupportedAttributes) + { + if (unsupportedAttribute.equals(name)) + { + unsupported = true; + break; + } + } + if (unsupported) + { + continue; + } + assertNotNull("Attribute " + name + " is not present", data.get(name)); + } + } + + public static void assertConnection(Map<String, Object> connectionData, AMQConnection connection) throws JMSException + { + assertNotNull("Unexpected connection data", connectionData); + assertAttributesPresent(connectionData, Connection.AVAILABLE_ATTRIBUTES, Connection.STATE, Connection.DURABLE, + Connection.LIFETIME_POLICY, Connection.TIME_TO_LIVE, Connection.CREATED, Connection.UPDATED, + Connection.INCOMING, Connection.REMOTE_PROCESS_NAME, Connection.REMOTE_PROCESS_PID, + Connection.LOCAL_ADDRESS, Connection.PROPERTIES); + + assertEquals("Unexpected value of connection attribute " + Connection.SESSION_COUNT_LIMIT, + (int) connection.getMaximumChannelCount(), connectionData.get(Connection.SESSION_COUNT_LIMIT)); + assertEquals("Unexpected value of connection attribute " + Connection.CLIENT_ID, "clientid", + connectionData.get(Connection.CLIENT_ID)); + assertEquals("Unexpected value of connection attribute " + Connection.PRINCIPAL, "guest", + connectionData.get(Connection.PRINCIPAL)); + + @SuppressWarnings("unchecked") + Map<String, Object> statistics = (Map<String, Object>) connectionData.get(STATISTICS_ATTRIBUTE); + assertAttributesPresent(statistics, Connection.AVAILABLE_STATISTICS, Connection.LOCAL_TRANSACTION_BEGINS, + Connection.LOCAL_TRANSACTION_ROLLBACKS, Connection.STATE_CHANGED, Connection.XA_TRANSACTION_BRANCH_ENDS, + Connection.XA_TRANSACTION_BRANCH_STARTS, Connection.XA_TRANSACTION_BRANCH_SUSPENDS); + assertEquals("Unexpected value of connection statistics attribute " + Connection.SESSION_COUNT, 1, + statistics.get(Connection.SESSION_COUNT)); + } + + public static void assertPortAttributes(Map<String, Object> port) + { + + assertNotNull("Unexpected value of attribute " + Port.ID, port.get(Port.ID)); + assertEquals("Unexpected value of attribute " + Port.DURABLE, Boolean.FALSE, port.get(Port.DURABLE)); + assertEquals("Unexpected value of attribute " + Port.LIFETIME_POLICY, LifetimePolicy.PERMANENT.name(), + port.get(Broker.LIFETIME_POLICY)); + assertEquals("Unexpected value of attribute " + Port.STATE, State.ACTIVE.name(), port.get(Port.STATE)); + assertEquals("Unexpected value of attribute " + Port.TIME_TO_LIVE, 0, port.get(Port.TIME_TO_LIVE)); + + @SuppressWarnings("unchecked") + Collection<String> protocols = (Collection<String>) port.get(Port.PROTOCOLS); + assertNotNull("Unexpected value of attribute " + Port.PROTOCOLS, protocols); + boolean isAMQPPort = false; + for (String protocolName : protocols) + { + if (Protocol.valueOf(protocolName).isAMQP()) + { + isAMQPPort = true; + break; + } + } + if (isAMQPPort) + { + assertAttributesPresent(port, Port.AVAILABLE_ATTRIBUTES, Port.CREATED, Port.UPDATED, Port.AUTHENTICATION_MANAGER); + assertNotNull("Unexpected value of attribute " + Port.BINDING_ADDRESS, port.get(Port.BINDING_ADDRESS)); + } + else + { + assertAttributesPresent(port, Port.AVAILABLE_ATTRIBUTES, Port.CREATED, Port.UPDATED, Port.AUTHENTICATION_MANAGER, + Port.BINDING_ADDRESS, Port.TCP_NO_DELAY, Port.SEND_BUFFER_SIZE, Port.RECEIVE_BUFFER_SIZE, + Port.NEED_CLIENT_AUTH, Port.WANT_CLIENT_AUTH); + } + + @SuppressWarnings("unchecked") + Collection<String> transports = (Collection<String>) port.get(Port.TRANSPORTS); + assertEquals("Unexpected value of attribute " + Port.TRANSPORTS, new HashSet<String>(Arrays.asList("TCP")), + new HashSet<String>(transports)); + } + + public static void assertDurableExchange(String exchangeName, String type, Map<String, Object> exchangeData) + { + assertExchange(exchangeName, type, exchangeData); + + assertEquals("Unexpected value of exchange attribute " + Exchange.DURABLE, Boolean.TRUE, + exchangeData.get(Exchange.DURABLE)); + } + + public static void assertExchange(String exchangeName, String type, Map<String, Object> exchangeData) + { + assertNotNull("Exchange " + exchangeName + " is not found!", exchangeData); + assertAttributesPresent(exchangeData, Exchange.AVAILABLE_ATTRIBUTES, Exchange.CREATED, Exchange.UPDATED, + Exchange.ALTERNATE_EXCHANGE, Exchange.TIME_TO_LIVE); + + assertEquals("Unexpected value of exchange attribute " + Exchange.NAME, exchangeName, + exchangeData.get(Exchange.NAME)); + assertNotNull("Unexpected value of exchange attribute " + Exchange.ID, exchangeData.get(VirtualHost.ID)); + assertEquals("Unexpected value of exchange attribute " + Exchange.STATE, State.ACTIVE.name(), + exchangeData.get(Exchange.STATE)); + + assertEquals("Unexpected value of exchange attribute " + Exchange.LIFETIME_POLICY, LifetimePolicy.PERMANENT.name(), + exchangeData.get(Exchange.LIFETIME_POLICY)); + assertEquals("Unexpected value of exchange attribute " + Exchange.TYPE, type, exchangeData.get(Exchange.TYPE)); + assertNotNull("Unexpected value of exchange attribute statistics", exchangeData.get(STATISTICS_ATTRIBUTE)); + + @SuppressWarnings("unchecked") + Map<String, Object> statistics = (Map<String, Object>) exchangeData.get(STATISTICS_ATTRIBUTE); + assertAttributesPresent(statistics, Exchange.AVAILABLE_STATISTICS, Exchange.STATE_CHANGED, Exchange.PRODUCER_COUNT); + } + + public static void assertBinding(String bindingName, String queueName, String exchange, Map<String, Object> binding) + { + assertNotNull("Binding map should not be null", binding); + assertAttributesPresent(binding, Binding.AVAILABLE_ATTRIBUTES, Binding.STATE, Binding.TIME_TO_LIVE, + Binding.CREATED, Binding.UPDATED); + + assertEquals("Unexpected binding attribute " + Binding.NAME, bindingName, binding.get(Binding.NAME)); + assertEquals("Unexpected binding attribute " + Binding.QUEUE, queueName, binding.get(Binding.QUEUE)); + assertEquals("Unexpected binding attribute " + Binding.EXCHANGE, exchange, binding.get(Binding.EXCHANGE)); + assertEquals("Unexpected binding attribute " + Binding.LIFETIME_POLICY, LifetimePolicy.PERMANENT.name(), + binding.get(Binding.LIFETIME_POLICY)); + } + + public static void assertBinding(String queueName, String exchange, Map<String, Object> binding) + { + assertBinding(queueName, queueName, exchange, binding); + } + +} diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/AuthenticationProviderRestTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/AuthenticationProviderRestTest.java new file mode 100644 index 0000000000..a171b4459b --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/AuthenticationProviderRestTest.java @@ -0,0 +1,73 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.systest.rest; + +import java.util.List; +import java.util.Map; + +import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.User; + +public class AuthenticationProviderRestTest extends QpidRestTestCase +{ + + public void testGet() throws Exception + { + List<Map<String, Object>> providerDetails = getRestTestHelper().getJsonAsList("/rest/authenticationprovider"); + assertNotNull("Providers details cannot be null", providerDetails); + assertEquals("Unexpected number of providers", 1, providerDetails.size()); + for (Map<String, Object> provider : providerDetails) + { + assertProvider("PrincipalDatabaseAuthenticationManager", provider); + Map<String, Object> data = getRestTestHelper().getJsonAsSingletonList("/rest/authenticationprovider/" + + provider.get(AuthenticationProvider.NAME)); + assertNotNull("Cannot load data for " + provider.get(AuthenticationProvider.NAME), data); + assertProvider("PrincipalDatabaseAuthenticationManager", data); + } + } + + private void assertProvider(String type, Map<String, Object> provider) + { + Asserts.assertAttributesPresent(provider, AuthenticationProvider.AVAILABLE_ATTRIBUTES, + AuthenticationProvider.CREATED, AuthenticationProvider.UPDATED, AuthenticationProvider.DESCRIPTION, + AuthenticationProvider.TIME_TO_LIVE); + assertEquals("Unexpected value of provider attribute " + AuthenticationProvider.STATE, State.ACTIVE.name(), + provider.get(AuthenticationProvider.STATE)); + assertEquals("Unexpected value of provider attribute " + AuthenticationProvider.LIFETIME_POLICY, + LifetimePolicy.PERMANENT.name(), provider.get(AuthenticationProvider.LIFETIME_POLICY)); + assertEquals("Unexpected value of provider attribute " + AuthenticationProvider.DURABLE, Boolean.TRUE, + provider.get(AuthenticationProvider.DURABLE)); + assertEquals("Unexpected value of provider attribute " + AuthenticationProvider.TYPE, type, + provider.get(AuthenticationProvider.TYPE)); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> users = (List<Map<String, Object>>) provider.get("users"); + assertNotNull("Users are not found", users); + assertTrue("Unexpected number of users", users.size() > 1); + for (Map<String, Object> user : users) + { + assertNotNull("Attribute " + User.ID, user.get(User.ID)); + assertNotNull("Attribute " + User.NAME, user.get(User.NAME)); + } + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/BasicAuthRestTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/BasicAuthRestTest.java new file mode 100644 index 0000000000..0574b6cc24 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/BasicAuthRestTest.java @@ -0,0 +1,121 @@ +/* + * + * 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; + +import static org.apache.qpid.test.utils.TestSSLConstants.TRUSTSTORE; +import static org.apache.qpid.test.utils.TestSSLConstants.TRUSTSTORE_PASSWORD; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.util.Collections; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.Protocol; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class BasicAuthRestTest extends QpidRestTestCase +{ + private static final String USERNAME = "admin"; + + @Override + public void setUp() throws Exception + { + setSystemProperty("javax.net.debug", "ssl"); + + //don't call super method, we will configure the broker in the test before doing so + } + + @Override + protected void customizeConfiguration() throws ConfigurationException, IOException + { + //do nothing, we will configure this locally + } + + private void configure(boolean useSsl) throws ConfigurationException, IOException + { + getRestTestHelper().setUseSsl(useSsl); + if (useSsl) + { + getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, Port.PROTOCOLS, Collections.singleton(Protocol.HTTPS)); + } + super.customizeConfiguration(); + } + + private void verifyGetBrokerAttempt(int responseCode) throws IOException + { + HttpURLConnection conn = getRestTestHelper().openManagementConnection("/rest/broker", "GET"); + assertEquals(responseCode, conn.getResponseCode()); + } + + public void testDefaultEnabledWithHttps() throws Exception + { + configure(true); + super.setUp(); + setSystemProperty("javax.net.ssl.trustStore", TRUSTSTORE); + setSystemProperty("javax.net.ssl.trustStorePassword", TRUSTSTORE_PASSWORD); + + // Try the attempt with authentication, it should succeed because + // BASIC auth is enabled by default on secure connections. + getRestTestHelper().setUsernameAndPassword(USERNAME, USERNAME); + verifyGetBrokerAttempt(HttpServletResponse.SC_OK); + } + + public void testDefaultDisabledWithHttp() throws Exception + { + configure(false); + super.setUp(); + + // Try the attempt with authentication, it should fail because + // BASIC auth is disabled by default on non-secure connections. + getRestTestHelper().setUsernameAndPassword(USERNAME, USERNAME); + verifyGetBrokerAttempt(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + + public void testEnablingForHttp() throws Exception + { + configure(false); + + getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, "httpBasicAuthenticationEnabled", true); + super.setUp(); + + // Try the attempt with authentication, it should succeed because + // BASIC auth is now enabled on non-secure connections. + getRestTestHelper().setUsernameAndPassword(USERNAME, USERNAME); + verifyGetBrokerAttempt(HttpServletResponse.SC_OK); + } + + public void testDisablingForHttps() throws Exception + { + configure(true); + getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, "httpsBasicAuthenticationEnabled", false); + super.setUp(); + setSystemProperty("javax.net.ssl.trustStore", TRUSTSTORE); + setSystemProperty("javax.net.ssl.trustStorePassword", TRUSTSTORE_PASSWORD); + + // Try the attempt with authentication, it should fail because + // BASIC auth is now disabled on secure connections. + getRestTestHelper().setUsernameAndPassword(USERNAME, USERNAME); + verifyGetBrokerAttempt(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } +} 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 new file mode 100644 index 0000000000..372db8f560 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/BindingRestTest.java @@ -0,0 +1,130 @@ +/* + * + * 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; + +import java.net.HttpURLConnection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.qpid.server.model.Binding; + +public class BindingRestTest extends QpidRestTestCase +{ + + public void testGetAllBindings() throws Exception + { + List<Map<String, Object>> bindings = getRestTestHelper().getJsonAsList("/rest/binding"); + assertNotNull("Bindings cannot be null", bindings); + assertTrue("Unexpected number of bindings: " + bindings.size(), + bindings.size() >= EXPECTED_VIRTUALHOSTS.length * EXPECTED_QUEUES.length); + for (Map<String, Object> binding : bindings) + { + Asserts.assertBinding((String) binding.get(Binding.NAME), (String) binding.get(Binding.EXCHANGE), binding); + } + } + + public void testGetVirtualHostBindings() throws Exception + { + List<Map<String, Object>> bindings = getRestTestHelper().getJsonAsList("/rest/binding/test"); + assertNotNull("Bindings cannot be null", bindings); + assertEquals("Unexpected number of bindings", EXPECTED_QUEUES.length * 2, bindings.size()); + for (String queueName : EXPECTED_QUEUES) + { + Map<String, Object> searchAttributes = new HashMap<String, Object>(); + searchAttributes.put(Binding.NAME, queueName); + searchAttributes.put(Binding.EXCHANGE, "amq.direct"); + + Map<String, Object> binding = getRestTestHelper().find(searchAttributes, bindings); + Asserts.assertBinding(queueName, "amq.direct", binding); + + searchAttributes.put(Binding.EXCHANGE, "<<default>>"); + + binding = getRestTestHelper().find(searchAttributes, bindings); + Asserts.assertBinding(queueName, "<<default>>", binding); + } + } + + public void testGetVirtualHostExchangeBindings() throws Exception + { + List<Map<String, Object>> bindings = getRestTestHelper().getJsonAsList("/rest/binding/test/amq.direct"); + assertNotNull("Bindings cannot be null", bindings); + assertEquals("Unexpected number of bindings", EXPECTED_QUEUES.length, bindings.size()); + for (String queueName : EXPECTED_QUEUES) + { + Map<String, Object> binding = getRestTestHelper().find(Binding.NAME, queueName, bindings); + Asserts.assertBinding(queueName, "amq.direct", binding); + } + } + + public void testGetVirtualHostExchangeQueueBindings() throws Exception + { + List<Map<String, Object>> bindings = getRestTestHelper().getJsonAsList("/rest/binding/test/amq.direct/queue"); + assertNotNull("Bindings cannot be null", bindings); + assertEquals("Unexpected number of bindings", 1, bindings.size()); + Asserts.assertBinding("queue", "amq.direct", bindings.get(0)); + } + + + public void testDeleteBinding() throws Exception + { + List<Map<String, Object>> bindings = getRestTestHelper().getJsonAsList("/rest/binding/test/amq.direct/queue/queue"); + assertEquals("Unexpected number of bindings", 1, bindings.size()); + Asserts.assertBinding("queue", "amq.direct", bindings.get(0)); + + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/binding/test/amq.direct/queue/queue", "DELETE"); + connection.connect(); + assertEquals("Unexpected response code", 200, connection.getResponseCode()); + + bindings = getRestTestHelper().getJsonAsList("/rest/binding/test/amq.direct/queue/queue"); + assertEquals("Binding should be deleted", 0, bindings.size()); + } + + public void testDeleteBindingById() throws Exception + { + Map<String, Object> binding = getRestTestHelper().getJsonAsSingletonList("/rest/binding/test/amq.direct/queue"); + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/binding/test/amq.direct?id=" + binding.get(Binding.ID), "DELETE"); + connection.connect(); + assertEquals("Unexpected response code", 200, connection.getResponseCode()); + List<Map<String, Object>> bindings = getRestTestHelper().getJsonAsList("/rest/binding/test/amq.direct/queue"); + assertEquals("Binding should be deleted", 0, bindings.size()); + } + + public void testCreateBinding() throws Exception + { + String bindingName = getTestName(); + Map<String, Object> bindingData = new HashMap<String, Object>(); + bindingData.put(Binding.NAME, bindingName); + bindingData.put(Binding.QUEUE, "queue"); + bindingData.put(Binding.EXCHANGE, "amq.direct"); + + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/binding/test/amq.direct/queue/" + bindingName, "PUT"); + connection.connect(); + getRestTestHelper().writeJsonRequest(connection, bindingData); + int responseCode = connection.getResponseCode(); + connection.disconnect(); + 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); + } + +} diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/BrokerRestHttpsTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/BrokerRestHttpsTest.java new file mode 100644 index 0000000000..13e0c1e819 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/BrokerRestHttpsTest.java @@ -0,0 +1,69 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.systest.rest; + +import static org.apache.qpid.test.utils.TestSSLConstants.TRUSTSTORE; +import static org.apache.qpid.test.utils.TestSSLConstants.TRUSTSTORE_PASSWORD; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.configuration.ConfigurationException; +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.Transport; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class BrokerRestHttpsTest extends QpidRestTestCase +{ + @Override + public void setUp() throws Exception + { + setSystemProperty("javax.net.debug", "ssl"); + super.setUp(); + setSystemProperty("javax.net.ssl.trustStore", TRUSTSTORE); + setSystemProperty("javax.net.ssl.trustStorePassword", TRUSTSTORE_PASSWORD); + } + + @Override + protected void customizeConfiguration() throws ConfigurationException, IOException + { + super.customizeConfiguration(); + getRestTestHelper().setUseSsl(true); + Map<String, Object> newAttributes = new HashMap<String, Object>(); + newAttributes.put(Port.PROTOCOLS, Collections.singleton(Protocol.HTTPS)); + newAttributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); + getBrokerConfiguration().setObjectAttributes(TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT,newAttributes); + } + + public void testGetWithHttps() throws Exception + { + Map<String, Object> brokerDetails = getRestTestHelper().getJsonAsSingletonList("/rest/broker"); + + Asserts.assertAttributesPresent(brokerDetails, Broker.AVAILABLE_ATTRIBUTES, Broker.BYTES_RETAINED, + Broker.PROCESS_PID, Broker.SUPPORTED_STORE_TYPES, Broker.CREATED, Broker.TIME_TO_LIVE, Broker.UPDATED, + Broker.ACL_FILE, Broker.KEY_STORE_CERT_ALIAS, Broker.TRUST_STORE_PATH, Broker.TRUST_STORE_PASSWORD, + Broker.GROUP_FILE); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/BrokerRestTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/BrokerRestTest.java new file mode 100644 index 0000000000..d479d39287 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/BrokerRestTest.java @@ -0,0 +1,120 @@ +/* + * + * 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; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import org.apache.qpid.common.QpidProperties; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class BrokerRestTest extends QpidRestTestCase +{ + + private static final String BROKER_AUTHENTICATIONPROVIDERS_ATTRIBUTE = "authenticationproviders"; + private static final String BROKER_PORTS_ATTRIBUTE = "ports"; + private static final String BROKER_VIRTUALHOSTS_ATTRIBUTE = "virtualhosts"; + private static final String BROKER_STATISTICS_ATTRIBUTE = "statistics"; + + public void testGet() throws Exception + { + Map<String, Object> brokerDetails = getRestTestHelper().getJsonAsSingletonList("/rest/broker"); + + assertBrokerAttributes(brokerDetails); + + @SuppressWarnings("unchecked") + Map<String, Object> statistics = (Map<String, Object>) brokerDetails.get(BROKER_STATISTICS_ATTRIBUTE); + Asserts.assertAttributesPresent(statistics, new String[]{ "bytesIn", "messagesOut", "bytesOut", "messagesIn" }); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> virtualhosts = (List<Map<String, Object>>) brokerDetails.get(BROKER_VIRTUALHOSTS_ATTRIBUTE); + assertEquals("Unexpected number of virtual hosts", 3, virtualhosts.size()); + + Asserts.assertVirtualHost(TEST3_VIRTUALHOST, getRestTestHelper().find(VirtualHost.NAME, TEST3_VIRTUALHOST, virtualhosts)); + Asserts.assertVirtualHost(TEST2_VIRTUALHOST, getRestTestHelper().find(VirtualHost.NAME, TEST2_VIRTUALHOST, virtualhosts)); + Asserts.assertVirtualHost(TEST1_VIRTUALHOST, getRestTestHelper().find(VirtualHost.NAME, TEST1_VIRTUALHOST, virtualhosts)); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> ports = (List<Map<String, Object>>) brokerDetails.get(BROKER_PORTS_ATTRIBUTE); + assertEquals("Unexpected number of ports", 4, ports.size()); + + for (Map<String, Object> port : ports) + { + Asserts.assertPortAttributes(port); + } + + Map<String, Object> amqpPort = getRestTestHelper().find(Port.NAME, TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT, ports); + Map<String, Object> httpPort = getRestTestHelper().find(Port.NAME, TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, ports); + + assertEquals("Unexpected binding address", "*", amqpPort.get(Port.BINDING_ADDRESS)); + assertNotNull("Cannot find AMQP port", amqpPort); + assertNotNull("Cannot find HTTP port", httpPort); + + @SuppressWarnings("unchecked") + Collection<String> port1Protocols = (Collection<String>) amqpPort.get(Port.PROTOCOLS); + assertFalse("AMQP protocol list cannot contain HTTP", port1Protocols.contains("HTTP")); + + @SuppressWarnings("unchecked") + Collection<String> port2Protocols = (Collection<String>) httpPort.get(Port.PROTOCOLS); + assertEquals("Unexpected value of attribute " + Port.PROTOCOLS, new HashSet<String>(Arrays.asList("HTTP")), + new HashSet<String>(port2Protocols)); + } + + protected void assertBrokerAttributes(Map<String, Object> brokerDetails) + { + Asserts.assertAttributesPresent(brokerDetails, Broker.AVAILABLE_ATTRIBUTES, + Broker.BYTES_RETAINED, Broker.PROCESS_PID, Broker.SUPPORTED_STORE_TYPES, + Broker.CREATED, Broker.TIME_TO_LIVE, Broker.UPDATED, Broker.ACL_FILE, + Broker.KEY_STORE_PATH, Broker.KEY_STORE_PASSWORD, Broker.KEY_STORE_CERT_ALIAS, + Broker.TRUST_STORE_PATH, Broker.TRUST_STORE_PASSWORD, Broker.GROUP_FILE); + + assertEquals("Unexpected value of attribute " + Broker.BUILD_VERSION, QpidProperties.getBuildVersion(), + brokerDetails.get(Broker.BUILD_VERSION)); + assertEquals("Unexpected value of attribute " + Broker.OPERATING_SYSTEM, System.getProperty("os.name") + " " + + System.getProperty("os.version") + " " + System.getProperty("os.arch"), + brokerDetails.get(Broker.OPERATING_SYSTEM)); + assertEquals( + "Unexpected value of attribute " + Broker.PLATFORM, + System.getProperty("java.vendor") + " " + + System.getProperty("java.runtime.version", System.getProperty("java.version")), + brokerDetails.get(Broker.PLATFORM)); + assertEquals("Unexpected value of attribute " + Broker.DURABLE, Boolean.TRUE, brokerDetails.get(Broker.DURABLE)); + assertEquals("Unexpected value of attribute " + Broker.LIFETIME_POLICY, LifetimePolicy.PERMANENT.name(), + brokerDetails.get(Broker.LIFETIME_POLICY)); + assertEquals("Unexpected value of attribute " + Broker.NAME, "QpidBroker", brokerDetails.get(Broker.NAME)); + assertEquals("Unexpected value of attribute " + Broker.STATE, State.ACTIVE.name(), brokerDetails.get(Broker.STATE)); + + assertNotNull("Unexpected value of attribute " + Broker.ID, brokerDetails.get(Broker.ID)); + assertNotNull("Unexpected value of attribute statistics", brokerDetails.get(BROKER_STATISTICS_ATTRIBUTE)); + assertNotNull("Unexpected value of attribute virtualhosts", brokerDetails.get(BROKER_VIRTUALHOSTS_ATTRIBUTE)); + assertNotNull("Unexpected value of attribute ports", brokerDetails.get(BROKER_PORTS_ATTRIBUTE)); + assertNotNull("Unexpected value of attribute authenticationproviders", brokerDetails.get(BROKER_AUTHENTICATIONPROVIDERS_ATTRIBUTE)); + } + +} diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/ConnectionRestTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/ConnectionRestTest.java new file mode 100644 index 0000000000..05c8e362a1 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/ConnectionRestTest.java @@ -0,0 +1,213 @@ +/* + * + * 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; + +import java.io.IOException; +import java.net.URLDecoder; +import java.util.List; +import java.util.Map; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.server.model.Connection; +import org.apache.qpid.server.model.Session; + +public class ConnectionRestTest extends QpidRestTestCase +{ + /** + * Message number to publish into queue + */ + private static final int MESSAGE_NUMBER = 5; + private static final int MESSAGE_SIZE = 6; + + private static final String SESSIONS_ATTRIBUTE = "sessions"; + + private javax.jms.Connection _connection; + private javax.jms.Session _session; + + public void setUp() throws Exception + { + super.setUp(); + + _connection = getConnection(); + _session = _connection.createSession(true, javax.jms.Session.SESSION_TRANSACTED); + String queueName = getTestQueueName(); + Destination queue = _session.createQueue(queueName); + MessageConsumer consumer = _session.createConsumer(queue); + MessageProducer producer = _session.createProducer(queue); + _connection.start(); + + // send messages + for (int i = 0; i < MESSAGE_NUMBER; i++) + { + producer.send(_session.createTextMessage("Test-" + i)); + } + _session.commit(); + + Message m = consumer.receive(1000l); + assertNotNull("Message was not received", m); + _session.commit(); + + // receive the rest of messages for rollback + for (int i = 0; i < MESSAGE_NUMBER - 1; i++) + { + m = consumer.receive(1000l); + assertNotNull("Message was not received", m); + } + _session.rollback(); + + // receive them again + for (int i = 0; i < MESSAGE_NUMBER - 1; i++) + { + m = consumer.receive(1000l); + assertNotNull("Message was not received", m); + } + } + + public void testGetAllConnections() throws Exception + { + List<Map<String, Object>> connections = getRestTestHelper().getJsonAsList("/rest/connection"); + assertEquals("Unexpected number of connections", 1, connections.size()); + Asserts.assertConnection(connections.get(0), (AMQConnection) _connection); + } + + public void testGetVirtualHostConnections() throws Exception + { + List<Map<String, Object>> connections = getRestTestHelper().getJsonAsList("/rest/connection/test"); + assertEquals("Unexpected number of connections", 1, connections.size()); + Asserts.assertConnection(connections.get(0), (AMQConnection) _connection); + } + + public void testGetConnectionByName() throws Exception + { + // get connection name + String connectionName = getConnectionName(); + + Map<String, Object> connectionDetails = getRestTestHelper().getJsonAsSingletonList("/rest/connection/test/" + + URLDecoder.decode(connectionName, "UTF-8")); + assertConnection(connectionDetails); + } + + public void testGetAllSessions() throws Exception + { + List<Map<String, Object>> sessions = getRestTestHelper().getJsonAsList("/rest/session"); + assertEquals("Unexpected number of sessions", 1, sessions.size()); + assertSession(sessions.get(0), (AMQSession<?, ?>) _session); + } + + public void testGetVirtualHostSessions() throws Exception + { + List<Map<String, Object>> sessions = getRestTestHelper().getJsonAsList("/rest/session/test"); + assertEquals("Unexpected number of sessions", 1, sessions.size()); + assertSession(sessions.get(0), (AMQSession<?, ?>) _session); + } + + public void testGetConnectionSessions() throws Exception + { + // get connection name + String connectionName = getConnectionName(); + + List<Map<String, Object>> sessions = getRestTestHelper().getJsonAsList("/rest/session/test/" + + URLDecoder.decode(connectionName, "UTF-8")); + assertEquals("Unexpected number of sessions", 1, sessions.size()); + assertSession(sessions.get(0), (AMQSession<?, ?>) _session); + } + + public void testGetSessionByName() throws Exception + { + // get connection name + String connectionName = getConnectionName(); + + List<Map<String, Object>> sessions = getRestTestHelper().getJsonAsList("/rest/session/test/" + + URLDecoder.decode(connectionName, "UTF-8") + "/" + ((AMQSession<?, ?>) _session).getChannelId()); + assertEquals("Unexpected number of sessions", 1, sessions.size()); + assertSession(sessions.get(0), (AMQSession<?, ?>) _session); + } + + private void assertConnection(Map<String, Object> connectionDetails) throws JMSException + { + Asserts.assertConnection(connectionDetails, (AMQConnection) _connection); + + @SuppressWarnings("unchecked") + Map<String, Object> statistics = (Map<String, Object>) connectionDetails.get(Asserts.STATISTICS_ATTRIBUTE); + assertEquals("Unexpected value of connection statistics attribute " + Connection.BYTES_IN, MESSAGE_NUMBER + * MESSAGE_SIZE, statistics.get(Connection.BYTES_IN)); + assertEquals("Unexpected value of connection statistics attribute " + Connection.BYTES_OUT, MESSAGE_SIZE + + ((MESSAGE_NUMBER - 1) * MESSAGE_SIZE) * 2, statistics.get(Connection.BYTES_OUT)); + assertEquals("Unexpected value of connection statistics attribute " + Connection.MESSAGES_IN, MESSAGE_NUMBER, + statistics.get(Connection.MESSAGES_IN)); + assertEquals("Unexpected value of connection statistics attribute " + Connection.MESSAGES_OUT, + MESSAGE_NUMBER * 2 - 1, statistics.get(Connection.MESSAGES_OUT)); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> sessions = (List<Map<String, Object>>) connectionDetails.get(SESSIONS_ATTRIBUTE); + assertNotNull("Sessions cannot be found", sessions); + assertEquals("Unexpected number of sessions", 1, sessions.size()); + assertSession(sessions.get(0), (AMQSession<?, ?>) _session); + } + + private void assertSession(Map<String, Object> sessionData, AMQSession<?, ?> session) + { + assertNotNull("Session map cannot be null", sessionData); + Asserts.assertAttributesPresent(sessionData, Session.AVAILABLE_ATTRIBUTES, Session.STATE, Session.DURABLE, + Session.LIFETIME_POLICY, Session.TIME_TO_LIVE, Session.CREATED, Session.UPDATED); + assertEquals("Unexpecte value of attribute " + Session.NAME, session.getChannelId() + "", + sessionData.get(Session.NAME)); + assertEquals("Unexpecte value of attribute " + Session.PRODUCER_FLOW_BLOCKED, Boolean.FALSE, + sessionData.get(Session.PRODUCER_FLOW_BLOCKED)); + assertEquals("Unexpecte value of attribute " + Session.CHANNEL_ID, session.getChannelId(), + sessionData.get(Session.CHANNEL_ID)); + + @SuppressWarnings("unchecked") + Map<String, Object> statistics = (Map<String, Object>) sessionData.get(Asserts.STATISTICS_ATTRIBUTE); + Asserts.assertAttributesPresent(statistics, Session.AVAILABLE_STATISTICS, Session.BYTES_IN, Session.BYTES_OUT, + Session.STATE_CHANGED, Session.UNACKNOWLEDGED_BYTES, Session.LOCAL_TRANSACTION_OPEN, + Session.XA_TRANSACTION_BRANCH_ENDS, Session.XA_TRANSACTION_BRANCH_STARTS, + Session.XA_TRANSACTION_BRANCH_SUSPENDS); + + assertEquals("Unexpecte value of statistic attribute " + Session.UNACKNOWLEDGED_MESSAGES, MESSAGE_NUMBER - 1, + statistics.get(Session.UNACKNOWLEDGED_MESSAGES)); + assertEquals("Unexpecte value of statistic attribute " + Session.LOCAL_TRANSACTION_BEGINS, 4, + statistics.get(Session.LOCAL_TRANSACTION_BEGINS)); + assertEquals("Unexpecte value of statistic attribute " + Session.LOCAL_TRANSACTION_ROLLBACKS, 1, + statistics.get(Session.LOCAL_TRANSACTION_ROLLBACKS)); + assertEquals("Unexpecte value of statistic attribute " + Session.CONSUMER_COUNT, 1, + statistics.get(Session.CONSUMER_COUNT)); + } + + private String getConnectionName() throws IOException + { + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); + @SuppressWarnings("unchecked") + List<Map<String, Object>> connections = (List<Map<String, Object>>) hostDetails + .get(VirtualHostRestTest.VIRTUALHOST_CONNECTIONS_ATTRIBUTE); + assertEquals("Unexpected number of connections", 1, connections.size()); + Map<String, Object> connection = connections.get(0); + String connectionName = (String) connection.get(Connection.NAME); + return connectionName; + } +} 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 new file mode 100644 index 0000000000..ec9791db13 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/ExchangeRestTest.java @@ -0,0 +1,87 @@ +/* + * + * 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; + +import java.net.URLDecoder; +import java.util.List; +import java.util.Map; + +import org.apache.qpid.server.model.Binding; +import org.apache.qpid.server.model.Exchange; + +public class ExchangeRestTest extends QpidRestTestCase +{ + public void testGet() throws Exception + { + List<Map<String, Object>> exchanges = getRestTestHelper().getJsonAsList("/rest/exchange"); + assertNotNull("Exchanges cannot be null", exchanges); + assertTrue("Unexpected number of exchanges", exchanges.size() >= EXPECTED_VIRTUALHOSTS.length * EXPECTED_EXCHANGES.length); + for (Map<String, Object> exchange : exchanges) + { + Asserts.assertExchange((String) exchange.get(Exchange.NAME), (String) exchange.get(Exchange.TYPE), exchange); + } + } + + public void testGetHostExchanges() throws Exception + { + List<Map<String, Object>> exchanges = getRestTestHelper().getJsonAsList("/rest/exchange/test"); + assertNotNull("Users cannot be null", exchanges); + assertEquals("Unexpected number of exchanges", exchanges.size(), EXPECTED_EXCHANGES.length); + for (String exchangeName : EXPECTED_EXCHANGES) + { + Map<String, Object> exchange = getRestTestHelper().find(Exchange.NAME, exchangeName, exchanges); + assertExchange(exchangeName, exchange); + } + } + + public void testGetHostExchangeByName() throws Exception + { + for (String exchangeName : EXPECTED_EXCHANGES) + { + Map<String, Object> exchange = getRestTestHelper().getJsonAsSingletonList("/rest/exchange/test/" + + URLDecoder.decode(exchangeName, "UTF-8")); + assertExchange(exchangeName, exchange); + } + } + + private void assertExchange(String exchangeName, Map<String, Object> exchange) + { + assertNotNull("Exchange with name " + exchangeName + " is not found", exchange); + String type = (String) exchange.get(Exchange.TYPE); + Asserts.assertExchange(exchangeName, type, exchange); + if ("direct".equals(type)) + { + assertBindings(exchange); + } + } + + private void assertBindings(Map<String, Object> exchange) + { + @SuppressWarnings("unchecked") + List<Map<String, Object>> bindings = (List<Map<String, Object>>) exchange.get("bindings"); + for (String queueName : EXPECTED_QUEUES) + { + Map<String, Object> binding = getRestTestHelper().find(Binding.NAME, queueName, bindings); + Asserts.assertBinding(queueName, (String) exchange.get(Exchange.NAME), binding); + } + } + +} diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/GroupProviderRestTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/GroupProviderRestTest.java new file mode 100644 index 0000000000..861bf8cb71 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/GroupProviderRestTest.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.systest.rest; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.Group; +import org.apache.qpid.server.model.GroupProvider; +import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.UUIDGenerator; + +public class GroupProviderRestTest extends QpidRestTestCase +{ + private static final String FILE_GROUP_MANAGER = "FileGroupManager"; + private File _groupFile; + + @Override + public void setUp() throws Exception + { + _groupFile = createTemporaryGroupFile(); + + getBrokerConfiguration().setBrokerAttribute(Broker.GROUP_FILE, _groupFile.getAbsolutePath()); + + super.setUp(); + } + + @Override + public void tearDown() throws Exception + { + super.tearDown(); + + if (_groupFile != null) + { + if (_groupFile.exists()) + { + _groupFile.delete(); + } + } + } + + public void testGet() throws Exception + { + List<Map<String, Object>> providerDetails = getRestTestHelper().getJsonAsList("/rest/groupprovider"); + assertNotNull("Providers details cannot be null", providerDetails); + assertEquals("Unexpected number of providers", 1, providerDetails.size()); + for (Map<String, Object> provider : providerDetails) + { + assertProvider(FILE_GROUP_MANAGER, provider); + Map<String, Object> data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + + provider.get(GroupProvider.NAME)); + assertNotNull("Cannot load data for " + provider.get(GroupProvider.NAME), data); + assertProvider(FILE_GROUP_MANAGER, data); + } + } + + public void testCreateNewGroup() throws Exception + { + String groupName = "newGroup"; + + Map<String, Object> data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + FILE_GROUP_MANAGER); + assertNotNull("Cannot load data for provider", data); + + getRestTestHelper().assertNumberOfGroups(data, 1); + + getRestTestHelper().createGroup(groupName, FILE_GROUP_MANAGER); + + data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + FILE_GROUP_MANAGER); + assertNotNull("Cannot load data for provider", data); + + getRestTestHelper().assertNumberOfGroups(data, 2); + } + + public void testRemoveGroup() throws Exception + { + String groupName = "myGroup"; + + Map<String, Object> data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + FILE_GROUP_MANAGER); + assertNotNull("Cannot load data for provider", data); + + getRestTestHelper().assertNumberOfGroups(data, 1); + + getRestTestHelper().removeGroup(groupName, FILE_GROUP_MANAGER); + + data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + FILE_GROUP_MANAGER); + assertNotNull("Cannot load data for provider", data); + + getRestTestHelper().assertNumberOfGroups(data, 0); + } + + + private void assertProvider(String type, Map<String, Object> provider) + { + Asserts.assertAttributesPresent(provider, GroupProvider.AVAILABLE_ATTRIBUTES, + GroupProvider.CREATED, GroupProvider.UPDATED, GroupProvider.DESCRIPTION, + GroupProvider.TIME_TO_LIVE); + assertEquals("Unexpected value of provider attribute " + GroupProvider.STATE, State.ACTIVE.name(), + provider.get(GroupProvider.STATE)); + assertEquals("Unexpected value of provider attribute " + GroupProvider.LIFETIME_POLICY, + LifetimePolicy.PERMANENT.name(), provider.get(GroupProvider.LIFETIME_POLICY)); + assertEquals("Unexpected value of provider attribute " + GroupProvider.DURABLE, Boolean.TRUE, + provider.get(GroupProvider.DURABLE)); + assertEquals("Unexpected value of provider attribute " + GroupProvider.TYPE, type, + provider.get(GroupProvider.TYPE)); + + final String name = (String) provider.get(GroupProvider.NAME); + assertEquals("Unexpected value of provider attribute " + GroupProvider.NAME, type, + name); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> groups = (List<Map<String, Object>>) provider.get("groups"); + assertNotNull("Groups were not found", groups); + assertEquals("Unexpected number of groups", 1, groups.size()); + for (Map<String, Object> group : groups) + { + + final String groupName = (String) group.get(Group.NAME); + assertNotNull("Attribute " + Group.NAME, groupName); + + assertNotNull("Attribute " + Group.ID, group.get(Group.ID)); + assertEquals("Attribute " + Group.ID, UUIDGenerator.generateGroupUUID(name, groupName).toString(), group.get(Group.ID)); + } + } + + private File createTemporaryGroupFile() throws Exception + { + File groupFile = File.createTempFile("group", "grp"); + groupFile.deleteOnExit(); + + Properties props = new Properties(); + props.put("myGroup.users", "guest"); + + props.store(new FileOutputStream(groupFile), "test group file"); + + return groupFile; + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/GroupRestTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/GroupRestTest.java new file mode 100644 index 0000000000..d3f93cc0fe --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/GroupRestTest.java @@ -0,0 +1,109 @@ +/* + * + * 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; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.GroupMember; + +public class GroupRestTest extends QpidRestTestCase +{ + private static final String GROUP_NAME = "myGroup"; + private static final String FILE_GROUP_MANAGER = "FileGroupManager"; + private static final String EXISTING_MEMBER = "user1"; + private static final String NEW_MEMBER = "user2"; + + private File _groupFile; + + @Override + public void setUp() throws Exception + { + _groupFile = createTemporaryGroupFile(); + + getBrokerConfiguration().setBrokerAttribute(Broker.GROUP_FILE, _groupFile.getAbsolutePath()); + + super.setUp(); + } + + @Override + public void tearDown() throws Exception + { + super.tearDown(); + + if (_groupFile != null) + { + if (_groupFile.exists()) + { + _groupFile.delete(); + } + } + } + + public void testGet() throws Exception + { + Map<String, Object> group = getRestTestHelper().getJsonAsSingletonList("/rest/group/FileGroupManager/myGroup"); + List<Map<String, Object>> groupmembers = (List<Map<String, Object>>) group.get("groupmembers"); + assertEquals(1, groupmembers.size()); + + Map<String, Object> member1 = groupmembers.get(0); + assertEquals(EXISTING_MEMBER, (String)member1.get(GroupMember.NAME)); + } + + public void testCreateNewMemberOfGroup() throws Exception + { + Map<String, Object> group = getRestTestHelper().getJsonAsSingletonList("/rest/group/FileGroupManager/myGroup"); + getRestTestHelper().assertNumberOfGroupMembers(group, 1); + + getRestTestHelper().createNewGroupMember(FILE_GROUP_MANAGER, GROUP_NAME, NEW_MEMBER); + + group = getRestTestHelper().getJsonAsSingletonList("/rest/group/FileGroupManager/myGroup"); + getRestTestHelper().assertNumberOfGroupMembers(group, 2); + } + + public void testRemoveMemberFromGroup() throws Exception + { + Map<String, Object> group = getRestTestHelper().getJsonAsSingletonList("/rest/group/FileGroupManager/myGroup"); + getRestTestHelper().assertNumberOfGroupMembers(group, 1); + + getRestTestHelper().removeMemberFromGroup(FILE_GROUP_MANAGER, GROUP_NAME, EXISTING_MEMBER); + + group = getRestTestHelper().getJsonAsSingletonList("/rest/group/FileGroupManager/myGroup"); + getRestTestHelper().assertNumberOfGroupMembers(group, 0); + } + + private File createTemporaryGroupFile() throws Exception + { + File groupFile = File.createTempFile("group", "grp"); + groupFile.deleteOnExit(); + + Properties props = new Properties(); + props.put(GROUP_NAME + ".users", EXISTING_MEMBER); + + props.store(new FileOutputStream(groupFile), "test group file"); + + return groupFile; + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/LogRecordsRestTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/LogRecordsRestTest.java new file mode 100644 index 0000000000..a2f9d3189c --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/LogRecordsRestTest.java @@ -0,0 +1,42 @@ +/* + * + * 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; + +import java.util.List; +import java.util.Map; + +public class LogRecordsRestTest extends QpidRestTestCase +{ + public void testGet() throws Exception + { + List<Map<String, Object>> logs = getRestTestHelper().getJsonAsList("/rest/logrecords"); + assertNotNull("Logs data cannot be null", logs); + assertTrue("Logs are not found", logs.size() > 0); + Map<String, Object> record = getRestTestHelper().find("message", "[Broker] BRK-1004 : Qpid Broker Ready", logs); + + assertNotNull("BRK-1004 message is not found", record); + assertNotNull("Message id cannot be null", record.get("id")); + assertNotNull("Message timestamp cannot be null", record.get("timestamp")); + assertEquals("Unexpected log level", "INFO", record.get("level")); + assertEquals("Unexpected thread", "main", record.get("thread")); + assertEquals("Unexpected logger", "qpid.message.broker.ready", record.get("logger")); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/MessagesRestTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/MessagesRestTest.java new file mode 100644 index 0000000000..fb6bfca1d8 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/MessagesRestTest.java @@ -0,0 +1,354 @@ +/* + * + * 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; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import javax.jms.BytesMessage; +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.Message; +import javax.jms.MessageProducer; +import javax.jms.Session; + +import org.codehaus.jackson.JsonParseException; +import org.codehaus.jackson.map.JsonMappingException; + +public class MessagesRestTest extends QpidRestTestCase +{ + + /** + * Message number to publish into queue + */ + private static final int MESSAGE_NUMBER = 12; + + private Connection _connection; + private Session _session; + private MessageProducer _producer; + private long _startTime; + private long _ttl; + + public void setUp() throws Exception + { + super.setUp(); + _startTime = System.currentTimeMillis(); + _connection = getConnection(); + _session = _connection.createSession(true, Session.SESSION_TRANSACTED); + String queueName = getTestQueueName(); + Destination queue = _session.createQueue(queueName); + _session.createConsumer(queue); + _producer = _session.createProducer(queue); + + _ttl = TimeUnit.DAYS.toMillis(1); + for (int i = 0; i < MESSAGE_NUMBER; i++) + { + Message m = _session.createTextMessage("Test-" + i); + m.setIntProperty("index", i); + if (i % 2 == 0) + { + _producer.send(m); + } + else + { + _producer.send(m, DeliveryMode.NON_PERSISTENT, 5, _ttl); + } + } + _session.commit(); + } + + public void testGet() throws Exception + { + String queueName = getTestQueueName(); + List<Map<String, Object>> messages = getRestTestHelper().getJsonAsList("/rest/message/test/" + queueName); + assertNotNull("Messages are not found", messages); + assertEquals("Unexpected number of messages", MESSAGE_NUMBER, messages.size()); + int position = 0; + for (Map<String, Object> message : messages) + { + assertMessage(position, message); + position++; + } + } + + public void testGetMessageContent() throws Exception + { + String queueName = getTestQueueName(); + + // add bytes message + BytesMessage byteMessage = _session.createBytesMessage(); + byte[] messageBytes = "Test".getBytes(); + byteMessage.writeBytes(messageBytes); + byteMessage.setStringProperty("test", "value"); + _producer.send(byteMessage); + _session.commit(); + + // get message IDs + List<Long> ids = getMesssageIds(queueName); + + Map<String, Object> message = getRestTestHelper().getJsonAsMap("/rest/message/test/" + queueName + "/" + ids.get(0)); + assertMessageAttributes(message); + assertMessageAttributeValues(message, true); + + @SuppressWarnings("unchecked") + Map<String, Object> headers = (Map<String, Object>) message.get("headers"); + assertNotNull("Message headers are not found", headers); + assertEquals("Unexpected message header", 0, headers.get("index")); + + Long lastMessageId = ids.get(ids.size() - 1); + message = getRestTestHelper().getJsonAsMap("/rest/message/test/" + queueName + "/" + lastMessageId); + assertMessageAttributes(message); + assertEquals("Unexpected message attribute mimeType", "application/octet-stream", message.get("mimeType")); + assertEquals("Unexpected message attribute size", 4, message.get("size")); + + @SuppressWarnings("unchecked") + Map<String, Object> bytesMessageHeader = (Map<String, Object>) message.get("headers"); + assertNotNull("Message headers are not found", bytesMessageHeader); + assertEquals("Unexpected message header", "value", bytesMessageHeader.get("test")); + + // get content + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/message-content/test/" + queueName + "/" + + lastMessageId, "GET"); + connection.connect(); + byte[] data = getRestTestHelper().readConnectionInputStream(connection); + assertTrue("Unexpected message", Arrays.equals(messageBytes, data)); + + } + + public void testPostMoveMessages() throws Exception + { + String queueName = getTestQueueName(); + String queueName2 = queueName + "_2"; + Destination queue2 = _session.createQueue(queueName2); + _session.createConsumer(queue2); + + // get message IDs + List<Long> ids = getMesssageIds(queueName); + + // move half of the messages + int movedNumber = ids.size() / 2; + List<Long> movedMessageIds = new ArrayList<Long>(); + for (int i = 0; i < movedNumber; i++) + { + movedMessageIds.add(ids.remove(i)); + } + + // move messages + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/message/test/" + queueName, "POST"); + + Map<String, Object> messagesData = new HashMap<String, Object>(); + messagesData.put("messages", movedMessageIds); + messagesData.put("destinationQueue", queueName2); + messagesData.put("move", Boolean.TRUE); + + getRestTestHelper().writeJsonRequest(connection, messagesData); + assertEquals("Unexpected response code", 200, connection.getResponseCode()); + + // check messages on target queue + List<Map<String, Object>> messages = getRestTestHelper().getJsonAsList("/rest/message/test/" + queueName2); + assertNotNull("Messages are not found", messages); + assertEquals("Unexpected number of messages", movedMessageIds.size(), messages.size()); + for (Long id : movedMessageIds) + { + Map<String, Object> message = getRestTestHelper().find("id", id.intValue(), messages); + assertMessageAttributes(message); + } + + // check messages on original queue + messages = getRestTestHelper().getJsonAsList("/rest/message/test/" + queueName); + assertNotNull("Messages are not found", messages); + assertEquals("Unexpected number of messages", ids.size(), messages.size()); + for (Long id : ids) + { + Map<String, Object> message = getRestTestHelper().find("id", id.intValue(), messages); + assertMessageAttributes(message); + } + for (Long id : movedMessageIds) + { + Map<String, Object> message = getRestTestHelper().find("id", id.intValue(), messages); + assertNull("Moved message " + id + " is found on original queue", message); + } + } + + public void testPostCopyMessages() throws Exception + { + String queueName = getTestQueueName(); + String queueName2 = queueName + "_2"; + Destination queue2 = _session.createQueue(queueName2); + _session.createConsumer(queue2); + + // get message IDs + List<Long> ids = getMesssageIds(queueName); + + // copy half of the messages + int copyNumber = ids.size() / 2; + List<Long> copyMessageIds = new ArrayList<Long>(); + for (int i = 0; i < copyNumber; i++) + { + copyMessageIds.add(ids.remove(i)); + } + + // copy messages + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/message/test/" + queueName, "POST"); + + Map<String, Object> messagesData = new HashMap<String, Object>(); + messagesData.put("messages", copyMessageIds); + messagesData.put("destinationQueue", queueName2); + + getRestTestHelper().writeJsonRequest(connection, messagesData); + assertEquals("Unexpected response code", 200, connection.getResponseCode()); + + // check messages on target queue + List<Map<String, Object>> messages = getRestTestHelper().getJsonAsList("/rest/message/test/" + queueName2); + assertNotNull("Messages are not found", messages); + assertEquals("Unexpected number of messages", copyMessageIds.size(), messages.size()); + for (Long id : copyMessageIds) + { + Map<String, Object> message = getRestTestHelper().find("id", id.intValue(), messages); + assertMessageAttributes(message); + } + + // check messages on original queue + messages = getRestTestHelper().getJsonAsList("/rest/message/test/" + queueName); + assertNotNull("Messages are not found", messages); + assertEquals("Unexpected number of messages", MESSAGE_NUMBER, messages.size()); + for (Long id : ids) + { + Map<String, Object> message = getRestTestHelper().find("id", id.intValue(), messages); + assertMessageAttributes(message); + } + for (Long id : copyMessageIds) + { + Map<String, Object> message = getRestTestHelper().find("id", id.intValue(), messages); + assertMessageAttributes(message); + } + } + + public void testDeleteMessages() throws Exception + { + String queueName = getTestQueueName(); + + // get message IDs + List<Long> ids = getMesssageIds(queueName); + + // delete half of the messages + int deleteNumber = ids.size() / 2; + StringBuilder queryString = new StringBuilder(); + List<Long> deleteMessageIds = new ArrayList<Long>(); + for (int i = 0; i < deleteNumber; i++) + { + Long id = ids.remove(i); + deleteMessageIds.add(id); + if (queryString.length() > 0) + { + queryString.append("&"); + } + queryString.append("id=").append(id); + } + + // delete messages + HttpURLConnection connection = getRestTestHelper().openManagementConnection( + "/rest/message/test/" + queueName + "?" + queryString.toString(), "DELETE"); + connection.connect(); + assertEquals("Unexpected response code", 200, connection.getResponseCode()); + + // check messages on queue + List<Map<String, Object>> messages = getRestTestHelper().getJsonAsList("/rest/message/test/" + queueName); + assertNotNull("Messages are not found", messages); + assertEquals("Unexpected number of messages", ids.size(), messages.size()); + for (Long id : ids) + { + Map<String, Object> message = getRestTestHelper().find("id", id.intValue(), messages); + assertMessageAttributes(message); + } + for (Long id : deleteMessageIds) + { + Map<String, Object> message = getRestTestHelper().find("id", id.intValue(), messages); + assertNull("Message with id " + id + " was not deleted", message); + } + } + + private List<Long> getMesssageIds(String queueName) throws IOException, JsonParseException, JsonMappingException + { + List<Map<String, Object>> messages = getRestTestHelper().getJsonAsList("/rest/message/test/" + queueName); + List<Long> ids = new ArrayList<Long>(); + for (Map<String, Object> message : messages) + { + ids.add(((Number) message.get("id")).longValue()); + } + return ids; + } + + private void assertMessage(int position, Map<String, Object> message) + { + assertMessageAttributes(message); + + assertEquals("Unexpected message attribute position", position, message.get("position")); + assertEquals("Unexpected message attribute size", position < 10 ? 6 : 7, message.get("size")); + boolean even = position % 2 == 0; + assertMessageAttributeValues(message, even); + } + + private void assertMessageAttributeValues(Map<String, Object> message, boolean even) + { + if (even) + { + assertEquals("Unexpected message attribute expirationTime", 0, message.get("expirationTime")); + assertEquals("Unexpected message attribute priority", 4, message.get("priority")); + assertEquals("Unexpected message attribute persistent", Boolean.TRUE, message.get("persistent")); + } + else + { + assertEquals("Unexpected message attribute expirationTime", ((Number) message.get("timestamp")).longValue() + + _ttl, message.get("expirationTime")); + assertEquals("Unexpected message attribute priority", 5, message.get("priority")); + assertEquals("Unexpected message attribute persistent", Boolean.FALSE, message.get("persistent")); + } + assertEquals("Unexpected message attribute mimeType", "text/plain", message.get("mimeType")); + assertEquals("Unexpected message attribute userId", "guest", message.get("userId")); + assertEquals("Unexpected message attribute deliveryCount", 0, message.get("deliveryCount")); + assertEquals("Unexpected message attribute state", "Available", message.get("state")); + } + + private void assertMessageAttributes(Map<String, Object> message) + { + assertNotNull("Message map cannot be null", message); + assertNotNull("Unexpected message attribute deliveryCount", message.get("deliveryCount")); + assertNotNull("Unexpected message attribute state", message.get("state")); + assertNotNull("Unexpected message attribute id", message.get("id")); + assertNotNull("Message arrivalTime cannot be null", message.get("arrivalTime")); + assertNotNull("Message timestamp cannot be null", message.get("timestamp")); + assertTrue("Message arrivalTime cannot be null", ((Number) message.get("arrivalTime")).longValue() > _startTime); + assertNotNull("Message messageId cannot be null", message.get("messageId")); + assertNotNull("Unexpected message attribute mimeType", message.get("mimeType")); + assertNotNull("Unexpected message attribute userId", message.get("userId")); + assertNotNull("Message priority cannot be null", message.get("priority")); + assertNotNull("Message expirationTime cannot be null", message.get("expirationTime")); + assertNotNull("Message persistent cannot be null", message.get("persistent")); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/PortRestTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/PortRestTest.java new file mode 100644 index 0000000000..578565be05 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/PortRestTest.java @@ -0,0 +1,64 @@ +/* + * + * 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; + +import java.net.URLDecoder; +import java.util.List; +import java.util.Map; + +import org.apache.qpid.server.model.Port; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class PortRestTest extends QpidRestTestCase +{ + public void testGet() throws Exception + { + List<Map<String, Object>> ports = getRestTestHelper().getJsonAsList("/rest/port/"); + assertNotNull("Port data cannot be null", ports); + assertEquals("Unexpected number of ports", 4, ports.size()); + + String httpPortName = TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT; + Map<String, Object> portData = getRestTestHelper().find(Port.NAME, httpPortName, ports); + assertNotNull("Http port " + httpPortName + " is not found", portData); + Asserts.assertPortAttributes(portData); + + String amqpPortName = TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT; + Map<String, Object> amqpPortData = getRestTestHelper().find(Port.NAME, amqpPortName, ports); + assertNotNull("Amqp port " + amqpPortName + " is not found", amqpPortData); + Asserts.assertPortAttributes(amqpPortData); + } + + public void testGetPort() throws Exception + { + List<Map<String, Object>> ports = getRestTestHelper().getJsonAsList("/rest/port/"); + assertNotNull("Ports data cannot be null", ports); + assertEquals("Unexpected number of ports", 4, ports.size()); + for (Map<String, Object> portMap : ports) + { + String portName = (String) portMap.get(Port.NAME); + assertNotNull("Port name attribute is not found", portName); + Map<String, Object> portData = getRestTestHelper().getJsonAsSingletonList("/rest/port/" + URLDecoder.decode(portName, "UTF-8")); + assertNotNull("Port " + portName + " is not found", portData); + Asserts.assertPortAttributes(portData); + } + } + +} diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/QpidRestTestCase.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/QpidRestTestCase.java new file mode 100644 index 0000000000..671bdd7eb8 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/QpidRestTestCase.java @@ -0,0 +1,84 @@ +/* + * + * 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; + +import java.io.IOException; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.test.utils.QpidBrokerTestCase; + +public class QpidRestTestCase extends QpidBrokerTestCase +{ + public static final String TEST1_VIRTUALHOST = "test"; + public static final String TEST2_VIRTUALHOST = "test2"; + public static final String TEST3_VIRTUALHOST = "test3"; + + public static final String[] EXPECTED_VIRTUALHOSTS = { TEST1_VIRTUALHOST, TEST2_VIRTUALHOST, TEST3_VIRTUALHOST}; + public static final String[] EXPECTED_QUEUES = { "queue", "ping" }; + public static final String[] EXPECTED_EXCHANGES = { "amq.fanout", "amq.match", "amq.direct","amq.topic","<<default>>" }; + + private RestTestHelper _restTestHelper = new RestTestHelper(findFreePort()); + + @Override + public void setUp() throws Exception + { + // Set up virtualhost config with queues and bindings to the amq.direct + for (String virtualhost : EXPECTED_VIRTUALHOSTS) + { + createTestVirtualHost(0, virtualhost); + for (String queue : EXPECTED_QUEUES) + { + setVirtualHostConfigurationProperty("virtualhosts.virtualhost." + virtualhost + ".queues.exchange", "amq.direct"); + setVirtualHostConfigurationProperty("virtualhosts.virtualhost." + virtualhost + ".queues.queue(-1).name", queue); + } + } + + customizeConfiguration(); + super.setUp(); + } + + @Override + protected void tearDown() throws Exception + { + try + { + super.tearDown(); + } + finally + { + getRestTestHelper().tearDown(); + } + } + + protected void customizeConfiguration() throws ConfigurationException, IOException + { + TestBrokerConfiguration config = getBrokerConfiguration(); + config.addHttpManagementConfiguration(); + config.setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, Port.PORT, _restTestHelper.getHttpPort()); + } + + public RestTestHelper getRestTestHelper() + { + return _restTestHelper; + } +} 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 new file mode 100644 index 0000000000..1f441e7cbb --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/QueueRestTest.java @@ -0,0 +1,225 @@ +/* + * + * 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; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URLDecoder; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; + +import org.apache.qpid.server.model.Binding; +import org.apache.qpid.server.model.Consumer; +import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.Queue; + +public class QueueRestTest extends QpidRestTestCase +{ + private static final String QUEUE_ATTRIBUTE_CONSUMERS = "consumers"; + private static final String QUEUE_ATTRIBUTE_BINDINGS = "bindings"; + + /** + * Message number to publish into queue + */ + private static final int MESSAGE_NUMBER = 2; + private static final int MESSAGE_PAYLOAD_SIZE = 6; + private static final int ENQUEUED_MESSAGES = 1; + private static final int DEQUEUED_MESSAGES = 1; + private static final int ENQUEUED_BYTES = MESSAGE_PAYLOAD_SIZE; + private static final int DEQUEUED_BYTES = MESSAGE_PAYLOAD_SIZE; + + private Connection _connection; + + public void setUp() throws Exception + { + super.setUp(); + _connection = getConnection(); + Session session = _connection.createSession(true, Session.SESSION_TRANSACTED); + String queueName = getTestQueueName(); + Destination queue = session.createQueue(queueName); + MessageConsumer consumer = session.createConsumer(queue); + MessageProducer producer = session.createProducer(queue); + + for (int i = 0; i < MESSAGE_NUMBER; i++) + { + producer.send(session.createTextMessage("Test-" + i)); + } + session.commit(); + _connection.start(); + Message m = consumer.receive(1000l); + assertNotNull("Message is not received", m); + session.commit(); + } + + public void testGetVirtualHostQueues() throws Exception + { + String queueName = getTestQueueName(); + List<Map<String, Object>> queues = getRestTestHelper().getJsonAsList("/rest/queue/test"); + assertEquals("Unexpected number of queues", EXPECTED_QUEUES.length + 1, queues.size()); + String[] expectedQueues = new String[EXPECTED_QUEUES.length + 1]; + System.arraycopy(EXPECTED_QUEUES, 0, expectedQueues, 0, EXPECTED_QUEUES.length); + expectedQueues[EXPECTED_QUEUES.length] = queueName; + + for (String name : expectedQueues) + { + Map<String, Object> queueDetails = getRestTestHelper().find(Queue.NAME, name, queues); + Asserts.assertQueue(name, "standard", queueDetails); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> bindings = (List<Map<String, Object>>) queueDetails.get(QUEUE_ATTRIBUTE_BINDINGS); + assertNotNull("Queue bindings are not found", bindings); + assertEquals("Unexpected number of bindings", 2, bindings.size()); + + Map<String, Object> defaultExchangeBinding = getRestTestHelper().find(Binding.EXCHANGE, "<<default>>", bindings); + Map<String, Object> directExchangeBinding = getRestTestHelper().find(Binding.EXCHANGE, "amq.direct", bindings); + Asserts.assertBinding(name, "<<default>>", defaultExchangeBinding); + Asserts.assertBinding(name, "amq.direct", directExchangeBinding); + } + } + + public void testGetByName() throws Exception + { + String queueName = getTestQueueName(); + Map<String, Object> queueDetails = getRestTestHelper().getJsonAsSingletonList("/rest/queue/test/" + queueName); + Asserts.assertQueue(queueName, "standard", queueDetails); + assertStatistics(queueDetails); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> bindings = (List<Map<String, Object>>) queueDetails.get(QUEUE_ATTRIBUTE_BINDINGS); + assertNotNull("Queue bindings are not found", bindings); + assertEquals("Unexpected number of bindings", 2, bindings.size()); + + Map<String, Object> defaultExchangeBinding = getRestTestHelper().find(Binding.EXCHANGE, "<<default>>", bindings); + Map<String, Object> directExchangeBinding = getRestTestHelper().find(Binding.EXCHANGE, "amq.direct", bindings); + Asserts.assertBinding(queueName, "<<default>>", defaultExchangeBinding); + Asserts.assertBinding(queueName, "amq.direct", directExchangeBinding); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> consumers = (List<Map<String, Object>>) queueDetails.get(QUEUE_ATTRIBUTE_CONSUMERS); + assertNotNull("Queue consumers are not found", consumers); + assertEquals("Unexpected number of consumers", 1, consumers.size()); + assertConsumer(consumers.get(0)); + } + + public void testPutCreateBinding() throws Exception + { + String queueName = getTestQueueName(); + String bindingName = queueName + 2; + String[] exchanges = { "amq.direct", "amq.fanout", "amq.topic", "amq.match", "<<default>>" }; + + for (int i = 0; i < exchanges.length; i++) + { + createBinding(bindingName, exchanges[i], queueName); + } + + Map<String, Object> queueDetails = getRestTestHelper().getJsonAsSingletonList("/rest/queue/test/" + queueName); + Asserts.assertQueue(queueName, "standard", queueDetails); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> bindings = (List<Map<String, Object>>) queueDetails.get(QUEUE_ATTRIBUTE_BINDINGS); + assertNotNull("Queue bindings are not found", bindings); + assertEquals("Unexpected number of bindings", exchanges.length + 2, bindings.size()); + + Map<String, Object> searchAttributes = new HashMap<String, Object>(); + searchAttributes.put(Binding.NAME, bindingName); + + for (int i = 0; i < exchanges.length; i++) + { + searchAttributes.put(Binding.EXCHANGE, exchanges[i]); + Map<String, Object> binding = getRestTestHelper().find(searchAttributes, bindings); + Asserts.assertBinding(bindingName, queueName, exchanges[i], binding); + } + } + + private void createBinding(String bindingName, String exchangeName, String queueName) throws IOException + { + HttpURLConnection connection = getRestTestHelper().openManagementConnection( + "/rest/binding/test/" + URLDecoder.decode(exchangeName, "UTF-8") + "/" + queueName + "/" + bindingName, + "PUT"); + + Map<String, Object> bindingData = new HashMap<String, Object>(); + bindingData.put(Binding.NAME, bindingName); + bindingData.put(Binding.EXCHANGE, exchangeName); + bindingData.put(Binding.QUEUE, queueName); + + getRestTestHelper().writeJsonRequest(connection, bindingData); + assertEquals("Unexpected response code", 201, connection.getResponseCode()); + + connection.disconnect(); + } + + private void assertConsumer(Map<String, Object> consumer) + { + assertNotNull("Consumer map should not be null", consumer); + Asserts.assertAttributesPresent(consumer, Consumer.AVAILABLE_ATTRIBUTES, Consumer.STATE, Consumer.TIME_TO_LIVE, + Consumer.CREATED, Consumer.UPDATED, Consumer.SETTLEMENT_MODE, Consumer.EXCLUSIVE, Consumer.SELECTOR, + Consumer.NO_LOCAL); + + assertEquals("Unexpected binding attribute " + Consumer.NAME, "1", consumer.get(Consumer.NAME)); + assertEquals("Unexpected binding attribute " + Consumer.DURABLE, Boolean.FALSE, consumer.get(Consumer.DURABLE)); + assertEquals("Unexpected binding attribute " + Consumer.LIFETIME_POLICY, LifetimePolicy.AUTO_DELETE.name(), + consumer.get(Consumer.LIFETIME_POLICY)); + assertEquals("Unexpected binding attribute " + Consumer.DISTRIBUTION_MODE, "MOVE", + consumer.get(Consumer.DISTRIBUTION_MODE)); + + @SuppressWarnings("unchecked") + Map<String, Object> statistics = (Map<String, Object>) consumer.get(Asserts.STATISTICS_ATTRIBUTE); + assertNotNull("Consumer statistics is not present", statistics); + Asserts.assertAttributesPresent(statistics, Consumer.AVAILABLE_STATISTICS, Consumer.STATE_CHANGED); + } + + private void assertStatistics(Map<String, Object> queueDetails) + { + @SuppressWarnings("unchecked") + Map<String, Object> statistics = (Map<String, Object>) queueDetails.get(Asserts.STATISTICS_ATTRIBUTE); + assertEquals("Unexpected queue statistics attribute " + Queue.PERSISTENT_DEQUEUED_MESSAGES, DEQUEUED_MESSAGES, + statistics.get(Queue.PERSISTENT_DEQUEUED_MESSAGES)); + assertEquals("Unexpected queue statistics attribute " + Queue.QUEUE_DEPTH_MESSAGES, ENQUEUED_MESSAGES, + statistics.get(Queue.QUEUE_DEPTH_MESSAGES)); + assertEquals("Unexpected queue statistics attribute " + Queue.CONSUMER_COUNT, 1, + statistics.get(Queue.CONSUMER_COUNT)); + assertEquals("Unexpected queue statistics attribute " + Queue.CONSUMER_COUNT_WITH_CREDIT, 1, + statistics.get(Queue.CONSUMER_COUNT_WITH_CREDIT)); + assertEquals("Unexpected queue statistics attribute " + Queue.BINDING_COUNT, 2, statistics.get(Queue.BINDING_COUNT)); + assertEquals("Unexpected queue statistics attribute " + Queue.PERSISTENT_DEQUEUED_MESSAGES, DEQUEUED_MESSAGES, + statistics.get(Queue.PERSISTENT_DEQUEUED_MESSAGES)); + assertEquals("Unexpected queue statistics attribute " + Queue.TOTAL_DEQUEUED_MESSAGES, DEQUEUED_MESSAGES, + statistics.get(Queue.TOTAL_DEQUEUED_MESSAGES)); + assertEquals("Unexpected queue statistics attribute " + Queue.TOTAL_DEQUEUED_BYTES, DEQUEUED_BYTES, + statistics.get(Queue.TOTAL_DEQUEUED_BYTES)); + assertEquals("Unexpected queue statistics attribute " + Queue.PERSISTENT_DEQUEUED_BYTES, DEQUEUED_BYTES, + statistics.get(Queue.TOTAL_DEQUEUED_BYTES)); + assertEquals("Unexpected queue statistics attribute " + Queue.PERSISTENT_ENQUEUED_BYTES, ENQUEUED_BYTES + + DEQUEUED_BYTES, statistics.get(Queue.PERSISTENT_ENQUEUED_BYTES)); + assertEquals("Unexpected queue statistics attribute " + Queue.TOTAL_ENQUEUED_BYTES, ENQUEUED_BYTES + DEQUEUED_BYTES, + statistics.get(Queue.TOTAL_ENQUEUED_BYTES)); + assertEquals("Unexpected queue statistics attribute " + Queue.QUEUE_DEPTH_BYTES, ENQUEUED_BYTES, + statistics.get(Queue.QUEUE_DEPTH_BYTES)); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java new file mode 100644 index 0000000000..0db1f7e50d --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java @@ -0,0 +1,452 @@ +/* + * 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; + +import static org.apache.qpid.test.utils.TestSSLConstants.TRUSTSTORE; +import static org.apache.qpid.test.utils.TestSSLConstants.TRUSTSTORE_PASSWORD; + +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLDecoder; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManagerFactory; +import javax.servlet.http.HttpServletResponse; + +import junit.framework.Assert; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.log4j.Logger; +import org.apache.qpid.server.security.auth.manager.AbstractPrincipalDatabaseAuthManagerFactory; +import org.apache.qpid.ssl.SSLContextFactory; +import org.apache.qpid.test.utils.QpidBrokerTestCase; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.JsonParseException; +import org.codehaus.jackson.map.JsonMappingException; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.type.TypeReference; + +public class RestTestHelper +{ + private static final Logger LOGGER = Logger.getLogger(RestTestHelper.class); + + private int _httpPort; + + private boolean _useSsl; + + private String _username; + + private String _password; + + private File _passwdFile; + + public RestTestHelper(int httpPort) + { + _httpPort = httpPort; + } + + public int getHttpPort() + { + return _httpPort; + } + + private String getHostName() + { + return "localhost"; + } + + private String getProtocol() + { + return _useSsl ? "https" : "http"; + } + + public String getManagementURL() + { + return getProtocol() + "://" + getHostName() + ":" + getHttpPort(); + } + + public URL getManagementURL(String path) throws MalformedURLException + { + return new URL(getManagementURL() + path); + } + + public HttpURLConnection openManagementConnection(String path, String method) throws IOException + { + URL url = getManagementURL(path); + HttpURLConnection httpCon = (HttpURLConnection) url.openConnection(); + if(_useSsl) + { + try + { + // We have to use a SSLSocketFactory from a new SSLContext so that we don't re-use + // the JVM's defaults that may have been initialised in previous tests. + + SSLContext sslContext = SSLContextFactory.buildClientContext( + TRUSTSTORE, TRUSTSTORE_PASSWORD, + KeyStore.getDefaultType(), + TrustManagerFactory.getDefaultAlgorithm(), + null, null, null, null, null); + + SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + + ((HttpsURLConnection) httpCon).setSSLSocketFactory(sslSocketFactory); + } + catch (GeneralSecurityException e) + { + throw new RuntimeException(e); + } + } + + if(_username != null) + { + String encoded = new String(new Base64().encode((_username + ":" + _password).getBytes())); + httpCon.setRequestProperty("Authorization", "Basic " + encoded); + } + + httpCon.setDoOutput(true); + httpCon.setRequestMethod(method); + return httpCon; + } + + public List<Map<String, Object>> readJsonResponseAsList(HttpURLConnection connection) throws IOException, + JsonParseException, JsonMappingException + { + byte[] data = readConnectionInputStream(connection); + + ObjectMapper mapper = new ObjectMapper(); + + TypeReference<List<LinkedHashMap<String, Object>>> typeReference = new TypeReference<List<LinkedHashMap<String, Object>>>() + { + }; + List<Map<String, Object>> providedObject = mapper.readValue(new ByteArrayInputStream(data), typeReference); + return providedObject; + } + + public Map<String, Object> readJsonResponseAsMap(HttpURLConnection connection) throws IOException, + JsonParseException, JsonMappingException + { + byte[] data = readConnectionInputStream(connection); + + ObjectMapper mapper = new ObjectMapper(); + + TypeReference<LinkedHashMap<String, Object>> typeReference = new TypeReference<LinkedHashMap<String, Object>>() + { + }; + Map<String, Object> providedObject = mapper.readValue(new ByteArrayInputStream(data), typeReference); + return providedObject; + } + + public byte[] readConnectionInputStream(HttpURLConnection connection) throws IOException + { + InputStream is = connection.getInputStream(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int len = -1; + while ((len = is.read(buffer)) != -1) + { + baos.write(buffer, 0, len); + } + if (LOGGER.isTraceEnabled()) + { + LOGGER.trace("RESPONSE:" + new String(baos.toByteArray())); + } + return baos.toByteArray(); + } + + public void writeJsonRequest(HttpURLConnection connection, Map<String, Object> data) throws JsonGenerationException, + JsonMappingException, IOException + { + ObjectMapper mapper = new ObjectMapper(); + mapper.writeValue(connection.getOutputStream(), data); + } + + public Map<String, Object> find(String name, Object value, List<Map<String, Object>> data) + { + for (Map<String, Object> map : data) + { + Object mapValue = map.get(name); + if (value.equals(mapValue)) + { + return map; + } + } + return null; + } + + public Map<String, Object> find(Map<String, Object> searchAttributes, List<Map<String, Object>> data) + { + for (Map<String, Object> map : data) + { + boolean equals = true; + for (Map.Entry<String, Object> entry : searchAttributes.entrySet()) + { + Object mapValue = map.get(entry.getKey()); + if (!entry.getValue().equals(mapValue)) + { + equals = false; + break; + } + } + if (equals) + { + return map; + } + } + return null; + } + + public Map<String, Object> getJsonAsSingletonList(String path) throws IOException + { + List<Map<String, Object>> response = getJsonAsList(path); + + Assert.assertNotNull("Response cannot be null", response); + Assert.assertEquals("Unexpected response", 1, response.size()); + return response.get(0); + } + + public List<Map<String, Object>> getJsonAsList(String path) throws IOException, JsonParseException, + JsonMappingException + { + HttpURLConnection connection = openManagementConnection(path, "GET"); + connection.connect(); + List<Map<String, Object>> response = readJsonResponseAsList(connection); + return response; + } + + public Map<String, Object> getJsonAsMap(String path) throws IOException + { + HttpURLConnection connection = openManagementConnection(path, "GET"); + connection.connect(); + Map<String, Object> response = readJsonResponseAsMap(connection); + return response; + } + + public void createNewGroupMember(String groupProviderName, String groupName, String memberName, int responseCode) throws IOException + { + HttpURLConnection connection = openManagementConnection( + "/rest/groupmember/" + URLDecoder.decode(groupProviderName, "UTF-8") + "/"+ URLDecoder.decode(groupName, "UTF-8") + "/" + URLDecoder.decode(memberName, "UTF-8"), + "PUT"); + + Map<String, Object> groupMemberData = new HashMap<String, Object>(); + // TODO add type + writeJsonRequest(connection, groupMemberData); + + Assert.assertEquals("Unexpected response code", responseCode, connection.getResponseCode()); + + connection.disconnect(); + } + + public void createNewGroupMember(String groupProviderName, String groupName, String memberName) throws IOException + { + createNewGroupMember(groupProviderName, groupName, memberName, HttpServletResponse.SC_CREATED); + } + + public void removeMemberFromGroup(String groupProviderName, String groupName, String memberName, int responseCode) throws IOException + { + HttpURLConnection connection = openManagementConnection( + "/rest/groupmember/" + URLDecoder.decode(groupProviderName, "UTF-8") + "/"+ URLDecoder.decode(groupName, "UTF-8") + "/" + URLDecoder.decode(memberName, "UTF-8"), + "DELETE"); + + Assert.assertEquals("Unexpected response code", responseCode, connection.getResponseCode()); + + connection.disconnect(); + } + + public void removeMemberFromGroup(String groupProviderName, String groupName, String memberName) throws IOException + { + removeMemberFromGroup(groupProviderName, groupName, memberName, HttpServletResponse.SC_OK); + } + + public void assertNumberOfGroupMembers(Map<String, Object> data, int expectedNumberOfGroupMembers) + { + @SuppressWarnings("unchecked") + List<Map<String, Object>> groups = (List<Map<String, Object>>) data.get("groupmembers"); + if (groups == null) + { + groups = Collections.emptyList(); + } + + Assert.assertEquals("Unexpected number of group members", expectedNumberOfGroupMembers, groups.size()); + } + + public void createGroup(String groupName, String groupProviderName) throws IOException + { + createGroup(groupName, groupProviderName, HttpServletResponse.SC_CREATED); + } + + public void createGroup(String groupName, String groupProviderName, int responseCode) throws IOException + { + HttpURLConnection connection = openManagementConnection( + "/rest/group/" + URLDecoder.decode(groupProviderName, "UTF-8") + "/"+ URLDecoder.decode(groupName, "UTF-8"), + "PUT"); + + Map<String, Object> groupData = new HashMap<String, Object>(); + writeJsonRequest(connection, groupData); + + Assert.assertEquals("Unexpected response code", responseCode, connection.getResponseCode()); + + connection.disconnect(); + } + + public void createOrUpdateUser(String username, String password) throws IOException + { + createOrUpdateUser(username, password, HttpServletResponse.SC_CREATED); + } + + public void createOrUpdateUser(String username, String password, int responseCode) throws IOException + { + HttpURLConnection connection = openManagementConnection("/rest/user/" + + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/" + username, "PUT"); + + Map<String, Object> data = new HashMap<String, Object>(); + data.put("password", password); + writeJsonRequest(connection, data); + + Assert.assertEquals("Unexpected response code", responseCode, connection.getResponseCode()); + + connection.disconnect(); + } + + public void removeGroup(String groupName, String groupProviderName, int responseCode) throws IOException + { + HttpURLConnection connection = openManagementConnection( + "/rest/group/" + URLDecoder.decode(groupProviderName, "UTF-8") + "/"+ URLDecoder.decode(groupName, "UTF-8"), + "DELETE"); + + Assert.assertEquals("Unexpected response code", responseCode, connection.getResponseCode()); + connection.disconnect(); + } + + public void removeGroup(String groupName, String groupProviderName) throws IOException + { + removeGroup(groupName, groupProviderName, HttpServletResponse.SC_OK); + } + + public void removeUserById(String id) throws IOException + { + HttpURLConnection connection = openManagementConnection("/rest/user/" + + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "?id=" + id, "DELETE"); + Assert.assertEquals("Unexpected response code", HttpServletResponse.SC_OK, connection.getResponseCode()); + connection.disconnect(); + } + + public void removeUser(String username, int responseCode) throws IOException + { + HttpURLConnection connection = openManagementConnection("/rest/user/" + + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/" + username, "DELETE"); + Assert.assertEquals("Unexpected response code", responseCode, connection.getResponseCode()); + connection.disconnect(); + } + + public void removeUser(String username) throws IOException + { + removeUser(username, HttpServletResponse.SC_OK); + } + + public void assertNumberOfGroups(Map<String, Object> data, int expectedNumberOfGroups) + { + @SuppressWarnings("unchecked") + List<Map<String, Object>> groups = (List<Map<String, Object>>) data.get("groups"); + if (groups == null) + { + groups = Collections.emptyList(); + } + Assert.assertEquals("Unexpected number of groups", expectedNumberOfGroups, groups.size()); + } + + public void setUseSsl(boolean useSsl) + { + _useSsl = useSsl; + } + + public void setUsernameAndPassword(String username, String password) + { + _username = username; + _password = password; + } + + /** + * Create password file that follows the convention username=password, which is deleted by {@link #tearDown()} + */ + public void configureTemporaryPasswordFile(QpidBrokerTestCase testCase, String... users) throws ConfigurationException, IOException + { + _passwdFile = createTemporaryPasswdFile(users); + + testCase.getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER, + AbstractPrincipalDatabaseAuthManagerFactory.ATTRIBUTE_PATH, _passwdFile.getAbsolutePath()); + } + + public void tearDown() + { + if (_passwdFile != null) + { + if (_passwdFile.exists()) + { + _passwdFile.delete(); + } + } + } + + private File createTemporaryPasswdFile(String[] users) throws IOException + { + BufferedWriter writer = null; + try + { + File testFile = File.createTempFile(this.getClass().getName(),"tmp"); + testFile.deleteOnExit(); + + writer = new BufferedWriter(new FileWriter(testFile)); + for (int i = 0; i < users.length; i++) + { + String username = users[i]; + writer.write(username + ":" + username); + writer.newLine(); + } + + return testFile; + + } + finally + { + if (writer != null) + { + writer.close(); + } + } + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/SaslRestTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/SaslRestTest.java new file mode 100644 index 0000000000..856fda9419 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/SaslRestTest.java @@ -0,0 +1,435 @@ +/* + * + * 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; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.server.plugin.AuthenticationManagerFactory; +import org.apache.qpid.server.security.auth.manager.AbstractPrincipalDatabaseAuthManagerFactory; +import org.apache.qpid.server.security.auth.manager.Base64MD5PasswordFileAuthenticationManagerFactory; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.tools.security.Passwd; +import org.codehaus.jackson.JsonParseException; +import org.codehaus.jackson.map.JsonMappingException; + +public class SaslRestTest extends QpidRestTestCase +{ + @Override + public void startBroker() + { + // prevent broker from starting in setUp + } + + public void startBrokerNow() throws Exception + { + super.startBroker(); + } + + public void testGetMechanismsWithBrokerPlainPasswordPrincipalDatabase() throws Exception + { + startBrokerNow(); + + Map<String, Object> saslData = getRestTestHelper().getJsonAsMap("/rest/sasl"); + assertNotNull("mechanisms attribute is not found", saslData.get("mechanisms")); + + @SuppressWarnings("unchecked") + List<String> mechanisms = (List<String>) saslData.get("mechanisms"); + String[] expectedMechanisms = { "AMQPLAIN", "PLAIN", "CRAM-MD5" }; + for (String mechanism : expectedMechanisms) + { + assertTrue("Mechanism " + mechanism + " is not found", mechanisms.contains(mechanism)); + } + assertNull("Unexpected user was returned", saslData.get("user")); + } + + public void testGetMechanismsWithBrokerBase64MD5FilePrincipalDatabase() throws Exception + { + configureBase64MD5FilePrincipalDatabase(); + startBrokerNow(); + + Map<String, Object> saslData = getRestTestHelper().getJsonAsMap("/rest/sasl"); + assertNotNull("mechanisms attribute is not found", saslData.get("mechanisms")); + + @SuppressWarnings("unchecked") + List<String> mechanisms = (List<String>) saslData.get("mechanisms"); + String[] expectedMechanisms = { "CRAM-MD5-HEX", "CRAM-MD5-HASHED" }; + for (String mechanism : expectedMechanisms) + { + assertTrue("Mechanism " + mechanism + " is not found", mechanisms.contains(mechanism)); + } + assertNull("Unexpected user was returned", saslData.get("user")); + } + + public void testPlainSaslAuthenticationForValidCredentials() throws Exception + { + startBrokerNow(); + + byte[] responseBytes = generatePlainClientResponse("admin", "admin"); + String responseData = Base64.encodeBase64String(responseBytes); + String parameters= "mechanism=PLAIN&response=" + responseData; + + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/sasl", "POST"); + OutputStream os = connection.getOutputStream(); + os.write(parameters.getBytes()); + os.flush(); + + int code = connection.getResponseCode(); + assertEquals("Unexpected response code", 200, code); + + List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); + + // request authenticated user details + connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET"); + applyCookiesToConnection(cookies, connection); + Map<String, Object> response2 = getRestTestHelper().readJsonResponseAsMap(connection); + assertEquals("Unexpected user", "admin", response2.get("user")); + } + + public void testPlainSaslAuthenticationForIncorrectPassword() throws Exception + { + startBrokerNow(); + + byte[] responseBytes = generatePlainClientResponse("admin", "incorrect"); + String responseData = Base64.encodeBase64String(responseBytes); + String parameters= "mechanism=PLAIN&response=" + responseData; + + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/sasl", "POST"); + OutputStream os = connection.getOutputStream(); + os.write(parameters.getBytes()); + os.flush(); + + int code = connection.getResponseCode(); + assertEquals("Unexpected response code", 403, code); + + List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); + + // request authenticated user details + connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET"); + applyCookiesToConnection(cookies, connection); + Map<String, Object> response2 = getRestTestHelper().readJsonResponseAsMap(connection); + assertNull("Unexpected user", response2.get("user")); + } + + public void testPlainSaslAuthenticationForNonExistingUser() throws Exception + { + startBrokerNow(); + + byte[] responseBytes = generatePlainClientResponse("nonexisting", "admin"); + String responseData = Base64.encodeBase64String(responseBytes); + String parameters= "mechanism=PLAIN&response=" + responseData; + + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/sasl", "POST"); + OutputStream os = connection.getOutputStream(); + os.write(parameters.getBytes()); + os.flush(); + + int code = connection.getResponseCode(); + assertEquals("Unexpected response code", 403, code); + + List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); + + // request authenticated user details + connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET"); + applyCookiesToConnection(cookies, connection); + Map<String, Object> response2 = getRestTestHelper().readJsonResponseAsMap(connection); + assertNull("Unexpected user", response2.get("user")); + } + + public void testCramMD5SaslAuthenticationForValidCredentials() throws Exception + { + startBrokerNow(); + + // request the challenge for CRAM-MD5 + HttpURLConnection connection = requestSasServerChallenge("CRAM-MD5"); + List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); + + // authenticate user with correct credentials + int code = authenticateUser(connection, "admin", "admin", "CRAM-MD5"); + assertEquals("Unexpected response code", 200, code); + + // request authenticated user details + connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET"); + applyCookiesToConnection(cookies, connection); + Map<String, Object> response2 = getRestTestHelper().readJsonResponseAsMap(connection); + assertEquals("Unexpected user", "admin", response2.get("user")); + } + + public void testCramMD5SaslAuthenticationForIncorrectPassword() throws Exception + { + startBrokerNow(); + + // request the challenge for CRAM-MD5 + HttpURLConnection connection = requestSasServerChallenge("CRAM-MD5"); + List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); + + // authenticate user with correct credentials + int code = authenticateUser(connection, "admin", "incorrect", "CRAM-MD5"); + assertEquals("Unexpected response code", 403, code); + + // request authenticated user details + connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET"); + applyCookiesToConnection(cookies, connection); + Map<String, Object> response2 = getRestTestHelper().readJsonResponseAsMap(connection); + assertNull("Unexpected user", response2.get("user")); + } + + public void testCramMD5SaslAuthenticationForNonExistingUser() throws Exception + { + startBrokerNow(); + + // request the challenge for CRAM-MD5 + HttpURLConnection connection = requestSasServerChallenge("CRAM-MD5"); + List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); + + // authenticate user with correct credentials + int code = authenticateUser(connection, "nonexisting", "admin", "CRAM-MD5"); + assertEquals("Unexpected response code", 403, code); + + // request authenticated user details + connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET"); + applyCookiesToConnection(cookies, connection); + Map<String, Object> response2 = getRestTestHelper().readJsonResponseAsMap(connection); + assertNull("Unexpected user", response2.get("user")); + } + + public void testCramMD5HexSaslAuthenticationForValidCredentials() throws Exception + { + configureBase64MD5FilePrincipalDatabase(); + startBrokerNow(); + + // request the challenge for CRAM-MD5-HEX + HttpURLConnection connection = requestSasServerChallenge("CRAM-MD5-HEX"); + List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); + + // authenticate user with correct credentials + int code = authenticateUser(connection, "admin", "admin", "CRAM-MD5-HEX"); + assertEquals("Unexpected response code", 200, code); + + // request authenticated user details + connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET"); + applyCookiesToConnection(cookies, connection); + Map<String, Object> response2 = getRestTestHelper().readJsonResponseAsMap(connection); + assertEquals("Unexpected user", "admin", response2.get("user")); + } + + public void testCramMD5HexSaslAuthenticationForIncorrectPassword() throws Exception + { + configureBase64MD5FilePrincipalDatabase(); + startBrokerNow(); + + HttpURLConnection connection = requestSasServerChallenge("CRAM-MD5-HEX"); + List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); + + // try to authenticate user with incorrect passowrd + int code = authenticateUser(connection, "admin", "incorrect", "CRAM-MD5-HEX"); + assertEquals("Unexpected response code", 403, code); + + // request authenticated user details + connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET"); + applyCookiesToConnection(cookies, connection); + Map<String, Object> response2 = getRestTestHelper().readJsonResponseAsMap(connection); + assertNull("Unexpected user", response2.get("user")); + } + + public void testCramMD5HexSaslAuthenticationForNonExistingUser() throws Exception + { + configureBase64MD5FilePrincipalDatabase(); + startBrokerNow(); + + HttpURLConnection connection = requestSasServerChallenge("CRAM-MD5-HEX"); + List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); + + // try to authenticate non-existing user + int code = authenticateUser(connection, "nonexisting", "admin", "CRAM-MD5-HEX"); + assertEquals("Unexpected response code", 403, code); + + // request authenticated user details + connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET"); + applyCookiesToConnection(cookies, connection); + Map<String, Object> response2 = getRestTestHelper().readJsonResponseAsMap(connection); + assertNull("Unexpected user", response2.get("user")); + } + + private HttpURLConnection requestSasServerChallenge(String mechanism) throws IOException + { + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/sasl", "POST"); + OutputStream os = connection.getOutputStream(); + os.write(("mechanism=" + mechanism).getBytes()); + os.flush(); + return connection; + } + + private int authenticateUser(HttpURLConnection requestChallengeConnection, String userName, String userPassword, String mechanism) + throws IOException, JsonParseException, JsonMappingException, Exception + { + // get the response + Map<String, Object> response = getRestTestHelper().readJsonResponseAsMap(requestChallengeConnection); + String challenge = (String) response.get("challenge"); + assertNotNull("Challenge is not found", challenge); + + // preserve cookies to have the same server session + List<String> cookies = requestChallengeConnection.getHeaderFields().get("Set-Cookie"); + + // generate the authentication response for the challenge received + byte[] challengeBytes = Base64.decodeBase64(challenge); + byte[] responseBytes = generateClientResponse(mechanism, userName, userPassword, challengeBytes); + String responseData = Base64.encodeBase64String(responseBytes); + String requestParameters = ("id=" + response.get("id") + "&response=" + responseData); + + // re-open connection + HttpURLConnection authenticateConnection = getRestTestHelper().openManagementConnection("/rest/sasl", "POST"); + + // set cookies to use the same server session + applyCookiesToConnection(cookies, authenticateConnection); + OutputStream os = authenticateConnection.getOutputStream(); + os.write(requestParameters.getBytes()); + os.flush(); + return authenticateConnection.getResponseCode(); + } + + private byte[] generateClientResponse(String mechanism, String userName, String userPassword, byte[] challengeBytes) throws Exception + { + byte[] responseBytes = null; + if ("CRAM-MD5-HEX".equalsIgnoreCase(mechanism)) + { + responseBytes = generateCramMD5HexClientResponse(userName, userPassword, challengeBytes); + } + else if ("CRAM-MD5".equalsIgnoreCase(mechanism)) + { + responseBytes = generateCramMD5ClientResponse(userName, userPassword, challengeBytes); + } + else + { + throw new RuntimeException("Not implemented test mechanism " + mechanism); + } + return responseBytes; + } + + private void applyCookiesToConnection(List<String> cookies, HttpURLConnection connection) + { + for (String cookie : cookies) + { + connection.addRequestProperty("Cookie", cookie.split(";", 2)[0]); + } + } + + private static byte SEPARATOR = 0; + + private byte[] generatePlainClientResponse(String userName, String userPassword) throws Exception + { + byte[] password = userPassword.getBytes("UTF8"); + byte user[] = userName.getBytes("UTF8"); + byte response[] = new byte[password.length + user.length + 2 ]; + int size = 0; + response[size++] = SEPARATOR; + System.arraycopy(user, 0, response, size, user.length); + size += user.length; + response[size++] = SEPARATOR; + System.arraycopy(password, 0, response, size, password.length); + return response; + } + + private byte[] generateCramMD5HexClientResponse(String userName, String userPassword, byte[] challengeBytes) throws Exception + { + String macAlgorithm = "HmacMD5"; + byte[] digestedPasswordBytes = MessageDigest.getInstance("MD5").digest(userPassword.getBytes("UTF-8")); + byte[] hexEncodedDigestedPasswordBytes = toHex(digestedPasswordBytes).getBytes("UTF-8"); + Mac mac = Mac.getInstance(macAlgorithm); + mac.init(new SecretKeySpec(hexEncodedDigestedPasswordBytes, macAlgorithm)); + final byte[] messageAuthenticationCode = mac.doFinal(challengeBytes); + String responseAsString = userName + " " + toHex(messageAuthenticationCode); + return responseAsString.getBytes(); + } + + private byte[] generateCramMD5ClientResponse(String userName, String userPassword, byte[] challengeBytes) throws Exception + { + String macAlgorithm = "HmacMD5"; + Mac mac = Mac.getInstance(macAlgorithm); + mac.init(new SecretKeySpec(userPassword.getBytes("UTF-8"), macAlgorithm)); + final byte[] messageAuthenticationCode = mac.doFinal(challengeBytes); + String responseAsString = userName + " " + toHex(messageAuthenticationCode); + return responseAsString.getBytes(); + } + + private String toHex(byte[] data) + { + StringBuffer hash = new StringBuffer(); + for (int i = 0; i < data.length; i++) + { + String hex = Integer.toHexString(0xFF & data[i]); + if (hex.length() == 1) + { + hash.append('0'); + } + hash.append(hex); + } + return hash.toString(); + } + + private void configureBase64MD5FilePrincipalDatabase() throws IOException, ConfigurationException + { + // generate user password entry + String passwordFileEntry; + try + { + passwordFileEntry = new Passwd().getOutput("admin", "admin"); + } + catch (NoSuchAlgorithmException e) + { + throw new ConfigurationException(e); + } + + // store the entry in the file + File passwordFile = File.createTempFile("passwd", "pwd"); + passwordFile.deleteOnExit(); + + FileWriter writer = null; + try + { + writer = new FileWriter(passwordFile); + writer.write(passwordFileEntry); + } + finally + { + writer.close(); + } + + // configure broker to use Base64MD5PasswordFilePrincipalDatabase + Map<String, Object> newAttributes = new HashMap<String, Object>(); + newAttributes.put(AbstractPrincipalDatabaseAuthManagerFactory.ATTRIBUTE_PATH, passwordFile.getAbsolutePath()); + newAttributes.put(AuthenticationManagerFactory.ATTRIBUTE_TYPE, Base64MD5PasswordFileAuthenticationManagerFactory.PROVIDER_TYPE); + getBrokerConfiguration().setObjectAttributes(TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER, newAttributes); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/StructureRestTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/StructureRestTest.java new file mode 100644 index 0000000000..427934fac2 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/StructureRestTest.java @@ -0,0 +1,114 @@ +/* + * + * 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; + +import java.util.List; +import java.util.Map; + +import org.apache.qpid.server.model.Port; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class StructureRestTest extends QpidRestTestCase +{ + + public void testGet() throws Exception + { + Map<String, Object> structure = getRestTestHelper().getJsonAsMap("/rest/structure"); + assertNotNull("Structure data cannot be null", structure); + assertNode(structure, "QpidBroker"); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> virtualhosts = (List<Map<String, Object>>) structure.get("virtualhosts"); + assertEquals("Unexpected number of virtual hosts", 3, virtualhosts.size()); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> ports = (List<Map<String, Object>>) structure.get("ports"); + assertEquals("Unexpected number of ports", 4, ports.size()); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> providers = (List<Map<String, Object>>) structure.get("authenticationproviders"); + assertEquals("Unexpected number of authentication providers", 1, providers.size()); + + for (String hostName : EXPECTED_VIRTUALHOSTS) + { + Map<String, Object> host = getRestTestHelper().find("name", hostName, virtualhosts); + assertNotNull("Host " + hostName + " is not found ", host); + assertNode(host, hostName); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> queues = (List<Map<String, Object>>) host.get("queues"); + assertNotNull("Host " + hostName + " queues are not found ", queues); + for (String queueName : EXPECTED_QUEUES) + { + Map<String, Object> queue = getRestTestHelper().find("name", queueName, queues); + assertNotNull(hostName + " queue " + queueName + " is not found ", queue); + assertNode(queue, queueName); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> bindings = (List<Map<String, Object>>) queue.get("bindings"); + assertNotNull(hostName + " queue " + queueName + " bindings are not found ", queues); + for (Map<String, Object> binding : bindings) + { + assertNode(binding, queueName); + } + } + + @SuppressWarnings("unchecked") + List<Map<String, Object>> exchanges = (List<Map<String, Object>>) host.get("exchanges"); + assertNotNull("Host " + hostName + " exchanges are not found ", exchanges); + for (String exchangeName : EXPECTED_EXCHANGES) + { + Map<String, Object> exchange = getRestTestHelper().find("name", exchangeName, exchanges); + assertNotNull("Exchange " + exchangeName + " is not found ", exchange); + assertNode(exchange, exchangeName); + if ("amq.direct".equalsIgnoreCase(exchangeName) || "<<default>>".equalsIgnoreCase(exchangeName)) + { + @SuppressWarnings("unchecked") + List<Map<String, Object>> bindings = (List<Map<String, Object>>) exchange.get("bindings"); + assertNotNull(hostName + " exchange " + exchangeName + " bindings are not found ", bindings); + for (String queueName : EXPECTED_QUEUES) + { + Map<String, Object> binding = getRestTestHelper().find("name", queueName, bindings); + assertNotNull(hostName + " exchange " + exchangeName + " binding " + queueName + " is not found", binding); + assertNode(binding, queueName); + } + } + } + } + + + String httpPortName = TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT; + Map<String, Object> portData = getRestTestHelper().find(Port.NAME, httpPortName, ports); + assertNotNull("Http Port " + httpPortName + " is not found", portData); + assertNode(portData, httpPortName); + + String amqpPortName = TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT; + Map<String, Object> amqpPortData = getRestTestHelper().find(Port.NAME, amqpPortName, ports); + assertNotNull("Amqp port " + amqpPortName + " is not found", amqpPortData); + assertNode(amqpPortData, amqpPortName); + } + + private void assertNode(Map<String, Object> node, String name) + { + assertEquals("Unexpected name", name, node.get("name")); + assertNotNull("Unexpected id", node.get("id")); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/UserRestTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/UserRestTest.java new file mode 100644 index 0000000000..017467a8be --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/UserRestTest.java @@ -0,0 +1,99 @@ +/* + * + * 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; + +import java.util.List; +import java.util.Map; + +import org.apache.qpid.server.model.User; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class UserRestTest extends QpidRestTestCase +{ + @Override + public void setUp() throws Exception + { + getRestTestHelper().configureTemporaryPasswordFile(this, "user1", "user2"); + + super.setUp(); // do this last because it starts the broker, using the modified config + } + + public void testGet() throws Exception + { + List<Map<String, Object>> users = getRestTestHelper().getJsonAsList("/rest/user"); + assertNotNull("Users cannot be null", users); + assertTrue("Unexpected number of users", users.size() > 1); + for (Map<String, Object> user : users) + { + assertUser(user); + } + } + + public void testGetUserByName() throws Exception + { + List<Map<String, Object>> users = getRestTestHelper().getJsonAsList("/rest/user"); + assertNotNull("Users cannot be null", users); + assertTrue("Unexpected number of users", users.size() > 1); + for (Map<String, Object> user : users) + { + assertNotNull("Attribute " + User.ID, user.get(User.ID)); + String userName = (String) user.get(User.NAME); + assertNotNull("Attribute " + User.NAME, userName); + Map<String, Object> userDetails = getRestTestHelper().getJsonAsSingletonList("/rest/user/" + + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/" + userName); + assertUser(userDetails); + assertEquals("Unexpected user name", userName, userDetails.get(User.NAME)); + } + } + + public void testPut() throws Exception + { + String userName = getTestName(); + getRestTestHelper().createOrUpdateUser(userName, "newPassword"); + + Map<String, Object> userDetails = getRestTestHelper().getJsonAsSingletonList("/rest/user/" + + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/" + userName); + assertUser(userDetails); + assertEquals("Unexpected user name", userName, userDetails.get(User.NAME)); + } + + public void testDelete() throws Exception + { + String userName = getTestName(); + getRestTestHelper().createOrUpdateUser(userName, "newPassword"); + + Map<String, Object> userDetails = getRestTestHelper().getJsonAsSingletonList("/rest/user/" + + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/" + userName); + String id = (String) userDetails.get(User.ID); + + getRestTestHelper().removeUserById(id); + + List<Map<String, Object>> users = getRestTestHelper().getJsonAsList("/rest/user/" + + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/" + userName); + assertEquals("User should be deleted", 0, users.size()); + } + + private void assertUser(Map<String, Object> user) + { + assertNotNull("Attribute " + User.ID, user.get(User.ID)); + assertNotNull("Attribute " + User.NAME, user.get(User.NAME)); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/VirtualHostRestTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/VirtualHostRestTest.java new file mode 100644 index 0000000000..fb2c941203 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/VirtualHostRestTest.java @@ -0,0 +1,576 @@ +/* + * + * 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; + +import java.io.File; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.jms.Session; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.XMLConfiguration; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.server.model.Exchange; +import org.apache.qpid.server.model.Queue; +import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.test.utils.TestFileUtils; +import org.apache.qpid.util.FileUtils; +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.map.JsonMappingException; + +public class VirtualHostRestTest extends QpidRestTestCase +{ + private static final String VIRTUALHOST_EXCHANGES_ATTRIBUTE = "exchanges"; + public static final String VIRTUALHOST_QUEUES_ATTRIBUTE = "queues"; + public static final String VIRTUALHOST_CONNECTIONS_ATTRIBUTE = "connections"; + + private AMQConnection _connection; + + public void testGet() throws Exception + { + List<Map<String, Object>> hosts = getRestTestHelper().getJsonAsList("/rest/virtualhost/"); + assertNotNull("Hosts data cannot be null", hosts); + assertEquals("Unexpected number of hosts", EXPECTED_VIRTUALHOSTS.length, hosts.size()); + for (String hostName : EXPECTED_VIRTUALHOSTS) + { + Map<String, Object> host = getRestTestHelper().find("name", hostName, hosts); + Asserts.assertVirtualHost(hostName, host); + } + } + + public void testGetHost() throws Exception + { + // create AMQP connection to get connection JSON details + _connection = (AMQConnection) getConnection(); + _connection.createSession(true, Session.SESSION_TRANSACTED); + + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); + Asserts.assertVirtualHost("test", hostDetails); + + @SuppressWarnings("unchecked") + Map<String, Object> statistics = (Map<String, Object>) hostDetails.get(Asserts.STATISTICS_ATTRIBUTE); + assertEquals("Unexpected number of exchanges in statistics", EXPECTED_EXCHANGES.length, statistics.get(VirtualHost.EXCHANGE_COUNT)); + assertEquals("Unexpected number of queues in statistics", EXPECTED_QUEUES.length, statistics.get(VirtualHost.QUEUE_COUNT)); + assertEquals("Unexpected number of connections in statistics", 1, statistics.get(VirtualHost.CONNECTION_COUNT)); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> exchanges = (List<Map<String, Object>>) hostDetails.get(VIRTUALHOST_EXCHANGES_ATTRIBUTE); + assertEquals("Unexpected number of exchanges", EXPECTED_EXCHANGES.length, exchanges.size()); + Asserts.assertDurableExchange("amq.fanout", "fanout", getRestTestHelper().find(Exchange.NAME, "amq.fanout", exchanges)); + Asserts.assertDurableExchange("amq.topic", "topic", getRestTestHelper().find(Exchange.NAME, "amq.topic", exchanges)); + Asserts.assertDurableExchange("amq.direct", "direct", getRestTestHelper().find(Exchange.NAME, "amq.direct", exchanges)); + Asserts.assertDurableExchange("amq.match", "headers", getRestTestHelper().find(Exchange.NAME, "amq.match", exchanges)); + Asserts.assertDurableExchange("<<default>>", "direct", getRestTestHelper().find(Exchange.NAME, "<<default>>", exchanges)); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> queues = (List<Map<String, Object>>) hostDetails.get(VIRTUALHOST_QUEUES_ATTRIBUTE); + assertEquals("Unexpected number of queues", EXPECTED_QUEUES.length, queues.size()); + Map<String, Object> queue = getRestTestHelper().find(Queue.NAME, "queue", queues); + Map<String, Object> ping = getRestTestHelper().find(Queue.NAME, "ping", queues); + Asserts.assertQueue("queue", "standard", queue); + Asserts.assertQueue("ping", "standard", ping); + assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.FALSE, queue.get(Queue.DURABLE)); + assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.FALSE, ping.get(Queue.DURABLE)); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> connections = (List<Map<String, Object>>) hostDetails + .get(VIRTUALHOST_CONNECTIONS_ATTRIBUTE); + assertEquals("Unexpected number of connections", 1, connections.size()); + Asserts.assertConnection(connections.get(0), _connection); + } + + public void testPutCreateVirtualHostUsingStoreType() throws Exception + { + String hostName = getTestName(); + String storeType = getTestProfileMessageStoreType(); + String storeLocation = createHost(hostName, storeType, null); + try + { + // make sure that the host is saved in the broker store + restartBroker(); + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/" + hostName); + Asserts.assertVirtualHost(hostName, hostDetails); + assertEquals("Unexpected store type", storeType, hostDetails.get(VirtualHost.STORE_TYPE)); + + assertNewVirtualHost(hostDetails); + } + finally + { + if (storeLocation != null) + { + FileUtils.delete(new File(storeLocation), true); + } + } + } + + public void testPutCreateVirtualHostUsingConfigPath() throws Exception + { + String hostName = getTestName(); + File configFile = TestFileUtils.createTempFile(this, hostName + "-config.xml"); + String configPath = configFile.getAbsolutePath(); + String storeLocation = getStoreLocation(hostName); + createAndSaveVirtualHostConfiguration(hostName, configFile, storeLocation); + createHost(hostName, null, configPath); + try + { + // make sure that the host is saved in the broker store + restartBroker(); + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/" + hostName); + Asserts.assertVirtualHost(hostName, hostDetails); + assertEquals("Unexpected config path", configPath, hostDetails.get(VirtualHost.CONFIG_PATH)); + + assertNewVirtualHost(hostDetails); + } + finally + { + if (storeLocation != null) + { + FileUtils.delete(new File(storeLocation), true); + } + configFile.delete(); + } + } + + public void testDeleteHost() throws Exception + { + String hostToDelete = TEST3_VIRTUALHOST; + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/virtualhost/" + hostToDelete, "DELETE"); + connection.connect(); + assertEquals("Unexpected response code", 200, connection.getResponseCode()); + + // make sure that changes are saved in the broker store + restartBroker(); + + List<Map<String, Object>> hosts = getRestTestHelper().getJsonAsList("/rest/virtualhost/" + hostToDelete); + assertEquals("Host should be deleted", 0, hosts.size()); + } + + public void testPutCreateQueue() throws Exception + { + String queueName = getTestQueueName(); + + createQueue(queueName + "-standard", "standard", null); + + Map<String, Object> sortedQueueAttributes = new HashMap<String, Object>(); + sortedQueueAttributes.put(Queue.SORT_KEY, "sortme"); + createQueue(queueName + "-sorted", "sorted", sortedQueueAttributes); + + Map<String, Object> priorityQueueAttributes = new HashMap<String, Object>(); + priorityQueueAttributes.put(Queue.PRIORITIES, 10); + createQueue(queueName + "-priority", "priority", priorityQueueAttributes); + + Map<String, Object> lvqQueueAttributes = new HashMap<String, Object>(); + lvqQueueAttributes.put(Queue.LVQ_KEY, "LVQ"); + createQueue(queueName + "-lvq", "lvq", lvqQueueAttributes); + + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> queues = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE); + Map<String, Object> standardQueue = getRestTestHelper().find(Queue.NAME, queueName + "-standard" , queues); + Map<String, Object> sortedQueue = getRestTestHelper().find(Queue.NAME, queueName + "-sorted" , queues); + Map<String, Object> priorityQueue = getRestTestHelper().find(Queue.NAME, queueName + "-priority" , queues); + Map<String, Object> lvqQueue = getRestTestHelper().find(Queue.NAME, queueName + "-lvq" , queues); + + Asserts.assertQueue(queueName + "-standard", "standard", standardQueue); + Asserts.assertQueue(queueName + "-sorted", "sorted", sortedQueue); + Asserts.assertQueue(queueName + "-priority", "priority", priorityQueue); + Asserts.assertQueue(queueName + "-lvq", "lvq", lvqQueue); + + assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, standardQueue.get(Queue.DURABLE)); + assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, sortedQueue.get(Queue.DURABLE)); + assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, priorityQueue.get(Queue.DURABLE)); + assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, lvqQueue.get(Queue.DURABLE)); + + assertEquals("Unexpected sorted key attribute", "sortme", sortedQueue.get(Queue.SORT_KEY)); + assertEquals("Unexpected lvq key attribute", "LVQ", lvqQueue.get(Queue.LVQ_KEY)); + assertEquals("Unexpected priorities key attribute", 10, priorityQueue.get(Queue.PRIORITIES)); + } + + public void testPutCreateExchange() throws Exception + { + String exchangeName = getTestName(); + + createExchange(exchangeName + "-direct", "direct"); + createExchange(exchangeName + "-topic", "topic"); + createExchange(exchangeName + "-headers", "headers"); + createExchange(exchangeName + "-fanout", "fanout"); + + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> exchanges = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_EXCHANGES_ATTRIBUTE); + Map<String, Object> directExchange = getRestTestHelper().find(Queue.NAME, exchangeName + "-direct" , exchanges); + Map<String, Object> topicExchange = getRestTestHelper().find(Queue.NAME, exchangeName + "-topic" , exchanges); + Map<String, Object> headersExchange = getRestTestHelper().find(Queue.NAME, exchangeName + "-headers" , exchanges); + Map<String, Object> fanoutExchange = getRestTestHelper().find(Queue.NAME, exchangeName + "-fanout" , exchanges); + + Asserts.assertDurableExchange(exchangeName + "-direct", "direct", directExchange); + Asserts.assertDurableExchange(exchangeName + "-topic", "topic", topicExchange); + Asserts.assertDurableExchange(exchangeName + "-headers", "headers", headersExchange); + Asserts.assertDurableExchange(exchangeName + "-fanout", "fanout", fanoutExchange); + + assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, directExchange.get(Queue.DURABLE)); + assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, topicExchange.get(Queue.DURABLE)); + assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, headersExchange.get(Queue.DURABLE)); + assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, fanoutExchange.get(Queue.DURABLE)); + + } + + public void testPutCreateLVQWithoutKey() throws Exception + { + String queueName = getTestQueueName()+ "-lvq"; + createQueue(queueName, "lvq", null); + + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> queues = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE); + Map<String, Object> lvqQueue = getRestTestHelper().find(Queue.NAME, queueName , queues); + + Asserts.assertQueue(queueName , "lvq", lvqQueue); + assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, lvqQueue.get(Queue.DURABLE)); + assertEquals("Unexpected lvq key attribute", AMQQueueFactory.QPID_LVQ_KEY, lvqQueue.get(Queue.LVQ_KEY)); + } + + public void testPutCreateSortedQueueWithoutKey() throws Exception + { + String queueName = getTestQueueName() + "-sorted"; + int responseCode = tryCreateQueue(queueName, "sorted", null); + assertEquals("Unexpected response code", HttpServletResponse.SC_CONFLICT, responseCode); + + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> queues = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE); + Map<String, Object> testQueue = getRestTestHelper().find(Queue.NAME, queueName , queues); + + assertNull("Sorted queue without a key was created ", testQueue); + } + + public void testPutCreatePriorityQueueWithoutKey() throws Exception + { + String queueName = getTestQueueName()+ "-priority"; + createQueue(queueName, "priority", null); + + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> queues = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE); + Map<String, Object> priorityQueue = getRestTestHelper().find(Queue.NAME, queueName , queues); + + Asserts.assertQueue(queueName , "priority", priorityQueue); + assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, priorityQueue.get(Queue.DURABLE)); + assertEquals("Unexpected number of priorities", 10, priorityQueue.get(Queue.PRIORITIES)); + } + + public void testPutCreateStandardQueueWithoutType() throws Exception + { + String queueName = getTestQueueName(); + createQueue(queueName, null, null); + + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> queues = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE); + Map<String, Object> queue = getRestTestHelper().find(Queue.NAME, queueName , queues); + + Asserts.assertQueue(queueName , "standard", queue); + } + + public void testPutCreateQueueOfUnsupportedType() throws Exception + { + String queueName = getTestQueueName(); + int responseCode = tryCreateQueue(queueName, "unsupported", null); + assertEquals("Unexpected response code", HttpServletResponse.SC_CONFLICT, responseCode); + + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> queues = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE); + Map<String, Object> queue = getRestTestHelper().find(Queue.NAME, queueName , queues); + + assertNull("Queue of unsupported type was created", queue); + } + + public void testDeleteQueue() throws Exception + { + String queueName = getTestQueueName(); + createQueue(queueName, null, null); + + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/queue/test/" + queueName, "DELETE"); + connection.connect(); + assertEquals("Unexpected response code", 200, connection.getResponseCode()); + List<Map<String, Object>> queues = getRestTestHelper().getJsonAsList("/rest/queue/test/" + queueName); + assertEquals("Queue should be deleted", 0, queues.size()); + } + + public void testDeleteQueueById() throws Exception + { + String queueName = getTestQueueName(); + createQueue(queueName, null, null); + Map<String, Object> queueDetails = getRestTestHelper().getJsonAsSingletonList("/rest/queue/test/" + queueName); + + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/queue/test?id=" + queueDetails.get(Queue.ID), "DELETE"); + connection.connect(); + assertEquals("Unexpected response code", 200, connection.getResponseCode()); + List<Map<String, Object>> queues = getRestTestHelper().getJsonAsList("/rest/queue/test/" + queueName); + assertEquals("Queue should be deleted", 0, queues.size()); + } + + public void testDeleteExchange() throws Exception + { + String exchangeName = getTestName(); + createExchange(exchangeName, "direct"); + + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/exchange/test/" + exchangeName, "DELETE"); + connection.connect(); + assertEquals("Unexpected response code", 200, connection.getResponseCode()); + List<Map<String, Object>> queues = getRestTestHelper().getJsonAsList("/rest/exchange/test/" + exchangeName); + assertEquals("Exchange should be deleted", 0, queues.size()); + } + + public void testDeleteExchangeById() throws Exception + { + String exchangeName = getTestName(); + createExchange(exchangeName, "direct"); + Map<String, Object> echangeDetails = getRestTestHelper().getJsonAsSingletonList("/rest/exchange/test/" + exchangeName); + + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/exchange/test?id=" + echangeDetails.get(Exchange.ID), "DELETE"); + connection.connect(); + assertEquals("Unexpected response code", 200, connection.getResponseCode()); + List<Map<String, Object>> queues = getRestTestHelper().getJsonAsList("/rest/exchange/test/" + exchangeName); + assertEquals("Exchange should be deleted", 0, queues.size()); + } + + public void testPutCreateQueueWithAttributes() throws Exception + { + String queueName = getTestQueueName(); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Queue.ALERT_REPEAT_GAP, 1000); + attributes.put(Queue.ALERT_THRESHOLD_MESSAGE_AGE, 3600000); + attributes.put(Queue.ALERT_THRESHOLD_MESSAGE_SIZE, 1000000000); + attributes.put(Queue.ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, 800); + attributes.put(Queue.MAXIMUM_DELIVERY_ATTEMPTS, 15); + attributes.put(Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES, 2000000000); + attributes.put(Queue.QUEUE_FLOW_RESUME_SIZE_BYTES, 1500000000); + + createQueue(queueName + "-standard", "standard", attributes); + + Map<String, Object> sortedQueueAttributes = new HashMap<String, Object>(); + sortedQueueAttributes.putAll(attributes); + sortedQueueAttributes.put(Queue.SORT_KEY, "sortme"); + createQueue(queueName + "-sorted", "sorted", sortedQueueAttributes); + + Map<String, Object> priorityQueueAttributes = new HashMap<String, Object>(); + priorityQueueAttributes.putAll(attributes); + priorityQueueAttributes.put(Queue.PRIORITIES, 10); + createQueue(queueName + "-priority", "priority", priorityQueueAttributes); + + Map<String, Object> lvqQueueAttributes = new HashMap<String, Object>(); + lvqQueueAttributes.putAll(attributes); + lvqQueueAttributes.put(Queue.LVQ_KEY, "LVQ"); + createQueue(queueName + "-lvq", "lvq", lvqQueueAttributes); + + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> queues = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE); + Map<String, Object> standardQueue = getRestTestHelper().find(Queue.NAME, queueName + "-standard" , queues); + Map<String, Object> sortedQueue = getRestTestHelper().find(Queue.NAME, queueName + "-sorted" , queues); + Map<String, Object> priorityQueue = getRestTestHelper().find(Queue.NAME, queueName + "-priority" , queues); + Map<String, Object> lvqQueue = getRestTestHelper().find(Queue.NAME, queueName + "-lvq" , queues); + + attributes.put(Queue.DURABLE, Boolean.TRUE); + Asserts.assertQueue(queueName + "-standard", "standard", standardQueue, attributes); + Asserts.assertQueue(queueName + "-sorted", "sorted", sortedQueue, attributes); + Asserts.assertQueue(queueName + "-priority", "priority", priorityQueue, attributes); + Asserts.assertQueue(queueName + "-lvq", "lvq", lvqQueue, attributes); + + assertEquals("Unexpected sorted key attribute", "sortme", sortedQueue.get(Queue.SORT_KEY)); + assertEquals("Unexpected lvq key attribute", "LVQ", lvqQueue.get(Queue.LVQ_KEY)); + assertEquals("Unexpected priorities key attribute", 10, priorityQueue.get(Queue.PRIORITIES)); + } + + @SuppressWarnings("unchecked") + public void testCreateQueueWithDLQEnabled() throws Exception + { + String queueName = getTestQueueName(); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AMQQueueFactory.X_QPID_DLQ_ENABLED, true); + + //verify the starting state + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); + List<Map<String, Object>> queues = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE); + List<Map<String, Object>> exchanges = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_EXCHANGES_ATTRIBUTE); + + assertNull("queue should not have already been present", getRestTestHelper().find(Queue.NAME, queueName , queues)); + assertNull("queue should not have already been present", getRestTestHelper().find(Queue.NAME, queueName + "_DLQ" , queues)); + assertNull("exchange should not have already been present", getRestTestHelper().find(Exchange.NAME, queueName + "_DLE" , exchanges)); + + //create the queue + createQueue(queueName, "standard", attributes); + + //verify the new queue, as well as the DLQueue and DLExchange have been created + hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); + queues = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE); + exchanges = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_EXCHANGES_ATTRIBUTE); + + Map<String, Object> queue = getRestTestHelper().find(Queue.NAME, queueName , queues); + Map<String, Object> dlqQueue = getRestTestHelper().find(Queue.NAME, queueName + "_DLQ" , queues); + Map<String, Object> dlExchange = getRestTestHelper().find(Exchange.NAME, queueName + "_DLE" , exchanges); + assertNotNull("queue should not have been present", queue); + assertNotNull("queue should not have been present", dlqQueue); + assertNotNull("exchange should not have been present", dlExchange); + + //verify that the alternate exchange is set as expected on the new queue + Map<String, Object> queueAttributes = new HashMap<String, Object>(); + queueAttributes.put(Queue.ALTERNATE_EXCHANGE, queueName + "_DLE"); + + Asserts.assertQueue(queueName, "standard", queue, queueAttributes); + Asserts.assertQueue(queueName, "standard", queue, null); + } + + private void createExchange(String exchangeName, String exchangeType) throws IOException + { + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/exchange/test/" + exchangeName, "PUT"); + + Map<String, Object> queueData = new HashMap<String, Object>(); + queueData.put(Exchange.NAME, exchangeName); + queueData.put(Exchange.DURABLE, Boolean.TRUE); + queueData.put(Exchange.TYPE, exchangeType); + + getRestTestHelper().writeJsonRequest(connection, queueData); + assertEquals("Unexpected response code", 201, connection.getResponseCode()); + + connection.disconnect(); + } + + private void createQueue(String queueName, String queueType, Map<String, Object> attributes) throws IOException, + JsonGenerationException, JsonMappingException + { + int responseCode = tryCreateQueue(queueName, queueType, attributes); + assertEquals("Unexpected response code", 201, responseCode); + } + + private int tryCreateQueue(String queueName, String queueType, Map<String, Object> attributes) throws IOException, + JsonGenerationException, JsonMappingException + { + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/queue/test/" + queueName, "PUT"); + + Map<String, Object> queueData = new HashMap<String, Object>(); + queueData.put(Queue.NAME, queueName); + queueData.put(Queue.DURABLE, Boolean.TRUE); + if (queueType != null) + { + queueData.put(Queue.TYPE, queueType); + } + if (attributes != null) + { + queueData.putAll(attributes); + } + + getRestTestHelper().writeJsonRequest(connection, queueData); + int responseCode = connection.getResponseCode(); + connection.disconnect(); + return responseCode; + } + + private String createHost(String hostName, String storeType, String configPath) throws IOException, JsonGenerationException, + JsonMappingException + { + String storePath = getStoreLocation(hostName); + int responseCode = tryCreateVirtualHost(hostName, storeType, storePath, configPath); + assertEquals("Unexpected response code", 201, responseCode); + return storePath; + } + + private String getStoreLocation(String hostName) + { + return new File(TMP_FOLDER, "store-" + hostName + "-" + System.currentTimeMillis()).getAbsolutePath(); + } + + private int tryCreateVirtualHost(String hostName, String storeType, String storePath, String configPath) throws IOException, + JsonGenerationException, JsonMappingException + { + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/virtualhost/" + hostName, "PUT"); + + Map<String, Object> hostData = new HashMap<String, Object>(); + hostData.put(VirtualHost.NAME, hostName); + if (storeType == null) + { + hostData.put(VirtualHost.CONFIG_PATH, configPath); + } + else + { + hostData.put(VirtualHost.STORE_PATH, storePath); + hostData.put(VirtualHost.STORE_TYPE, storeType); + } + + getRestTestHelper().writeJsonRequest(connection, hostData); + int responseCode = connection.getResponseCode(); + connection.disconnect(); + return responseCode; + } + + private XMLConfiguration createAndSaveVirtualHostConfiguration(String hostName, File configFile, String storeLocation) + throws ConfigurationException + { + XMLConfiguration testConfiguration = new XMLConfiguration(); + testConfiguration.setProperty("virtualhosts.virtualhost." + hostName + ".store.class", + getTestProfileMessageStoreClassName()); + testConfiguration.setProperty("virtualhosts.virtualhost." + hostName + ".store.environment-path", storeLocation); + testConfiguration.save(configFile); + return testConfiguration; + } + + private void assertNewVirtualHost(Map<String, Object> hostDetails) + { + @SuppressWarnings("unchecked") + Map<String, Object> statistics = (Map<String, Object>) hostDetails.get(Asserts.STATISTICS_ATTRIBUTE); + assertEquals("Unexpected number of exchanges in statistics", EXPECTED_EXCHANGES.length, + statistics.get(VirtualHost.EXCHANGE_COUNT)); + assertEquals("Unexpected number of queues in statistics", 0, statistics.get(VirtualHost.QUEUE_COUNT)); + assertEquals("Unexpected number of connections in statistics", 0, statistics.get(VirtualHost.CONNECTION_COUNT)); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> exchanges = (List<Map<String, Object>>) hostDetails.get(VIRTUALHOST_EXCHANGES_ATTRIBUTE); + assertEquals("Unexpected number of exchanges", EXPECTED_EXCHANGES.length, exchanges.size()); + RestTestHelper restTestHelper = getRestTestHelper(); + Asserts.assertDurableExchange("amq.fanout", "fanout", restTestHelper.find(Exchange.NAME, "amq.fanout", exchanges)); + Asserts.assertDurableExchange("amq.topic", "topic", restTestHelper.find(Exchange.NAME, "amq.topic", exchanges)); + Asserts.assertDurableExchange("amq.direct", "direct", restTestHelper.find(Exchange.NAME, "amq.direct", exchanges)); + Asserts.assertDurableExchange("amq.match", "headers", restTestHelper.find(Exchange.NAME, "amq.match", exchanges)); + Asserts.assertDurableExchange("<<default>>", "direct", restTestHelper.find(Exchange.NAME, "<<default>>", exchanges)); + + assertNull("Unexpected queues", hostDetails.get(VIRTUALHOST_QUEUES_ATTRIBUTE)); + assertNull("Unexpected connections", hostDetails.get(VIRTUALHOST_CONNECTIONS_ATTRIBUTE)); + } + +} diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java new file mode 100644 index 0000000000..40ea723b1e --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java @@ -0,0 +1,197 @@ +/* + * + * 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.FileOutputStream; +import java.io.IOException; +import java.util.Map; +import java.util.Properties; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.security.acl.AbstractACLTestCase; +import org.apache.qpid.systest.rest.QpidRestTestCase; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class GroupRestACLTest extends QpidRestTestCase +{ + private static final String FILE_GROUP_MANAGER = "FileGroupManager"; + + private static final String ALLOWED_GROUP = "allowedGroup"; + private static final String DENIED_GROUP = "deniedGroup"; + private static final String OTHER_GROUP = "otherGroup"; + + private static final String ALLOWED_USER = "webadmin"; + private static final String DENIED_USER = "admin"; + private static final String OTHER_USER = "admin"; + + private File _groupFile; + + @Override + public void setUp() throws Exception + { + _groupFile = createTemporaryGroupFile(); + getBrokerConfiguration().setBrokerAttribute(Broker.GROUP_FILE, _groupFile.getAbsolutePath()); + + //DONT call super.setUp(), the tests will start the broker after configuring it + } + + @Override + protected void customizeConfiguration() throws ConfigurationException, IOException + { + super.customizeConfiguration(); + getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, "httpBasicAuthenticationEnabled", true); + } + + @Override + public void tearDown() throws Exception + { + super.tearDown(); + + if (_groupFile != null) + { + if (_groupFile.exists()) + { + _groupFile.delete(); + } + } + } + + private File createTemporaryGroupFile() throws Exception + { + File groupFile = File.createTempFile("group", "grp"); + groupFile.deleteOnExit(); + + Properties props = new Properties(); + props.put(ALLOWED_GROUP + ".users", ALLOWED_USER); + props.put(DENIED_GROUP + ".users", DENIED_USER); + props.put(OTHER_GROUP + ".users", OTHER_USER); + + props.store(new FileOutputStream(groupFile), "test group file"); + + return groupFile; + } + + public void testCreateGroup() throws Exception + { + AbstractACLTestCase.writeACLFileUtil(this, null, + "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + "ACL ALLOW-LOG " + ALLOWED_GROUP + " CREATE GROUP", + "ACL DENY-LOG " + DENIED_GROUP + " CREATE GROUP"); + + //Start the broker with the custom config + super.setUp(); + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + Map<String, Object> data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + FILE_GROUP_MANAGER); + getRestTestHelper().assertNumberOfGroups(data, 3); + + getRestTestHelper().createGroup("newGroup", FILE_GROUP_MANAGER); + + data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + FILE_GROUP_MANAGER); + getRestTestHelper().assertNumberOfGroups(data, 4); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + getRestTestHelper().createGroup("anotherNewGroup", FILE_GROUP_MANAGER, HttpServletResponse.SC_FORBIDDEN); + + data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + FILE_GROUP_MANAGER); + getRestTestHelper().assertNumberOfGroups(data, 4); + } + + public void testDeleteGroup() throws Exception + { + AbstractACLTestCase.writeACLFileUtil(this, null, + "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + "ACL ALLOW-LOG " + ALLOWED_GROUP + " DELETE GROUP", + "ACL DENY-LOG " + DENIED_GROUP + " DELETE GROUP"); + + //Start the broker with the custom config + super.setUp(); + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + Map<String, Object> data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + FILE_GROUP_MANAGER); + getRestTestHelper().assertNumberOfGroups(data, 3); + + getRestTestHelper().removeGroup(OTHER_GROUP, FILE_GROUP_MANAGER, HttpServletResponse.SC_FORBIDDEN); + + data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + FILE_GROUP_MANAGER); + getRestTestHelper().assertNumberOfGroups(data, 3); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + getRestTestHelper().removeGroup(OTHER_GROUP, FILE_GROUP_MANAGER); + + data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + FILE_GROUP_MANAGER); + getRestTestHelper().assertNumberOfGroups(data, 2); + } + + public void testUpdateGroupAddMember() throws Exception + { + AbstractACLTestCase.writeACLFileUtil(this, null, + "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + "ACL ALLOW-LOG " + ALLOWED_GROUP + " UPDATE GROUP", + "ACL DENY-LOG " + DENIED_GROUP + " UPDATE GROUP"); + + //Start the broker with the custom config + super.setUp(); + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + assertNumberOfGroupMembers(OTHER_GROUP, 1); + + getRestTestHelper().createNewGroupMember(FILE_GROUP_MANAGER, OTHER_GROUP, "newGroupMember", HttpServletResponse.SC_FORBIDDEN); + assertNumberOfGroupMembers(OTHER_GROUP, 1); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + getRestTestHelper().createNewGroupMember(FILE_GROUP_MANAGER, OTHER_GROUP, "newGroupMember"); + assertNumberOfGroupMembers(OTHER_GROUP, 2); + } + + public void testUpdateGroupDeleteMember() throws Exception + { + AbstractACLTestCase.writeACLFileUtil(this, null, + "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + "ACL ALLOW-LOG " + ALLOWED_GROUP + " UPDATE GROUP", + "ACL DENY-LOG " + DENIED_GROUP + " UPDATE GROUP"); + + //Start the broker with the custom config + super.setUp(); + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + assertNumberOfGroupMembers(OTHER_GROUP, 1); + + getRestTestHelper().removeMemberFromGroup(FILE_GROUP_MANAGER, OTHER_GROUP, OTHER_USER, HttpServletResponse.SC_FORBIDDEN); + assertNumberOfGroupMembers(OTHER_GROUP, 1); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + getRestTestHelper().removeMemberFromGroup(FILE_GROUP_MANAGER, OTHER_GROUP, OTHER_USER); + assertNumberOfGroupMembers(OTHER_GROUP, 0); + } + + private void assertNumberOfGroupMembers(String groupName, int expectedNumberOfMembers) throws IOException + { + Map<String, Object> group = getRestTestHelper().getJsonAsSingletonList("/rest/group/FileGroupManager/" + groupName); + getRestTestHelper().assertNumberOfGroupMembers(group, expectedNumberOfMembers); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java b/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java new file mode 100644 index 0000000000..12973113d8 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java @@ -0,0 +1,200 @@ +/* + * 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.FileOutputStream; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.server.model.Broker; +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.JsonParseException; +import org.codehaus.jackson.map.JsonMappingException; + +public class UserRestACLTest extends QpidRestTestCase +{ + private static final String ALLOWED_GROUP = "allowedGroup"; + private static final String DENIED_GROUP = "deniedGroup"; + private static final String OTHER_GROUP = "otherGroup"; + + private static final String ALLOWED_USER = "webadmin"; + private static final String DENIED_USER = "admin"; + private static final String OTHER_USER = "other"; + + private File _groupFile; + + @Override + public void setUp() throws Exception + { + _groupFile = createTemporaryGroupFile(); + getBrokerConfiguration().setBrokerAttribute(Broker.GROUP_FILE, _groupFile.getAbsolutePath()); + + getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER, OTHER_USER); + + //DONT call super.setUp(), the tests will start the broker after configuring it + } + + @Override + protected void customizeConfiguration() throws ConfigurationException, IOException + { + super.customizeConfiguration(); + getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, "httpBasicAuthenticationEnabled", true); + } + + @Override + public void tearDown() throws Exception + { + super.tearDown(); + + if (_groupFile != null) + { + if (_groupFile.exists()) + { + _groupFile.delete(); + } + } + } + + private File createTemporaryGroupFile() throws Exception + { + File groupFile = File.createTempFile("group", "grp"); + groupFile.deleteOnExit(); + + Properties props = new Properties(); + props.put(ALLOWED_GROUP + ".users", ALLOWED_USER); + props.put(DENIED_GROUP + ".users", DENIED_USER); + props.put(OTHER_GROUP + ".users", OTHER_USER); + + props.store(new FileOutputStream(groupFile), "test group file"); + + return groupFile; + } + + public void testAddUser() throws Exception + { + AbstractACLTestCase.writeACLFileUtil(this, null, + "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + "ACL ALLOW-LOG " + ALLOWED_GROUP + " CREATE USER", + "ACL DENY-LOG " + DENIED_GROUP + " CREATE USER"); + + //Start the broker with the custom config + super.setUp(); + + String newUser = "newUser"; + String password = "password"; + + assertUserDoesNotExist(newUser); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + getRestTestHelper().createOrUpdateUser(newUser, password, HttpServletResponse.SC_FORBIDDEN); + assertUserDoesNotExist(newUser); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + getRestTestHelper().createOrUpdateUser(newUser, password); + assertUserExists(newUser); + } + + public void testDeleteUser() throws Exception + { + AbstractACLTestCase.writeACLFileUtil(this, null, + "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + "ACL ALLOW-LOG " + ALLOWED_GROUP + " DELETE USER", + "ACL DENY-LOG " + DENIED_GROUP + " DELETE USER"); + + //Start the broker with the custom config + super.setUp(); + + assertUserExists(OTHER_USER); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + getRestTestHelper().removeUser(OTHER_USER, HttpServletResponse.SC_FORBIDDEN); + assertUserExists(OTHER_USER); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + getRestTestHelper().removeUser(OTHER_USER); + assertUserDoesNotExist(OTHER_USER); + } + + public void testUpdateUser() throws Exception + { + AbstractACLTestCase.writeACLFileUtil(this, null, + "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + "ACL ALLOW-LOG " + ALLOWED_GROUP + " UPDATE USER", + "ACL DENY-LOG " + DENIED_GROUP + " UPDATE USER"); + + //Start the broker with the custom config + super.setUp(); + + String newPassword = "newPassword"; + + checkPassword(OTHER_USER, OTHER_USER, true); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + getRestTestHelper().createOrUpdateUser(OTHER_USER, newPassword, HttpServletResponse.SC_FORBIDDEN); + + checkPassword(OTHER_USER, newPassword, false); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + getRestTestHelper().createOrUpdateUser(OTHER_USER, newPassword, HttpServletResponse.SC_OK); // expect SC_OK rather than the default SC_CREATED + + checkPassword(OTHER_USER, newPassword, true); + checkPassword(OTHER_USER, OTHER_USER, false); + } + + private void checkPassword(String username, String password, boolean passwordExpectedToBeCorrect) throws IOException + { + getRestTestHelper().setUsernameAndPassword(username, password); + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/user/" + + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/", "GET"); + + boolean passwordIsCorrect = connection.getResponseCode() == HttpServletResponse.SC_OK; + + connection.disconnect(); + + assertEquals(passwordExpectedToBeCorrect, passwordIsCorrect); + } + + private void assertUserDoesNotExist(String newUser) throws JsonParseException, JsonMappingException, IOException + { + String path = "/rest/user/" + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/" + newUser; + List<Map<String, Object>> userDetailsList = getRestTestHelper().getJsonAsList(path); + assertTrue(userDetailsList.isEmpty()); + } + + private void assertUserExists(String username) throws IOException + { + String path = "/rest/user/" + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/" + username; + Map<String, Object> userDetails = getRestTestHelper().getJsonAsSingletonList(path); + + assertEquals( + "User returned by " + path + " should have name=" + username + ". The returned JSON was: " + userDetails, + username, + userDetails.get("name")); + } +} |