diff options
Diffstat (limited to 'qpid/java/systests/src/test/java/org/apache/qpid/systest')
50 files changed, 11647 insertions, 0 deletions
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/BrokerManagementTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/BrokerManagementTest.java new file mode 100644 index 0000000000..f6b56f64ce --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/BrokerManagementTest.java @@ -0,0 +1,119 @@ +/* + * 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.management.jmx; + +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.management.common.mbeans.ManagedBroker; +import org.apache.qpid.management.common.mbeans.ManagedExchange; +import org.apache.qpid.test.utils.JMXTestUtils; +import org.apache.qpid.test.utils.QpidBrokerTestCase; + +/** + * Tests the JMX API for the Managed Broker. + * + */ +public class BrokerManagementTest extends QpidBrokerTestCase +{ + private static final String VIRTUAL_HOST = "test"; + + /** + * JMX helper. + */ + private JMXTestUtils _jmxUtils; + private ManagedBroker _managedBroker; + + public void setUp() throws Exception + { + getBrokerConfiguration().addJmxManagementConfiguration(); + + _jmxUtils = new JMXTestUtils(this); + + super.setUp(); + _jmxUtils.open(); + _managedBroker = _jmxUtils.getManagedBroker(VIRTUAL_HOST); + } + + public void tearDown() throws Exception + { + if (_jmxUtils != null) + { + _jmxUtils.close(); + } + super.tearDown(); + } + + /** + * Tests queue creation/deletion also verifying the automatic binding to the default exchange. + */ + public void testCreateQueueAndDeletion() throws Exception + { + final String queueName = getTestQueueName(); + + + _managedBroker.createNewQueue(queueName, "testowner", true); + + // Ensure the queue exists + assertNotNull("Queue object name expected to exist", _jmxUtils.getQueueObjectName(VIRTUAL_HOST, queueName)); + assertNotNull("Manager queue expected to be available", _jmxUtils.getManagedQueue(queueName)); + + + // Now delete the queue + _managedBroker.deleteQueue(queueName); + + + } + + /** + * Tests exchange creation/deletion via JMX API. + */ + public void testCreateExchangeAndUnregister() throws Exception + { + String exchangeName = getTestName(); + _managedBroker.createNewExchange(exchangeName, "topic", true); + + ManagedExchange exchange = _jmxUtils.getManagedExchange(exchangeName); + assertNotNull("Exchange should exist", exchange); + + _managedBroker.unregisterExchange(exchangeName); + } + + /** + * Tests that it is disallowed to unregister the default exchange. + */ + public void testUnregisterOfAmqDirectExchangeDisallowed() throws Exception + { + String amqDirectExchangeName = "amq.direct"; + + ManagedExchange amqDirectExchange = _jmxUtils.getManagedExchange(amqDirectExchangeName); + assertNotNull("Exchange should exist", amqDirectExchange); + try + { + _managedBroker.unregisterExchange(amqDirectExchangeName); + fail("Exception not thrown"); + } + catch (UnsupportedOperationException e) + { + // PASS + assertEquals("'"+amqDirectExchangeName+"' is a reserved exchange and can't be deleted", e.getMessage()); + } + amqDirectExchange = _jmxUtils.getManagedExchange(amqDirectExchangeName); + assertNotNull("Exchange should exist", amqDirectExchange); + } + +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/ConnectionManagementTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/ConnectionManagementTest.java new file mode 100644 index 0000000000..34b13dfaca --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/ConnectionManagementTest.java @@ -0,0 +1,285 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.systest.management.jmx; + +import java.io.IOException; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.management.JMException; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.TabularData; + +import org.apache.commons.lang.StringUtils; +import org.apache.qpid.common.QpidProperties; +import org.apache.qpid.management.common.mbeans.ManagedConnection; +import org.apache.qpid.management.common.mbeans.ManagedQueue; +import org.apache.qpid.test.utils.JMXTestUtils; +import org.apache.qpid.test.utils.QpidBrokerTestCase; + +public class ConnectionManagementTest extends QpidBrokerTestCase +{ + private static final String VIRTUAL_HOST_NAME = "test"; + + private JMXTestUtils _jmxUtils; + private Connection _connection; + + public void setUp() throws Exception + { + getBrokerConfiguration().addJmxManagementConfiguration(); + + _jmxUtils = new JMXTestUtils(this); + + super.setUp(); + _jmxUtils.open(); + } + + public void tearDown() throws Exception + { + try + { + if (_jmxUtils != null) + { + _jmxUtils.close(); + } + } + finally + { + super.tearDown(); + } + } + + public void testNumberOfManagedConnectionsMatchesNumberOfClientConnections() throws Exception + { + assertEquals("Expected no managed connections", 0, getManagedConnections().size()); + + _connection = getConnection(); + assertEquals("Expected one managed connection", 1, getManagedConnections().size()); + + _connection.close(); + assertEquals("Expected no managed connections after client connection closed", 0, getManagedConnections().size()); + } + + public void testGetAttributes() throws Exception + { + _connection = getConnection(); + final ManagedConnection mBean = getConnectionMBean(); + + checkAuthorisedId(mBean); + checkClientVersion(mBean); + checkClientId(mBean); + } + + public void testNonTransactedSession() throws Exception + { + _connection = getConnection(); + + boolean transactional = false; + boolean flowBlocked = false; + + _connection.createSession(transactional, Session.AUTO_ACKNOWLEDGE); + + final ManagedConnection mBean = getConnectionMBean(); + final CompositeDataSupport row = getTheOneChannelRow(mBean); + assertChannelRowData(row, 0, transactional, flowBlocked); + } + + public void testTransactedSessionWithUnackMessages() throws Exception + { + _connection = getConnection(); + _connection.start(); + + boolean transactional = true; + int numberOfMessages = 2; + final Session session = _connection.createSession(transactional, Session.SESSION_TRANSACTED); + final Destination destination = session.createQueue(getTestQueueName()); + final MessageConsumer consumer = session.createConsumer(destination); + + sendMessage(session, destination, numberOfMessages); + receiveMessagesWithoutCommit(consumer, numberOfMessages); + + final ManagedConnection mBean = getConnectionMBean(); + final CompositeDataSupport row = getTheOneChannelRow(mBean); + boolean flowBlocked = false; + assertChannelRowData(row, numberOfMessages, transactional, flowBlocked); + + // check that commit advances the lastIoTime + final Date initialLastIOTime = mBean.getLastIoTime(); + session.commit(); + assertTrue("commit should have caused last IO time to advance", mBean.getLastIoTime().after(initialLastIOTime)); + + // check that channels() now returns one session with no unacknowledged messages + final CompositeDataSupport rowAfterCommit = getTheOneChannelRow(mBean); + final Number unackCountAfterCommit = (Number) rowAfterCommit.get(ManagedConnection.UNACKED_COUNT); + assertEquals("Unexpected number of unacknowledged messages", 0, unackCountAfterCommit); + } + + + public void testProducerFlowBlocked() throws Exception + { + _connection = getConnection(); + _connection.start(); + + String queueName = getTestQueueName(); + Session session = _connection.createSession(true, Session.SESSION_TRANSACTED); + Queue queue = session.createQueue(queueName); + createQueueOnBroker(session, queue); + + ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName); + managedQueue.setFlowResumeCapacity(DEFAULT_MESSAGE_SIZE * 2l); + managedQueue.setCapacity(DEFAULT_MESSAGE_SIZE * 3l); + + final ManagedConnection managedConnection = getConnectionMBean(); + + // Check that producer flow is not block before test + final CompositeDataSupport rowBeforeSend = getTheOneChannelRow(managedConnection); + assertFlowBlocked(rowBeforeSend, false); + + + // Check that producer flow does not become block too soon + sendMessage(session, queue, 3); + final CompositeDataSupport rowBeforeFull = getTheOneChannelRow(managedConnection); + assertFlowBlocked(rowBeforeFull, false); + + // Fourth message will over-fill the queue (but as we are not sending more messages, client thread wont't block) + sendMessage(session, queue, 1); + final CompositeDataSupport rowAfterFull = getTheOneChannelRow(managedConnection); + assertFlowBlocked(rowAfterFull, true); + + // Consume two to bring the queue down to the resume capacity + MessageConsumer consumer = session.createConsumer(queue); + assertNotNull("Could not receive first message", consumer.receive(1000)); + assertNotNull("Could not receive second message", consumer.receive(1000)); + session.commit(); + + // Check that producer flow is no longer blocked + final CompositeDataSupport rowAfterReceive = getTheOneChannelRow(managedConnection); + assertFlowBlocked(rowAfterReceive, false); + } + + private void createQueueOnBroker(Session session, Destination destination) throws JMSException + { + session.createConsumer(destination).close(); // Create a consumer only to cause queue creation + } + + private void assertChannelRowData(final CompositeData row, int unacknowledgedMessages, boolean isTransactional, boolean flowBlocked) + { + assertNotNull(row); + assertEquals("Unexpected transactional flag", isTransactional, row.get(ManagedConnection.TRANSACTIONAL)); + assertEquals("Unexpected unacknowledged message count", unacknowledgedMessages, row.get(ManagedConnection.UNACKED_COUNT)); + assertEquals("Unexpected flow blocked", flowBlocked, row.get(ManagedConnection.FLOW_BLOCKED)); + } + + private void assertFlowBlocked(final CompositeData row, boolean flowBlocked) + { + assertNotNull(row); + assertEquals("Unexpected flow blocked", flowBlocked, row.get(ManagedConnection.FLOW_BLOCKED)); + } + + private void checkAuthorisedId(ManagedConnection mBean) throws Exception + { + assertEquals("Unexpected authorized id", GUEST_USERNAME, mBean.getAuthorizedId()); + } + + private void checkClientVersion(ManagedConnection mBean) throws Exception + { + String expectedVersion = QpidProperties.getReleaseVersion(); + assertTrue(StringUtils.isNotBlank(expectedVersion)); + + assertEquals("Unexpected version", expectedVersion, mBean.getVersion()); + } + + private void checkClientId(ManagedConnection mBean) throws Exception + { + String expectedClientId = _connection.getClientID(); + assertTrue(StringUtils.isNotBlank(expectedClientId)); + + assertEquals("Unexpected ClientId", expectedClientId, mBean.getClientId()); + } + + private ManagedConnection getConnectionMBean() + { + List<ManagedConnection> connections = getManagedConnections(); + assertNotNull("Connection MBean is not found", connections); + assertEquals("Unexpected number of connection mbeans", 1, connections.size()); + final ManagedConnection mBean = connections.get(0); + assertNotNull("Connection MBean is null", mBean); + return mBean; + } + + private List<ManagedConnection> getManagedConnections() + { + return _jmxUtils.getManagedConnections(VIRTUAL_HOST_NAME); + } + + private CompositeDataSupport getTheOneChannelRow(final ManagedConnection mBean) throws Exception + { + TabularData channelsData = getChannelsDataWithRetry(mBean); + + assertEquals("Unexpected number of rows in channel table", 1, channelsData.size()); + + @SuppressWarnings("unchecked") + final Iterator<CompositeDataSupport> rowItr = (Iterator<CompositeDataSupport>) channelsData.values().iterator(); + final CompositeDataSupport row = rowItr.next(); + return row; + } + + private void receiveMessagesWithoutCommit(final MessageConsumer consumer, int numberOfMessages) throws Exception + { + for (int i = 0; i < numberOfMessages; i++) + { + final Message m = consumer.receive(1000l); + assertNotNull("Message " + i + " is not received", m); + } + } + + private TabularData getChannelsDataWithRetry(final ManagedConnection mBean) + throws IOException, JMException + { + TabularData channelsData = mBean.channels(); + int retries = 0; + while(channelsData.size() == 0 && retries < 5) + { + sleep(); + channelsData = mBean.channels(); + retries++; + } + return channelsData; + } + + private void sleep() + { + try + { + Thread.sleep(50); + } + catch (InterruptedException ie) + { + Thread.currentThread().interrupt(); + } + }} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/ExchangeManagementTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/ExchangeManagementTest.java new file mode 100644 index 0000000000..8c0a11b7cc --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/ExchangeManagementTest.java @@ -0,0 +1,164 @@ +/* + * + * 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.management.jmx; + +import java.util.Collections; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; + +import org.apache.qpid.common.AMQPFilterTypes; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.management.common.mbeans.ManagedBroker; +import org.apache.qpid.management.common.mbeans.ManagedExchange; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.test.utils.JMXTestUtils; +import org.apache.qpid.test.utils.QpidBrokerTestCase; + +public class ExchangeManagementTest extends QpidBrokerTestCase +{ + private static final String MESSAGE_PROPERTY_INDEX = "index"; + private static final String MESSAGE_PROPERTY_TEST = "test"; + private static final String MESSAGE_PROPERTY_DUMMY = "dummy"; + private static final String SELECTOR_ARGUMENT = AMQPFilterTypes.JMS_SELECTOR.toString(); + private static final String SELECTOR = MESSAGE_PROPERTY_TEST + "='test'"; + private static final String VIRTUAL_HOST = "test"; + + private JMXTestUtils _jmxUtils; + private ManagedBroker _managedBroker; + private String _testQueueName; + private ManagedExchange _directExchange; + private ManagedExchange _topicExchange; + private ManagedExchange _fanoutExchange; + private ManagedExchange _headersExchange; + private Connection _connection; + private Session _session; + + public void setUp() throws Exception + { + getBrokerConfiguration().addJmxManagementConfiguration(); + + // to test exchange selectors the publishing of unroutable messages should be allowed + getBrokerConfiguration().setBrokerAttribute(Broker.CONNECTION_CLOSE_WHEN_NO_ROUTE, false); + + _jmxUtils = new JMXTestUtils(this); + + super.setUp(); + + _jmxUtils.open(); + + _managedBroker = _jmxUtils.getManagedBroker(VIRTUAL_HOST); + _testQueueName = getTestName(); + _managedBroker.createNewQueue(_testQueueName, null, true); + _directExchange = _jmxUtils.getManagedExchange(ExchangeDefaults.DIRECT_EXCHANGE_NAME); + _topicExchange = _jmxUtils.getManagedExchange(ExchangeDefaults.TOPIC_EXCHANGE_NAME); + _fanoutExchange = _jmxUtils.getManagedExchange(ExchangeDefaults.FANOUT_EXCHANGE_NAME); + _headersExchange = _jmxUtils.getManagedExchange(ExchangeDefaults.HEADERS_EXCHANGE_NAME); + + _connection = getConnection(); + _connection.start(); + _session = _connection.createSession(true, Session.SESSION_TRANSACTED); + } + + public void testCreateNewBindingWithArgumentsOnDirectExchange() throws Exception + { + String bindingKey = "test-direct-binding"; + + _directExchange.createNewBinding(_testQueueName, bindingKey, + Collections.<String, Object> singletonMap(SELECTOR_ARGUMENT, SELECTOR)); + + bindingTest(_session.createQueue(bindingKey)); + } + + public void testCreateNewBindingWithArgumentsOnTopicExchange() throws Exception + { + String bindingKey = "test-topic-binding"; + + _topicExchange.createNewBinding(_testQueueName, bindingKey, + Collections.<String, Object> singletonMap(SELECTOR_ARGUMENT, SELECTOR)); + + bindingTest(_session.createTopic(bindingKey)); + } + + public void testCreateNewBindingWithArgumentsOnFanoutExchange() throws Exception + { + _fanoutExchange.createNewBinding(_testQueueName, null, + Collections.<String, Object> singletonMap(SELECTOR_ARGUMENT, SELECTOR)); + + bindingTest(_session.createQueue("fanout://amq.fanout//?routingkey='routing-key-must-not-be-null'")); + } + + public void testCreateNewBindingWithArgumentsOnHeadersExchange() throws Exception + { + // headers exchange uses 'dummy' property to match messages + // i.e. all test messages have matching header value + _headersExchange.createNewBinding(_testQueueName, "x-match=any,dummy=test", + Collections.<String, Object> singletonMap(SELECTOR_ARGUMENT, SELECTOR)); + + bindingTest(_session.createQueue("headers://amq.match//?routingkey='routing-key-must-not-be-null'")); + } + + private void bindingTest(Destination destination) throws JMSException + { + publishMessages(destination, 4); + receiveAndAssertMessages(2); + } + + private void publishMessages(Destination destination, int messageNumber) throws JMSException + { + MessageProducer producer = _session.createProducer(destination); + + for (int i = 0; i < messageNumber; i++) + { + Message m = _session.createMessage(); + m.setStringProperty(MESSAGE_PROPERTY_TEST, i % 2 == 0 ? MESSAGE_PROPERTY_TEST : ""); + m.setIntProperty(MESSAGE_PROPERTY_INDEX, i); + m.setStringProperty(MESSAGE_PROPERTY_DUMMY, "test"); + producer.send(m); + } + _session.commit(); + } + + private void receiveAndAssertMessages(int messageNumber) throws JMSException + { + MessageConsumer consumer = _session.createConsumer(_session.createQueue(_testQueueName)); + + for (int i = 0; i < messageNumber; i++) + { + int index = i * 2; + Message message = consumer.receive(1000l); + assertNotNull("Expected message is not received at " + i, message); + assertEquals("Unexpected test property at " + i, MESSAGE_PROPERTY_TEST, + message.getStringProperty(MESSAGE_PROPERTY_TEST)); + assertEquals("Unexpected index property at " + i, index, message.getIntProperty(MESSAGE_PROPERTY_INDEX)); + } + + Message message = consumer.receive(1000l); + assertNull("Unexpected message received", message); + _session.commit(); + } + +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/LoggingManagementTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/LoggingManagementTest.java new file mode 100644 index 0000000000..3717c1594d --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/LoggingManagementTest.java @@ -0,0 +1,148 @@ +/* + * 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.management.jmx; + +import java.io.File; +import java.util.List; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.TabularData; + +import org.apache.qpid.management.common.mbeans.LoggingManagement; +import org.apache.qpid.server.logging.log4j.LoggingManagementFacadeTest; +import org.apache.qpid.test.utils.JMXTestUtils; +import org.apache.qpid.test.utils.QpidBrokerTestCase; +import org.apache.qpid.util.FileUtils; +import org.apache.qpid.util.LogMonitor; + +/** + * System test for Logging Management. <b>These tests rely on value set within + * test-profiles/log4j-test.xml</b>. + * + * @see LoggingManagementMBeanTest + * @see LoggingManagementFacadeTest + * + */ +public class LoggingManagementTest extends QpidBrokerTestCase +{ + private JMXTestUtils _jmxUtils; + private LoggingManagement _loggingManagement; + private LogMonitor _monitor; + + public void setUp() throws Exception + { + getBrokerConfiguration().addJmxManagementConfiguration(); + + _jmxUtils = new JMXTestUtils(this); + + // System test normally run with log for4j test config from beneath test-profiles. We need to + // copy it as some of our tests write to this file. + + File tmpLogFile = File.createTempFile("log4j" + "." + getName(), ".xml"); + tmpLogFile.deleteOnExit(); + FileUtils.copy(getBrokerCommandLog4JFile(), tmpLogFile); + setBrokerCommandLog4JFile(tmpLogFile); + + super.setUp(); + _jmxUtils.open(); + + _loggingManagement = _jmxUtils.getLoggingManagement(); + _monitor = new LogMonitor(_outputFile); + } + + public void tearDown() throws Exception + { + try + { + if (_jmxUtils != null) + { + _jmxUtils.close(); + } + } + finally + { + super.tearDown(); + } + } + + public void testViewEffectiveRuntimeLoggerLevels() throws Exception + { + final String qpidMainLogger = "org.apache.qpid"; + + TabularData table = _loggingManagement.viewEffectiveRuntimeLoggerLevels(); + final CompositeData row = table.get(new String[] {qpidMainLogger} ); + assertChannelRow(row, qpidMainLogger, "DEBUG"); + } + + public void testViewConfigFileLoggerLevels() throws Exception + { + final String operationalLoggingLogger = "qpid.message"; + + TabularData table = _loggingManagement.viewConfigFileLoggerLevels(); + final CompositeData row = table.get(new String[] {operationalLoggingLogger} ); + assertChannelRow(row, operationalLoggingLogger, "INFO"); + } + + public void testTurnOffOrgApacheQpidAtRuntime() throws Exception + { + final String logger = "org.apache.qpid"; + _monitor.markDiscardPoint(); + _loggingManagement.setRuntimeLoggerLevel(logger, "OFF"); + + List<String> matches = _monitor.waitAndFindMatches("Setting level to OFF for logger 'org.apache.qpid'", 5000); + assertEquals(1, matches.size()); + + TabularData table = _loggingManagement.viewEffectiveRuntimeLoggerLevels(); + final CompositeData row1 = table.get(new String[] {logger} ); + assertChannelRow(row1, logger, "OFF"); + } + + public void testChangesToConfigFileBecomeEffectiveAfterReload() throws Exception + { + final String operationalLoggingLogger = "qpid.message"; + assertEffectiveLoggingLevel(operationalLoggingLogger, "INFO"); + + _monitor.markDiscardPoint(); + _loggingManagement.setConfigFileLoggerLevel(operationalLoggingLogger, "OFF"); + + List<String> matches = _monitor.waitAndFindMatches("Setting level to OFF for logger 'qpid.message'", 5000); + assertEquals(1, matches.size()); + + assertEffectiveLoggingLevel(operationalLoggingLogger, "INFO"); + + _loggingManagement.reloadConfigFile(); + + assertEffectiveLoggingLevel(operationalLoggingLogger, "OFF"); + } + + private void assertEffectiveLoggingLevel(String operationalLoggingLogger, String expectedLevel) + { + TabularData table = _loggingManagement.viewEffectiveRuntimeLoggerLevels(); + final CompositeData row1 = table.get(new String[] {operationalLoggingLogger} ); + assertChannelRow(row1, operationalLoggingLogger, expectedLevel); + } + + private void assertChannelRow(final CompositeData row, String logger, String level) + { + assertNotNull("No row for " + logger, row); + assertEquals("Unexpected logger name", logger, row.get(LoggingManagement.LOGGER_NAME)); + assertEquals("Unexpected level", level, row.get(LoggingManagement.LOGGER_LEVEL)); + } + +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/MBeanLifeCycleTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/MBeanLifeCycleTest.java new file mode 100644 index 0000000000..71f911627e --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/MBeanLifeCycleTest.java @@ -0,0 +1,140 @@ +/* + * + * 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.management.jmx; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.management.ObjectName; +import javax.servlet.http.HttpServletResponse; + +import org.apache.qpid.management.common.mbeans.ManagedBroker; +import org.apache.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.model.Plugin; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.VirtualHostNode; +import org.apache.qpid.server.security.auth.manager.AnonymousAuthenticationManager; +import org.apache.qpid.server.virtualhost.ProvidedStoreVirtualHostImpl; +import org.apache.qpid.server.virtualhostnode.memory.MemoryVirtualHostNode; +import org.apache.qpid.systest.rest.QpidRestTestCase; +import org.apache.qpid.test.utils.JMXTestUtils; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class MBeanLifeCycleTest extends QpidRestTestCase +{ + private final static String TEST_VIRTUAL_HOST_MBEAN_SEARCH_QUERY = "org.apache.qpid:type=VirtualHost.VirtualHostManager,VirtualHost=" + + ObjectName.quote(TEST2_VIRTUALHOST); + private JMXTestUtils _jmxUtils; + + @Override + public void setUp() throws Exception + { + super.setUp(); + _jmxUtils = new JMXTestUtils(this); + _jmxUtils.open(); + } + + @Override + protected void customizeConfiguration() throws IOException + { + TestBrokerConfiguration config = getBrokerConfiguration(); + config.addHttpManagementConfiguration(); + config.setObjectAttribute(Port.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, Port.PORT, getRestTestHelper().getHttpPort()); + + Map<String, Object> anonymousProviderAttributes = new HashMap<String, Object>(); + anonymousProviderAttributes.put(AuthenticationProvider.TYPE, AnonymousAuthenticationManager.PROVIDER_TYPE); + anonymousProviderAttributes.put(AuthenticationProvider.NAME, ANONYMOUS_AUTHENTICATION_PROVIDER); + config.addObjectConfiguration(AuthenticationProvider.class, anonymousProviderAttributes); + + // set password authentication provider on http port for the tests + config.setObjectAttribute(Port.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, Port.AUTHENTICATION_PROVIDER, + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + config.setObjectAttribute(Plugin.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED, true); + getBrokerConfiguration().addJmxManagementConfiguration(); + } + + @Override + public void tearDown() throws Exception + { + if (_jmxUtils != null) + { + _jmxUtils.close(); + } + super.tearDown(); + } + + public void testVirtualHostMBeanIsRegisteredOnVirtualHostCreation() throws Exception + { + String nodeName = "ntmp"; + String hostName = "htmp"; + + Map<String, Object> nodeData = new HashMap<>(); + nodeData.put(VirtualHostNode.NAME, nodeName); + nodeData.put(VirtualHostNode.TYPE, MemoryVirtualHostNode.VIRTUAL_HOST_NODE_TYPE); + getRestTestHelper().submitRequest("virtualhostnode/" + nodeName, "PUT", nodeData, HttpServletResponse.SC_CREATED); + + Map<String, Object> virtualhostData = new HashMap<>(); + virtualhostData.put(VirtualHost.NAME, nodeName); + virtualhostData.put(VirtualHost.TYPE, ProvidedStoreVirtualHostImpl.VIRTUAL_HOST_TYPE); + getRestTestHelper().submitRequest("virtualhost/" + nodeName + "/" + hostName, + "PUT", + virtualhostData, + HttpServletResponse.SC_CREATED); + + + ManagedBroker managedBroker = _jmxUtils.getManagedBroker(hostName); + assertNotNull("Host mBean is not created", managedBroker); + } + + public void testVirtualHostMBeanIsUnregisteredOnVirtualHostDeletion() throws Exception + { + boolean mBeanExists =_jmxUtils.doesManagedObjectExist(TEST_VIRTUAL_HOST_MBEAN_SEARCH_QUERY); + assertTrue("Host mBean is not registered", mBeanExists); + + getRestTestHelper().submitRequest("virtualhostnode/" + TEST2_VIRTUALHOST, "DELETE", HttpServletResponse.SC_OK); + + mBeanExists =_jmxUtils.doesManagedObjectExist(TEST_VIRTUAL_HOST_MBEAN_SEARCH_QUERY); + assertFalse("Host mBean is not unregistered", mBeanExists); + } + + public void testVirtualHostMBeanIsUnregisteredOnVirtualHostNodeStop() throws Exception + { + boolean mBeanExists =_jmxUtils.doesManagedObjectExist(TEST_VIRTUAL_HOST_MBEAN_SEARCH_QUERY); + assertTrue("Host mBean is not registered", mBeanExists); + + ManagedBroker managedBroker = _jmxUtils.getManagedBroker(TEST2_VIRTUALHOST); + assertNotNull("Host mBean is not created", managedBroker); + + Map<String, Object> nodeData = new HashMap<String, Object>(); + nodeData.put(VirtualHostNode.NAME, TEST2_VIRTUALHOST); + nodeData.put(VirtualHostNode.DESIRED_STATE, State.STOPPED.name()); + + int status = getRestTestHelper().submitRequest("virtualhostnode/" + TEST2_VIRTUALHOST, "PUT", nodeData); + assertEquals("Unexpected code", 200, status); + + mBeanExists =_jmxUtils.doesManagedObjectExist(TEST_VIRTUAL_HOST_MBEAN_SEARCH_QUERY); + assertFalse("Host mBean is not unregistered", mBeanExists); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/ManagementActorLoggingTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/ManagementActorLoggingTest.java new file mode 100644 index 0000000000..4358b4b450 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/ManagementActorLoggingTest.java @@ -0,0 +1,482 @@ +/* + * + * 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.management.jmx; + +import org.apache.qpid.management.common.mbeans.ManagedBroker; +import org.apache.qpid.management.common.mbeans.ManagedConnection; +import org.apache.qpid.management.common.mbeans.ManagedExchange; +import org.apache.qpid.server.logging.AbstractTestLogging; +import org.apache.qpid.server.logging.subjects.AbstractTestLogSubject; +import org.apache.qpid.test.utils.JMXTestUtils; + +import javax.jms.Connection; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.management.JMException; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Test class to test if any change in the broker JMX code is affesting the management console + * There are some hardcoding of management feature names and parameter names to create a customized + * look in the console. + */ +public class ManagementActorLoggingTest extends AbstractTestLogging +{ + private JMXTestUtils _jmxUtils; + private boolean _closed = false; + + @Override + public void setUp() throws Exception + { + getBrokerConfiguration().addJmxManagementConfiguration(); + + _jmxUtils = new JMXTestUtils(this); + + super.setUp(); + _jmxUtils.open(); + } + + @Override + public void tearDown() throws Exception + { + if(!_closed) + { + _jmxUtils.close(); + } + super.tearDown(); + } + + /** + * Description: + * When a connected client has its connection closed via the Management Console this will be logged as a CON-1002 message. + * Input: + * + * 1. Running Broker + * 2. Connected Client + * 3. Connection is closed via Management Console + * Output: + * + * <date> CON-1002 : Close + * + * Validation Steps: + * 4. The CON ID is correct + * 5. This must be the last CON message for the Connection + * 6. It must be preceded by a CON-1001 for this Connection + * + * @throws Exception - {@see ManagedConnection.closeConnection and #getConnection} + * @throws java.io.IOException - if there is a problem reseting the log monitor + */ + public void testConnectionCloseViaManagement() throws IOException, Exception + { + //Create a connection to the broker + Connection connection = getConnection(); + + // Monitor the connection for an exception being thrown + // this should be a DisconnectionException but it is not this tests + // job to valiate that. Only use the exception as a synchronisation + // to check the log file for the Close message + final CountDownLatch exceptionReceived = new CountDownLatch(1); + connection.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException e) + { + //Failover being attempted. + exceptionReceived.countDown(); + } + }); + + //Remove the connection close from any 0-10 connections + _monitor.markDiscardPoint(); + + // Get a managedConnection + ManagedConnection mangedConnection = _jmxUtils.getManagedObject(ManagedConnection.class, "org.apache.qpid:type=VirtualHost.Connection,*"); + + //Close the connection + mangedConnection.closeConnection(); + + //Wait for the connection to close + assertTrue("Timed out waiting for conneciton to report close", + exceptionReceived.await(2, TimeUnit.SECONDS)); + + //Validate results + List<String> results = waitAndFindMatches("CON-1002"); + + assertEquals("Unexpected Connection Close count", 1, results.size()); + } + + /** + * Description: + * Exchange creation is possible from the Management Console. + * When an exchanged is created in this way then a EXH-1001 create message + * is expected to be logged. + * Input: + * + * 1. Running broker + * 2. Connected Management Console + * 3. Exchange Created via Management Console + * Output: + * + * EXH-1001 : Create : [Durable] Type:<value> Name:<value> + * + * Validation Steps: + * 4. The EXH ID is correct + * 5. The correct tags are present in the message based on the create options + * + * @throws java.io.IOException - if there is a problem reseting the log monitor + * @throws javax.management.JMException - {@see #createQueue and ManagedExchange.deleteQueue} + */ + public void testCreateExchangeDirectTransientViaManagementConsole() throws IOException, JMException + { + _monitor.markDiscardPoint(); + + _jmxUtils.createExchange("test", getName(), "direct", false); + + // Validate + + //1 - ID is correct + List<String> results = waitAndFindMatches("EXH-1001"); + + assertEquals("More than one exchange creation found", 1, results.size()); + + String log = getLogMessage(results, 0); + + // Validate correct exchange name + assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName())); + + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); + } + + public void testCreateExchangeTopicTransientViaManagementConsole() throws IOException, JMException + { + //Remove any previous exchange declares + _monitor.markDiscardPoint(); + + _jmxUtils.createExchange("test", getName(), "topic", false); + + // Validate + + //1 - ID is correct + List<String> results = waitAndFindMatches("EXH-1001"); + + assertEquals("More than one exchange creation found", 1, results.size()); + + String log = getLogMessage(results, 0); + + // Validate correct exchange name + assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName())); + + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); + + } + + public void testCreateExchangeFanoutTransientViaManagementConsole() throws IOException, JMException + { + //Remove any previous exchange declares + _monitor.markDiscardPoint(); + + _jmxUtils.createExchange("test", getName(), "fanout", false); + + // Validate + + //1 - ID is correct + List<String> results = waitAndFindMatches("EXH-1001"); + + assertEquals("More than one exchange creation found", 1, results.size()); + + String log = getLogMessage(results, 0); + + // Validate correct exchange name + assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName())); + + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); + + } + + public void testCreateExchangeHeadersTransientViaManagementConsole() throws IOException, JMException + { + //Remove any previous exchange declares + _monitor.markDiscardPoint(); + + _jmxUtils.createExchange("test", getName(), "headers", false); + + // Validate + + //1 - ID is correct + List<String> results = waitAndFindMatches("EXH-1001"); + + assertEquals("More than one exchange creation found", 1, results.size()); + + String log = getLogMessage(results, 0); + + // Validate correct exchange name + assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName())); + + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); + + } + + /** + * Description: + * Queue creation is possible from the Management Console. When a queue is created in this way then a QUE-1001 create message is expected to be logged. + * Input: + * + * 1. Running broker + * 2. Connected Management Console + * 3. Queue Created via Management Console + * Output: + * + * <date> QUE-1001 : Create : Transient Owner:<name> + * + * Validation Steps: + * 4. The QUE ID is correct + * 5. The correct tags are present in the message based on the create options + * + * @throws java.io.IOException - if there is a problem reseting the log monitor + * @throws javax.management.JMException - {@see #createQueue and ManagedExchange.deleteQueue} + */ + public void testCreateQueueTransientViaManagementConsole() throws IOException, JMException + { + //Remove any previous queue declares + _monitor.markDiscardPoint(); + + _jmxUtils.createQueue("test", getName(), null, false); + + // Validate + + List<String> results = waitAndFindMatches("QUE-1001"); + + assertEquals("More than one queue creation found", 1, results.size()); + + String log = getLogMessage(results, 0); + + // Validate correct queue name + String subject = fromSubject(log); + assertEquals("Incorrect queue name created", getName(), AbstractTestLogSubject.getSlice("qu", subject)); + + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); + } + + /** + * Description: + * The ManagementConsole can be used to delete a queue. When this is done a QUE-1002 Deleted message must be logged. + * Input: + * + * 1. Running Broker + * 2. Queue created on the broker with no subscribers + * 3. Management Console connected + * 4. Queue is deleted via Management Console + * Output: + * + * <date> QUE-1002 : Deleted + * + * Validation Steps: + * 5. The QUE ID is correct + * + * @throws java.io.IOException - if there is a problem reseting the log monitor + * @throws javax.management.JMException - {@see #createQueue and ManagedExchange.deleteQueue} + */ + public void testQueueDeleteViaManagementConsole() throws IOException, JMException + { + //Remove any previous queue declares + _monitor.markDiscardPoint(); + + _jmxUtils.createQueue("test", getName(), null, false); + + ManagedBroker managedBroker = _jmxUtils.getManagedBroker("test"); + + managedBroker.deleteQueue(getName()); + + List<String> results = waitAndFindMatches("QUE-1002"); + + assertEquals("More than one queue deletion found", 1, results.size()); + + String log = getLog(results.get(0)); + + // Validate correct binding + String subject = fromSubject(log); + assertEquals("Incorrect queue named in delete", getName(), AbstractTestLogSubject.getSlice("qu", subject)); + + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); + + } + + /** + * Description: + * The binding of a Queue and an Exchange is done via a Binding. When this Binding is created via the Management Console a BND-1001 Create message will be logged. + * Input: + * + * 1. Running Broker + * 2. Connected Management Console + * 3. Use Management Console to perform binding + * Output: + * + * <date> BND-1001 : Create + * + * Validation Steps: + * 4. The BND ID is correct + * 5. This will be the first message for the given binding + * + * @throws java.io.IOException - if there is a problem reseting the log monitor + * @throws javax.management.JMException - {@see #createQueue and ManagedExchange.createNewBinding} + */ + public void testBindingCreateOnDirectViaManagementConsole() throws IOException, JMException + { + //Remove any previous queue declares + _monitor.markDiscardPoint(); + + _jmxUtils.createQueue("test", getName(), null, false); + + ManagedExchange managedExchange = _jmxUtils.getManagedExchange("amq.direct"); + + managedExchange.createNewBinding(getName(), getName()); + + List<String> results = waitAndFindMatches("BND-1001"); + + assertEquals("Unexpected number of bindings logged", 1, results.size()); + + String log = getLogMessage(results, 0); + + // Validate correct binding + String subject = fromSubject(log); + assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject)); + assertEquals("Incorrect routing key in create", getName(), AbstractTestLogSubject.getSlice("rk", subject)); + + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); + } + + public void testBindingCreateOnTopicViaManagementConsole() throws IOException, JMException + { + //Remove any previous queue declares + _monitor.markDiscardPoint(); + + _jmxUtils.createQueue("test", getName(), null, false); + + ManagedExchange managedExchange = _jmxUtils.getManagedExchange("amq.topic"); + + managedExchange.createNewBinding(getName(), getName()); + + List<String> results = waitAndFindMatches("BND-1001"); + + assertEquals("Unexpected number of bindings logged", 1, results.size()); + + String log = getLogMessage(results, 0); + + // Validate correct binding + String subject = fromSubject(log); + assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject)); + assertEquals("Incorrect routing key in create", getName(), AbstractTestLogSubject.getSlice("rk", subject)); + + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); + } + + public void testBindingCreateOnFanoutViaManagementConsole() throws IOException, JMException + { + //Remove any previous queue declares + _monitor.markDiscardPoint(); + + _jmxUtils.createQueue("test", getName(), null, false); + + ManagedExchange managedExchange = _jmxUtils.getManagedExchange("amq.fanout"); + + managedExchange.createNewBinding(getName(), getName()); + + List<String> results = waitAndFindMatches("BND-1001"); + + assertEquals("Unexpected number of bindings logged", 1, results.size()); + + String log = getLogMessage(results, 0); + + // Validate correct binding + String subject = fromSubject(log); + assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject)); + assertEquals("Incorrect routing key in create", getName(), AbstractTestLogSubject.getSlice("rk", subject)); + + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); + } + + /** + * Description: + * Bindings can be deleted so that a queue can be rebound with a different set of values. This can be performed via the Management Console + * Input: + * + * 1. Running Broker + * 2. Management Console connected + * 3. Management Console is used to perform unbind. + * Output: + * + * <date> BND-1002 : Deleted + * + * Validation Steps: + * 4. The BND ID is correct + * 5. There must have been a BND-1001 Create message first. + * 6. This will be the last message for the given binding + * + * @throws java.io.IOException - if there is a problem reseting the log monitor or an issue with the JMX Connection + * @throws javax.management.JMException - {@see #createExchange and ManagedBroker.unregisterExchange} + */ + public void testUnRegisterExchangeViaManagementConsole() throws IOException, JMException + { + //Remove any previous queue declares + _monitor.markDiscardPoint(); + + _jmxUtils.createExchange("test", getName(), "direct", false); + + ManagedBroker managedBroker = _jmxUtils.getManagedBroker("test"); + + managedBroker.unregisterExchange(getName()); + + List<String> results = waitAndFindMatches("EXH-1002"); + + assertEquals("More than one exchange deletion found", 1, results.size()); + + String log = getLog(results.get(0)); + + // Validate correct binding + String subject = fromSubject(log); + assertEquals("Incorrect exchange named in delete", "direct/" + getName(), AbstractTestLogSubject.getSlice("ex", subject)); + + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); + } + +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java new file mode 100644 index 0000000000..cb6eae013e --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java @@ -0,0 +1,324 @@ +/* + * + * 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.management.jmx; + + +import java.util.Collections; +import java.util.List; + +import org.apache.qpid.server.logging.AbstractTestLogging; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.Transport; +import org.apache.qpid.test.utils.JMXTestUtils; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.util.LogMonitor; + +/** + * Management Console Test Suite + * + * The Management Console test suite validates that the follow log messages as specified in the Functional Specification. + * + * This suite of tests validate that the management console messages occur correctly and according to the following format: + * + * MNG-1001 : <type> Management Startup + * MNG-1002 : Starting : <service> : Listening on port <Port> + * MNG-1003 : Shutting down : <service> : port <Port> + * MNG-1004 : <type> Management Ready + * MNG-1005 : <type> Management Stopped + * MNG-1006 : Using SSL Keystore : <path> + * MNG-1007 : Open : User <username> + * MNG-1008 : Close : User <username> + */ +public class ManagementLoggingTest extends AbstractTestLogging +{ + private static final String MNG_PREFIX = "MNG-"; + + public void setUp() throws Exception + { + setLogMessagePrefix(); + + // We either do this here or have a null check in tearDown. + // As when this test is run against profiles other than java it will NPE + _monitor = new LogMonitor(_outputFile); + //We explicitly do not call super.setUp as starting up the broker is + //part of the test case. + + } + + /** + * Description: + * Using the startup configuration validate that the management startup + * message is logged correctly. + * Input: + * Standard configuration with management enabled + * Output: + * + * <date> MNG-1001 : Startup + * + * Constraints: + * This is the FIRST message logged by MNG + * Validation Steps: + * + * 1. The BRK ID is correct + * 2. This is the FIRST message logged by MNG + */ + public void testManagementStartupEnabled() throws Exception + { + // This test only works on java brokers + if (isJavaBroker()) + { + startBrokerAndCreateMonitor(true, false); + + // Ensure we have received the MNG log msg. + waitForMessage("MNG-1001"); + + List<String> results = findMatches(MNG_PREFIX); + // Validation + + assertTrue("MNGer message not logged", results.size() > 0); + + String log = getLogMessage(results, 0); + + //1 + validateMessageID("MNG-1001", log); + + //2 + //There will be 2 copies of the startup message (one via SystemOut, and one via Log4J) + results = findMatches("MNG-1001"); + assertEquals("Unexpected startup message count.", + 2, results.size()); + + //3 + assertEquals("Startup log message is not 'Startup'.", "JMX Management Startup", + getMessageString(log)); + } + } + + /** + * Description: + * Verify that when management is disabled in the configuration file the + * startup message is not logged. + * Input: + * Standard configuration with management disabled + * Output: + * NO MNG messages + * Validation Steps: + * + * 1. Validate that no MNG messages are produced. + */ + public void testManagementStartupDisabled() throws Exception + { + if (isJavaBroker()) + { + startBrokerAndCreateMonitor(false, false); + + List<String> results = findMatches(MNG_PREFIX); + // Validation + + assertEquals("MNGer messages logged", 0, results.size()); + } + } + + /** + * The two MNG-1002 messages are logged at the same time so lets test them + * at the same time. + * + * Description: + * Using the default configuration validate that the RMI Registry socket is + * correctly reported as being opened + * + * Input: + * The default configuration file + * Output: + * + * <date> MESSAGE MNG-1002 : Starting : RMI Registry : Listening on port 8999 + * + * Constraints: + * The RMI ConnectorServer and Registry log messages do not have a prescribed order + * Validation Steps: + * + * 1. The MNG ID is correct + * 2. The specified port is the correct '8999' + * + * Description: + * Using the default configuration validate that the RMI ConnectorServer + * socket is correctly reported as being opened + * + * Input: + * The default configuration file + * Output: + * + * <date> MESSAGE MNG-1002 : Starting : RMI ConnectorServer : Listening on port 9099 + * + * Constraints: + * The RMI ConnectorServer and Registry log messages do not have a prescribed order + * Validation Steps: + * + * 1. The MNG ID is correct + * 2. The specified port is the correct '9099' + */ + public void testManagementStartupRMIEntries() throws Exception + { + if (isJavaBroker()) + { + startBrokerAndCreateMonitor(true, false); + + List<String> results = waitAndFindMatches("MNG-1002"); + // Validation + + //There will be 4 startup messages (two via SystemOut, and two via Log4J) + assertEquals("Unexpected MNG-1002 message count", 4, results.size()); + + String log = getLogMessage(results, 0); + + //1 + validateMessageID("MNG-1002", log); + + //Check the RMI Registry port is as expected + int mPort = getManagementPort(getPort()); + assertTrue("RMI Registry port not as expected(" + mPort + ").:" + getMessageString(log), + getMessageString(log).endsWith(String.valueOf(mPort))); + + log = getLogMessage(results, 2); + + //1 + validateMessageID("MNG-1002", log); + + // We expect the RMI Registry port (the defined 'management port') to be + // 100 lower than the JMX RMIConnector Server Port (the actual JMX server) + int jmxPort = mPort + JMXPORT_CONNECTORSERVER_OFFSET; + assertTrue("JMX RMIConnectorServer port not as expected(" + jmxPort + ").:" + getMessageString(log), + getMessageString(log).endsWith(String.valueOf(jmxPort))); + } + } + + /** + * Description: + * Using the default configuration with SSL enabled for the management port the SSL Keystore path should be reported via MNG-1006 + * Input: + * Management SSL enabled default configuration. + * Output: + * + * <date> MESSAGE MNG-1006 : Using SSL Keystore : test_resources/ssl/keystore.jks + * + * Validation Steps: + * + * 1. The MNG ID is correct + * 2. The keystore path is as specified in the configuration + */ + public void testManagementStartupSSLKeystore() throws Exception + { + if (isJavaBroker()) + { + setSystemProperty("javax.net.debug", "ssl"); + startBrokerAndCreateMonitor(true, true); + + List<String> results = waitAndFindMatches("MNG-1006"); + + assertTrue("MNGer message not logged", results.size() > 0); + + String log = getLogMessage(results, 0); + + //1 + validateMessageID("MNG-1006", log); + + // Validate we only have two MNG-1002 (one via stdout, one via log4j) + results = findMatches("MNG-1006"); + assertEquals("Upexpected SSL Keystore message count", + 2, results.size()); + + // Validate the keystore path is as expected + assertTrue("SSL Keystore entry expected.:" + getMessageString(log), + getMessageString(log).endsWith("systestsKeyStore")); + } + } + + /** + * Description: Tests the management connection open/close are logged correctly. + * + * Output: + * + * <date> MESSAGE MNG-1007 : Open : User <username> + * <date> MESSAGE MNG-1008 : Close : User <username> + * + * Validation Steps: + * + * 1. The MNG ID is correct + * 2. The message and username are correct + */ + public void testManagementUserOpenClose() throws Exception + { + if (isJavaBroker()) + { + startBrokerAndCreateMonitor(true, false); + + final JMXTestUtils jmxUtils = new JMXTestUtils(this); + List<String> openResults = null; + List<String> closeResults = null; + try + { + jmxUtils.open(); + openResults = waitAndFindMatches("MNG-1007"); + } + finally + { + if (jmxUtils != null) + { + jmxUtils.close(); + closeResults = waitAndFindMatches("MNG-1008"); + } + } + + assertNotNull("Management Open results null", openResults.size()); + assertEquals("Management Open logged unexpected number of times", 1, openResults.size()); + + assertNotNull("Management Close results null", closeResults.size()); + assertEquals("Management Close logged unexpected number of times", 1, closeResults.size()); + + final String openMessage = getMessageString(getLogMessage(openResults, 0)); + assertTrue("Unexpected open message " + openMessage, openMessage.endsWith("Open : User admin")); + final String closeMessage = getMessageString(getLogMessage(closeResults, 0)); + assertTrue("Unexpected close message " + closeMessage, closeMessage.endsWith("Close : User admin")); + } + } + + private void startBrokerAndCreateMonitor(boolean managementEnabled, boolean useManagementSSL) throws Exception + { + TestBrokerConfiguration config = getBrokerConfiguration(); + + if (managementEnabled) + { + config.addJmxManagementConfiguration(); + } + + if(useManagementSSL) + { + // This test requires we have ssl, change the transport and add they keystore to the port config + config.setObjectAttribute(Port.class, TestBrokerConfiguration.ENTRY_NAME_JMX_PORT, Port.TRANSPORTS, Collections.singleton(Transport.SSL)); + config.setObjectAttribute(Port.class, TestBrokerConfiguration.ENTRY_NAME_JMX_PORT, Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); + } + + startBroker(); + + // Now we can create the monitor as _outputFile will now be defined + _monitor = new LogMonitor(_outputFile); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/QueueManagementTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/QueueManagementTest.java new file mode 100644 index 0000000000..d0f133aa73 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/QueueManagementTest.java @@ -0,0 +1,869 @@ +/* + * 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.management.jmx; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.management.Notification; +import javax.management.NotificationListener; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.TabularData; +import javax.naming.NamingException; + +import org.apache.commons.lang.time.FastDateFormat; +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.configuration.ClientProperties; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.management.common.mbeans.ManagedBroker; +import org.apache.qpid.management.common.mbeans.ManagedQueue; +import org.apache.qpid.server.queue.NotificationCheckTest; +import org.apache.qpid.server.queue.QueueArgumentsConverter; +import org.apache.qpid.server.queue.StandardQueueImpl; +import org.apache.qpid.test.client.destination.AddressBasedDestinationTest; +import org.apache.qpid.test.utils.JMXTestUtils; +import org.apache.qpid.test.utils.QpidBrokerTestCase; + +/** + * Tests the JMX API for the Managed Queue. + * + */ +public class QueueManagementTest extends QpidBrokerTestCase +{ + + private static final Logger LOGGER = Logger.getLogger(QueueManagementTest.class); + + private static final String VIRTUAL_HOST = "test"; + private static final String TEST_QUEUE_DESCRIPTION = "my description"; + + private JMXTestUtils _jmxUtils; + private Connection _connection; + private Session _session; + + private String _sourceQueueName; + private String _destinationQueueName; + private Destination _sourceQueue; + private Destination _destinationQueue; + private ManagedQueue _managedSourceQueue; + private ManagedQueue _managedDestinationQueue; + + public void setUp() throws Exception + { + getBrokerConfiguration().addJmxManagementConfiguration(); + + _jmxUtils = new JMXTestUtils(this); + + super.setUp(); + _sourceQueueName = getTestQueueName() + "_src"; + _destinationQueueName = getTestQueueName() + "_dest"; + + createConnectionAndSession(); + + _sourceQueue = _session.createQueue(_sourceQueueName); + _destinationQueue = _session.createQueue(_destinationQueueName); + createQueueOnBroker(_sourceQueue); + createQueueOnBroker(_destinationQueue); + + _jmxUtils.open(); + + createManagementInterfacesForQueues(); + } + + public void tearDown() throws Exception + { + if (_jmxUtils != null) + { + _jmxUtils.close(); + } + super.tearDown(); + } + + public void testQueueAttributes() throws Exception + { + Queue queue = _session.createQueue(getTestQueueName()); + createQueueOnBroker(queue); + + final String queueName = queue.getQueueName(); + + final ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName); + assertEquals("Unexpected name", queueName, managedQueue.getName()); + assertEquals("Unexpected queue type", "standard", managedQueue.getQueueType()); + } + + public void testExclusiveQueueHasJmsClientIdAsOwner() throws Exception + { + final String subName = "testOwner"; + _session.createDurableSubscriber(getTestTopic(), subName); + + final String queueName = _connection.getClientID() + ":" + subName; + + final ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName); + assertNotNull(_connection.getClientID()); + assertEquals("Unexpected owner", _connection.getClientID(), managedQueue.getOwner()); + } + + public void testNonExclusiveQueueHasNoOwner() throws Exception + { + Queue nonExclusiveQueue = _session.createQueue(getTestQueueName()); + createQueueOnBroker(nonExclusiveQueue); + + final String queueName = nonExclusiveQueue.getQueueName(); + + final ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName); + assertNull("Unexpected owner", managedQueue.getOwner()); + } + + public void testSetNewQueueDescriptionOnExistingQueue() throws Exception + { + Queue queue = _session.createQueue(getTestQueueName()); + createQueueOnBroker(queue); + + final String queueName = queue.getQueueName(); + + final ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName); + assertNull("Unexpected description", managedQueue.getDescription()); + + managedQueue.setDescription(TEST_QUEUE_DESCRIPTION); + assertEquals(TEST_QUEUE_DESCRIPTION, managedQueue.getDescription()); + } + + public void testNewQueueWithDescription() throws Exception + { + String queueName = getTestQueueName(); + Map<String, Object> arguments = Collections.singletonMap(QueueArgumentsConverter.X_QPID_DESCRIPTION, (Object)TEST_QUEUE_DESCRIPTION); + ((AMQSession<?, ?>)_session).createQueue(AMQShortString.valueOf(queueName), false, true, false, arguments); + + final ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName); + assertEquals(TEST_QUEUE_DESCRIPTION, managedQueue.getDescription()); + } + + /** + * Requires persistent store. + */ + public void testQueueDescriptionSurvivesRestart() throws Exception + { + String queueName = getTestQueueName(); + Map<String, Object> arguments = Collections.singletonMap(QueueArgumentsConverter.X_QPID_DESCRIPTION, (Object)TEST_QUEUE_DESCRIPTION); + + ((AMQSession<?, ?>)_session).createQueue(AMQShortString.valueOf(queueName), false, true, false, arguments); + + ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName); + assertEquals(TEST_QUEUE_DESCRIPTION, managedQueue.getDescription()); + + restartBroker(); + + managedQueue = _jmxUtils.getManagedQueue(queueName); + assertEquals(TEST_QUEUE_DESCRIPTION, managedQueue.getDescription()); + } + + /** + * Tests queue creation with {@link QueueArgumentsConverter#X_QPID_MAXIMUM_DELIVERY_COUNT} argument. Also tests + * that the attribute is exposed correctly through {@link ManagedQueue#getMaximumDeliveryCount()}. + */ + public void testCreateQueueWithMaximumDeliveryCountSet() throws Exception + { + final String queueName = getName(); + final ManagedBroker managedBroker = _jmxUtils.getManagedBroker(VIRTUAL_HOST); + + final Integer deliveryCount = 1; + final Map<String, Object> arguments = Collections.singletonMap(QueueArgumentsConverter.X_QPID_MAXIMUM_DELIVERY_COUNT, (Object)deliveryCount); + managedBroker.createNewQueue(queueName, null, true, arguments); + + // Ensure the queue exists + assertNotNull("Queue object name expected to exist", _jmxUtils.getQueueObjectName(VIRTUAL_HOST, queueName)); + assertNotNull("Manager queue expected to be available", _jmxUtils.getManagedQueue(queueName)); + + final ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName); + assertEquals("Unexpected maximum delivery count", deliveryCount, managedQueue.getMaximumDeliveryCount()); + } + + public void testCreateQueueWithAlertingThresholdsSet() throws Exception + { + final String queueName = getName(); + final ManagedBroker managedBroker = _jmxUtils.getManagedBroker(VIRTUAL_HOST); + + final Long maximumMessageCount = 100l; + final Long maximumMessageSize = 200l; + final Long maximumQueueDepth = 300l; + final Long maximumMessageAge = 400l; + final Map<String, Object> arguments = new HashMap<String, Object>(); + arguments.put(QueueArgumentsConverter.X_QPID_MAXIMUM_MESSAGE_COUNT, maximumMessageCount); + arguments.put(QueueArgumentsConverter.X_QPID_MAXIMUM_MESSAGE_SIZE, maximumMessageSize); + arguments.put(QueueArgumentsConverter.X_QPID_MAXIMUM_QUEUE_DEPTH, maximumQueueDepth); + arguments.put(QueueArgumentsConverter.X_QPID_MAXIMUM_MESSAGE_AGE, maximumMessageAge); + + managedBroker.createNewQueue(queueName, null, true, arguments); + + // Ensure the queue exists + assertNotNull("Queue object name expected to exist", _jmxUtils.getQueueObjectName(VIRTUAL_HOST, queueName)); + assertNotNull("Manager queue expected to be available", _jmxUtils.getManagedQueue(queueName)); + + ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName); + assertEquals("Unexpected maximum message count", maximumMessageCount, managedQueue.getMaximumMessageCount()); + assertEquals("Unexpected maximum message size", maximumMessageSize, managedQueue.getMaximumMessageSize()); + assertEquals("Unexpected maximum queue depth", maximumQueueDepth, managedQueue.getMaximumQueueDepth()); + assertEquals("Unexpected maximum message age", maximumMessageAge, managedQueue.getMaximumMessageAge()); + } + + /** + * Requires 0-10 as relies on ADDR addresses. + * @see AddressBasedDestinationTest for the testing of message routing to the alternate exchange + */ + public void testGetSetAlternateExchange() throws Exception + { + String queueName = getTestQueueName(); + String altExchange = "amq.fanout"; + String addrWithAltExch = String.format("ADDR:%s;{create:always,node:{type:queue,x-declare:{alternate-exchange:'%s'}}}", queueName, altExchange); + Queue queue = _session.createQueue(addrWithAltExch); + + createQueueOnBroker(queue); + + final ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName); + assertEquals("Newly created queue does not have expected alternate exchange", altExchange, managedQueue.getAlternateExchange()); + + String newAltExch = "amq.topic"; + managedQueue.setAlternateExchange(newAltExch); + assertEquals("Unexpected alternate exchange after set", newAltExch, managedQueue.getAlternateExchange()); + } + + /** + * Requires 0-10 as relies on ADDR addresses. + */ + public void testRemoveAlternateExchange() throws Exception + { + String queueName = getTestQueueName(); + String altExchange = "amq.fanout"; + String addrWithAltExch = String.format("ADDR:%s;{create:always,node:{type:queue,x-declare:{alternate-exchange:'%s'}}}", queueName, altExchange); + Queue queue = _session.createQueue(addrWithAltExch); + + createQueueOnBroker(queue); + + final ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName); + assertEquals("Newly created queue does not have expected alternate exchange", altExchange, managedQueue.getAlternateExchange()); + + managedQueue.setAlternateExchange(""); + assertNull("Unexpected alternate exchange after set", managedQueue.getAlternateExchange()); + } + + /** + * Requires persistent store + * Requires 0-10 as relies on ADDR addresses. + */ + public void testAlternateExchangeSurvivesRestart() throws Exception + { + String nonMandatoryExchangeName = "exch" + getName(); + + final ManagedBroker managedBroker = _jmxUtils.getManagedBroker(VIRTUAL_HOST); + managedBroker.createNewExchange(nonMandatoryExchangeName, "fanout", true); + + String queueName1 = getTestQueueName() + "1"; + String altExchange1 = "amq.fanout"; + String addr1WithAltExch = String.format("ADDR:%s;{create:always,node:{durable: true,type:queue,x-declare:{alternate-exchange:'%s'}}}", queueName1, altExchange1); + Queue queue1 = _session.createQueue(addr1WithAltExch); + + String queueName2 = getTestQueueName() + "2"; + String addr2WithoutAltExch = String.format("ADDR:%s;{create:always,node:{durable: true,type:queue}}", queueName2); + Queue queue2 = _session.createQueue(addr2WithoutAltExch); + + createQueueOnBroker(queue1); + createQueueOnBroker(queue2); + + ManagedQueue managedQueue1 = _jmxUtils.getManagedQueue(queueName1); + assertEquals("Newly created queue1 does not have expected alternate exchange", altExchange1, managedQueue1.getAlternateExchange()); + + ManagedQueue managedQueue2 = _jmxUtils.getManagedQueue(queueName2); + assertNull("Newly created queue2 does not have expected alternate exchange", managedQueue2.getAlternateExchange()); + + String altExchange2 = nonMandatoryExchangeName; + managedQueue2.setAlternateExchange(altExchange2); + + restartBroker(); + + managedQueue1 = _jmxUtils.getManagedQueue(queueName1); + assertEquals("Queue1 does not have expected alternate exchange after restart", altExchange1, managedQueue1.getAlternateExchange()); + + managedQueue2 = _jmxUtils.getManagedQueue(queueName2); + assertEquals("Queue2 does not have expected updated alternate exchange after restart", altExchange2, managedQueue2.getAlternateExchange()); + } + + /** + * Tests the ability to receive queue alerts as JMX notifications. + * + * @see NotificationCheckTest + * @see SimpleAMQQueueTest#testNotificationFiredAsync() + * @see SimpleAMQQueueTest#testNotificationFiredOnEnqueue() + */ + public void testQueueNotification() throws Exception + { + final String queueName = getName(); + final long maximumMessageCount = 3; + + Queue queue = _session.createQueue(queueName); + createQueueOnBroker(queue); + + ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName); + managedQueue.setMaximumMessageCount(maximumMessageCount); + + RecordingNotificationListener listener = new RecordingNotificationListener(1); + + _jmxUtils.addNotificationListener(_jmxUtils.getQueueObjectName(VIRTUAL_HOST, queueName), listener, null, null); + + // Send two messages - this should *not* trigger the notification + sendMessage(_session, queue, 2); + + assertEquals("Premature notification received", 0, listener.getNumberOfNotificationsReceived()); + + // A further message should trigger the message count alert + sendMessage(_session, queue, 1); + + listener.awaitExpectedNotifications(5, TimeUnit.SECONDS); + + assertEquals("Unexpected number of JMX notifications received", 1, listener.getNumberOfNotificationsReceived()); + + Notification notification = listener.getLastNotification(); + assertEquals("Unexpected notification message", "MESSAGE_COUNT_ALERT 3: Maximum count on queue threshold (3) breached.", notification.getMessage()); + } + + /** + * Tests {@link ManagedQueue#viewMessages(long, long)} interface. + */ + public void testViewSingleMessage() throws Exception + { + final List<Message> sentMessages = sendMessage(_session, _sourceQueue, 1); + syncSession(_session); + final Message sentMessage = sentMessages.get(0); + + assertEquals("Unexpected queue depth", 1, _managedSourceQueue.getMessageCount().intValue()); + + // Check the contents of the message + final TabularData tab = _managedSourceQueue.viewMessages(1l, 1l); + assertEquals("Unexpected number of rows in table", 1, tab.size()); + final Iterator<CompositeData> rowItr = (Iterator<CompositeData>) tab.values().iterator(); + + final CompositeData row1 = rowItr.next(); + assertNotNull("Message should have AMQ message id", row1.get(ManagedQueue.MSG_AMQ_ID)); + assertEquals("Unexpected queue position", 1l, row1.get(ManagedQueue.MSG_QUEUE_POS)); + assertEquals("Unexpected redelivered flag", Boolean.FALSE, row1.get(ManagedQueue.MSG_REDELIVERED)); + + // Check the contents of header (encoded in a string array) + final String[] headerArray = (String[]) row1.get(ManagedQueue.MSG_HEADER); + assertNotNull("Expected message header array", headerArray); + final Map<String, String> headers = headerArrayToMap(headerArray); + + final String expectedJMSMessageID = isBroker010() ? sentMessage.getJMSMessageID().replace("ID:", "") : sentMessage.getJMSMessageID(); + final String expectedFormattedJMSTimestamp = FastDateFormat.getInstance(ManagedQueue.JMSTIMESTAMP_DATETIME_FORMAT).format(sentMessage.getJMSTimestamp()); + assertEquals("Unexpected JMSMessageID within header", expectedJMSMessageID, headers.get("JMSMessageID")); + assertEquals("Unexpected JMSPriority within header", String.valueOf(sentMessage.getJMSPriority()), headers.get("JMSPriority")); + assertEquals("Unexpected JMSTimestamp within header", expectedFormattedJMSTimestamp, headers.get("JMSTimestamp")); + } + + /** + * Tests {@link ManagedQueue#moveMessages(long, long, String)} interface. + */ + public void testMoveMessagesBetweenQueues() throws Exception + { + final int numberOfMessagesToSend = 10; + + sendMessage(_session, _sourceQueue, numberOfMessagesToSend); + syncSession(_session); + assertEquals("Unexpected queue depth after send", numberOfMessagesToSend, _managedSourceQueue.getMessageCount().intValue()); + + List<Long> amqMessagesIds = getAMQMessageIdsOn(_managedSourceQueue, 1, numberOfMessagesToSend); + + // Move first three messages to destination + long fromMessageId = amqMessagesIds.get(0); + long toMessageId = amqMessagesIds.get(2); + _managedSourceQueue.moveMessages(fromMessageId, toMessageId, _destinationQueueName); + + assertEquals("Unexpected queue depth on destination queue after first move", 3, _managedDestinationQueue.getMessageCount().intValue()); + assertEquals("Unexpected queue depth on source queue after first move", 7, _managedSourceQueue.getMessageCount().intValue()); + + // Now move a further two messages to destination + fromMessageId = amqMessagesIds.get(7); + toMessageId = amqMessagesIds.get(8); + _managedSourceQueue.moveMessages(fromMessageId, toMessageId, _destinationQueueName); + assertEquals("Unexpected queue depth on destination queue after second move", 5, _managedDestinationQueue.getMessageCount().intValue()); + assertEquals("Unexpected queue depth on source queue after second move", 5, _managedSourceQueue.getMessageCount().intValue()); + + assertMessageIndicesOn(_destinationQueue, 0, 1, 2, 7, 8); + } + + /** + * Tests {@link ManagedQueue#copyMessages(long, long, String)} interface. + */ + public void testCopyMessagesBetweenQueues() throws Exception + { + final int numberOfMessagesToSend = 10; + sendMessage(_session, _sourceQueue, numberOfMessagesToSend); + syncSession(_session); + assertEquals("Unexpected queue depth after send", numberOfMessagesToSend, _managedSourceQueue.getMessageCount().intValue()); + + List<Long> amqMessagesIds = getAMQMessageIdsOn(_managedSourceQueue, 1, numberOfMessagesToSend); + + // Copy first three messages to destination + long fromMessageId = amqMessagesIds.get(0); + long toMessageId = amqMessagesIds.get(2); + _managedSourceQueue.copyMessages(fromMessageId, toMessageId, _destinationQueueName); + + assertEquals("Unexpected queue depth on destination queue after first copy", 3, _managedDestinationQueue.getMessageCount().intValue()); + assertEquals("Unexpected queue depth on source queue after first copy", numberOfMessagesToSend, _managedSourceQueue.getMessageCount().intValue()); + + // Now copy a further two messages to destination + fromMessageId = amqMessagesIds.get(7); + toMessageId = amqMessagesIds.get(8); + _managedSourceQueue.copyMessages(fromMessageId, toMessageId, _destinationQueueName); + assertEquals("Unexpected queue depth on destination queue after second copy", 5, _managedDestinationQueue.getMessageCount().intValue()); + assertEquals("Unexpected queue depth on source queue after second copy", numberOfMessagesToSend, _managedSourceQueue.getMessageCount().intValue()); + + assertMessageIndicesOn(_destinationQueue, 0, 1, 2, 7, 8); + } + + + /** + * Tests {@link ManagedQueue#copyMessages(long, long, String)} interface. + */ + public void testCopyMessagesBetweenQueuesWithDuplicates() throws Exception + { + final int numberOfMessagesToSend = 10; + sendMessage(_session, _sourceQueue, numberOfMessagesToSend); + syncSession(_session); + assertEquals("Unexpected queue depth after send", + numberOfMessagesToSend, + _managedSourceQueue.getMessageCount().intValue()); + + List<Long> amqMessagesIds = getAMQMessageIdsOn(_managedSourceQueue, 1, numberOfMessagesToSend); + + // Copy first three messages to destination + long fromMessageId = amqMessagesIds.get(0); + long toMessageId = amqMessagesIds.get(2); + _managedSourceQueue.copyMessages(fromMessageId, toMessageId, _destinationQueueName); + + assertEquals("Unexpected queue depth on destination queue after first copy", + 3, + _managedDestinationQueue.getMessageCount().intValue()); + assertEquals("Unexpected queue depth on source queue after first copy", + numberOfMessagesToSend, + _managedSourceQueue.getMessageCount().intValue()); + + // Now copy a further two messages to destination + fromMessageId = amqMessagesIds.get(7); + toMessageId = amqMessagesIds.get(8); + _managedSourceQueue.copyMessages(fromMessageId, toMessageId, _destinationQueueName); + assertEquals("Unexpected queue depth on destination queue after second copy", + 5, + _managedDestinationQueue.getMessageCount().intValue()); + assertEquals("Unexpected queue depth on source queue after second copy", + numberOfMessagesToSend, + _managedSourceQueue.getMessageCount().intValue()); + + // Attempt to copy mixture of messages already on and some not already on the queue + + fromMessageId = amqMessagesIds.get(5); + toMessageId = amqMessagesIds.get(8); + _managedSourceQueue.copyMessages(fromMessageId, toMessageId, _destinationQueueName); + assertEquals("Unexpected queue depth on destination queue after second copy", + 7, + _managedDestinationQueue.getMessageCount().intValue()); + assertEquals("Unexpected queue depth on source queue after second copy", + numberOfMessagesToSend, + _managedSourceQueue.getMessageCount().intValue()); + + assertMessageIndicesOn(_destinationQueue, 0, 1, 2, 7, 8, 5, 6); + + + } + + public void testMoveMessagesBetweenQueuesWithActiveConsumerOnSourceQueue() throws Exception + { + setTestClientSystemProperty(ClientProperties.MAX_PREFETCH_PROP_NAME, new Integer(1).toString()); + Connection asyncConnection = getConnection(); + asyncConnection.start(); + + final int numberOfMessagesToSend = 50; + sendMessage(_session, _sourceQueue, numberOfMessagesToSend); + syncSession(_session); + assertEquals("Unexpected queue depth after send", numberOfMessagesToSend, _managedSourceQueue.getMessageCount().intValue()); + + List<Long> amqMessagesIds = getAMQMessageIdsOn(_managedSourceQueue, 1, numberOfMessagesToSend); + + long fromMessageId = amqMessagesIds.get(0); + long toMessageId = amqMessagesIds.get(numberOfMessagesToSend - 1); + + CountDownLatch consumerReadToHalfwayLatch = new CountDownLatch(numberOfMessagesToSend / 2); + AtomicInteger totalConsumed = new AtomicInteger(0); + startAsyncConsumerOn(_sourceQueue, asyncConnection, consumerReadToHalfwayLatch, totalConsumed); + + boolean halfwayPointReached = consumerReadToHalfwayLatch.await(5000, TimeUnit.MILLISECONDS); + assertTrue("Did not read half of messages within time allowed", halfwayPointReached); + + _managedSourceQueue.moveMessages(fromMessageId, toMessageId, _destinationQueueName); + + asyncConnection.stop(); + + // The exact number of messages moved will be non deterministic, as the number of messages processed + // by the consumer cannot be predicted. There is also the possibility that a message can remain + // on the source queue. This situation will arise if a message has been acquired by the consumer, but not + // yet delivered to the client application (i.e. MessageListener#onMessage()) when the Connection#stop() occurs. + // + // The number of messages moved + the number consumed + any messages remaining on source should + // *always* be equal to the number we originally sent. + + int numberOfMessagesReadByConsumer = totalConsumed.intValue(); + int numberOfMessagesOnDestinationQueue = _managedDestinationQueue.getMessageCount().intValue(); + int numberOfMessagesRemainingOnSourceQueue = _managedSourceQueue.getMessageCount().intValue(); + + LOGGER.debug("Async consumer read : " + numberOfMessagesReadByConsumer + + " Number of messages moved to destination : " + numberOfMessagesOnDestinationQueue + + " Number of messages remaining on source : " + numberOfMessagesRemainingOnSourceQueue); + assertEquals("Unexpected number of messages after move", numberOfMessagesToSend, numberOfMessagesReadByConsumer + numberOfMessagesOnDestinationQueue + numberOfMessagesRemainingOnSourceQueue); + } + + public void testMoveMessagesBetweenQueuesWithActiveConsumerOnDestinationQueue() throws Exception + { + setTestClientSystemProperty(ClientProperties.MAX_PREFETCH_PROP_NAME, new Integer(1).toString()); + Connection asyncConnection = getConnection(); + asyncConnection.start(); + + final int numberOfMessagesToSend = 50; + sendMessage(_session, _sourceQueue, numberOfMessagesToSend); + syncSession(_session); + assertEquals("Unexpected queue depth after send", numberOfMessagesToSend, _managedSourceQueue.getMessageCount().intValue()); + + List<Long> amqMessagesIds = getAMQMessageIdsOn(_managedSourceQueue, 1, numberOfMessagesToSend); + long fromMessageId = amqMessagesIds.get(0); + long toMessageId = amqMessagesIds.get(numberOfMessagesToSend - 1); + + AtomicInteger totalConsumed = new AtomicInteger(0); + CountDownLatch allMessagesConsumedLatch = new CountDownLatch(numberOfMessagesToSend); + startAsyncConsumerOn(_destinationQueue, asyncConnection, allMessagesConsumedLatch, totalConsumed); + + _managedSourceQueue.moveMessages(fromMessageId, toMessageId, _destinationQueueName); + + allMessagesConsumedLatch.await(5000, TimeUnit.MILLISECONDS); + assertEquals("Did not consume all messages from destination queue", numberOfMessagesToSend, totalConsumed.intValue()); + } + + /** + * Tests {@link ManagedQueue#moveMessages(long, long, String)} interface. + */ + public void testMoveMessageBetweenQueuesWithBrokerRestart() throws Exception + { + final int numberOfMessagesToSend = 1; + + sendMessage(_session, _sourceQueue, numberOfMessagesToSend); + syncSession(_session); + assertEquals("Unexpected queue depth after send", numberOfMessagesToSend, _managedSourceQueue.getMessageCount().intValue()); + + restartBroker(); + + createManagementInterfacesForQueues(); + createConnectionAndSession(); + + List<Long> amqMessagesIds = getAMQMessageIdsOn(_managedSourceQueue, 1, numberOfMessagesToSend); + + // Move messages to destination + long messageId = amqMessagesIds.get(0); + _managedSourceQueue.moveMessages(messageId, messageId, _destinationQueueName); + + assertEquals("Unexpected queue depth on destination queue after move", 1, _managedDestinationQueue.getMessageCount().intValue()); + assertEquals("Unexpected queue depth on source queue after move", 0, _managedSourceQueue.getMessageCount().intValue()); + + assertMessageIndicesOn(_destinationQueue, 0); + } + + /** + * Tests {@link ManagedQueue#copyMessages(long, long, String)} interface. + */ + public void testCopyMessageBetweenQueuesWithBrokerRestart() throws Exception + { + final int numberOfMessagesToSend = 1; + + sendMessage(_session, _sourceQueue, numberOfMessagesToSend); + syncSession(_session); + assertEquals("Unexpected queue depth after send", numberOfMessagesToSend, _managedSourceQueue.getMessageCount().intValue()); + + restartBroker(); + + createManagementInterfacesForQueues(); + createConnectionAndSession(); + + List<Long> amqMessagesIds = getAMQMessageIdsOn(_managedSourceQueue, 1, numberOfMessagesToSend); + + // Move messages to destination + long messageId = amqMessagesIds.get(0); + _managedSourceQueue.copyMessages(messageId, messageId, _destinationQueueName); + + assertEquals("Unexpected queue depth on destination queue after copy", 1, _managedDestinationQueue.getMessageCount().intValue()); + assertEquals("Unexpected queue depth on source queue after copy", 1, _managedSourceQueue.getMessageCount().intValue()); + + assertMessageIndicesOn(_destinationQueue, 0); + } + + /** + * Tests {@link ManagedQueue#deleteMessages(long, long)} interface. + */ + public void testDeleteMessages() throws Exception + { + final int numberOfMessagesToSend = 15; + + sendMessage(_session, _sourceQueue, numberOfMessagesToSend); + syncSession(_session); + assertEquals("Unexpected queue depth after send", numberOfMessagesToSend, _managedSourceQueue.getMessageCount().intValue()); + List<Long> amqMessagesIds = getAMQMessageIdsOn(_managedSourceQueue, 1, numberOfMessagesToSend); + // Current expected queue state, in terms of message header indices: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14] + + // Delete the first message (Remember the amqMessagesIds list, and the message indices added as a property when sending, are both 0-based index) + long fromMessageId = amqMessagesIds.get(0); + long toMessageId = fromMessageId; + _managedSourceQueue.deleteMessages(fromMessageId, toMessageId); + assertEquals("Unexpected message count after first deletion", numberOfMessagesToSend - 1, _managedSourceQueue.getMessageCount().intValue()); + // Current expected queue state, in terms of message header indices: [X,1,2,3,4,5,6,7,8,9,10,11,12,13,14] + + // Delete the 9th-10th messages, in the middle of the queue + fromMessageId = amqMessagesIds.get(8); + toMessageId = amqMessagesIds.get(9); + _managedSourceQueue.deleteMessages(fromMessageId, toMessageId); + assertEquals("Unexpected message count after third deletion", numberOfMessagesToSend - 3, _managedSourceQueue.getMessageCount().intValue()); + // Current expected queue state, in terms of message header indices: [X,1,2,3,4,5,6,7,X,X,10,11,12,13,14] + + // Delete the 11th and 12th messages, but still include the IDs for the 9th and 10th messages in the + // range to ensure their IDs are 'skipped' until the matching messages are found + fromMessageId = amqMessagesIds.get(8); + toMessageId = amqMessagesIds.get(11); + _managedSourceQueue.deleteMessages(fromMessageId, toMessageId); + assertEquals("Unexpected message count after fourth deletion", numberOfMessagesToSend - 5, _managedSourceQueue.getMessageCount().intValue()); + // Current expected queue state, in terms of message header indices: [X,1,2,3,4,5,6,7,X,X,X,X,12,13,14] + + // Delete the 8th message and the 13th message, including the IDs for the 9th-12th messages in the + // range to ensure their IDs are 'skipped' and the other matching message is found + fromMessageId = amqMessagesIds.get(7); + toMessageId = amqMessagesIds.get(12); + _managedSourceQueue.deleteMessages(fromMessageId, toMessageId); + assertEquals("Unexpected message count after fourth deletion", numberOfMessagesToSend - 7, _managedSourceQueue.getMessageCount().intValue()); + // Current expected queue state, in terms of message header indices: [X,1,2,3,4,5,6,X,X,X,X,X,X,13,14] + + // Delete the last message message + fromMessageId = amqMessagesIds.get(numberOfMessagesToSend -1); + toMessageId = fromMessageId; + _managedSourceQueue.deleteMessages(fromMessageId, toMessageId); + assertEquals("Unexpected message count after second deletion", numberOfMessagesToSend - 8, _managedSourceQueue.getMessageCount().intValue()); + // Current expected queue state, in terms of message header indices: [X,1,2,3,4,5,6,X,X,X,X,X,X,13,X] + + // Verify the message indices with a consumer + assertMessageIndicesOn(_sourceQueue, 1,2,3,4,5,6,13); + } + + public void testGetMessageGroupKey() throws Exception + { + final String queueName = getName(); + final ManagedBroker managedBroker = _jmxUtils.getManagedBroker(VIRTUAL_HOST); + + final Object messageGroupKey = "test"; + final Map<String, Object> arguments = Collections.singletonMap(QueueArgumentsConverter.QPID_GROUP_HEADER_KEY, messageGroupKey); + managedBroker.createNewQueue(queueName, null, true, arguments); + + final ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName); + + assertNotNull("Manager queue expected to be available", managedQueue); + assertEquals("Unexpected message group key", messageGroupKey, managedQueue.getMessageGroupKey()); + assertEquals("Unexpected message group sharing", false, managedQueue.isMessageGroupSharedGroups()); + } + + public void testIsMessageGroupSharedGroups() throws Exception + { + final String queueName = getName(); + final ManagedBroker managedBroker = _jmxUtils.getManagedBroker(VIRTUAL_HOST); + + final Object messageGroupKey = "test"; + final Map<String, Object> arguments = new HashMap<String, Object>(2); + arguments.put(QueueArgumentsConverter.QPID_GROUP_HEADER_KEY, messageGroupKey); + arguments.put(QueueArgumentsConverter.QPID_SHARED_MSG_GROUP, StandardQueueImpl.SHARED_MSG_GROUP_ARG_VALUE); + managedBroker.createNewQueue(queueName, null, true, arguments); + + final ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName); + + assertNotNull("Manager queue expected to be available", managedQueue); + assertEquals("Unexpected message group key", messageGroupKey, managedQueue.getMessageGroupKey()); + assertEquals("Unexpected message group sharing", true, managedQueue.isMessageGroupSharedGroups()); + } + + @Override + public Message createNextMessage(Session session, int messageNumber) throws JMSException + { + Message message = session.createTextMessage(getContentForMessageNumber(messageNumber)); + message.setIntProperty(INDEX, messageNumber); + return message; + } + + private void startAsyncConsumerOn(Destination queue, Connection asyncConnection, + final CountDownLatch requiredNumberOfMessagesRead, final AtomicInteger totalConsumed) throws Exception + { + Session session = asyncConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageConsumer consumer = session.createConsumer(queue); + consumer.setMessageListener(new MessageListener() + { + + @Override + public void onMessage(Message arg0) + { + totalConsumed.incrementAndGet(); + requiredNumberOfMessagesRead.countDown(); + } + }); + } + + private void assertMessageIndicesOn(Destination queue, int... expectedIndices) throws Exception + { + MessageConsumer consumer = _session.createConsumer(queue); + + for (int i : expectedIndices) + { + TextMessage message = (TextMessage)consumer.receive(1000); + assertNotNull("Expected message with index " + i, message); + assertEquals("Expected message with index " + i, i, message.getIntProperty(INDEX)); + assertEquals("Expected message content", getContentForMessageNumber(i), message.getText()); + } + + assertNull("Unexpected message encountered", consumer.receive(1000)); + } + + private List<Long> getAMQMessageIdsOn(ManagedQueue managedQueue, long startIndex, long endIndex) throws Exception + { + final SortedSet<Long> messageIds = new TreeSet<Long>(); + + final TabularData tab = managedQueue.viewMessages(startIndex, endIndex); + final Iterator<CompositeData> rowItr = (Iterator<CompositeData>) tab.values().iterator(); + while(rowItr.hasNext()) + { + final CompositeData row = rowItr.next(); + long amqMessageId = (Long)row.get(ManagedQueue.MSG_AMQ_ID); + messageIds.add(amqMessageId); + } + + return new ArrayList<Long>(messageIds); + } + + /** + * + * Utility method to convert array of Strings in the form x = y into a + * map with key/value x => y. + * + */ + private Map<String,String> headerArrayToMap(final String[] headerArray) + { + final Map<String, String> headerMap = new HashMap<String, String>(); + final List<String> headerList = Arrays.asList(headerArray); + for (Iterator<String> iterator = headerList.iterator(); iterator.hasNext();) + { + final String nameValuePair = iterator.next(); + final String[] nameValue = nameValuePair.split(" *= *", 2); + headerMap.put(nameValue[0], nameValue[1]); + } + return headerMap; + } + + private void createQueueOnBroker(Destination destination) throws JMSException + { + _session.createConsumer(destination).close(); // Create a consumer only to cause queue creation + } + + private void syncSession(Session session) throws Exception + { + ((AMQSession<?,?>)session).sync(); + } + + private void createConnectionAndSession() throws JMSException, + NamingException + { + _connection = getConnection(); + _connection.start(); + _session = _connection.createSession(true, Session.SESSION_TRANSACTED); + } + + private void createManagementInterfacesForQueues() + { + _managedSourceQueue = _jmxUtils.getManagedQueue(_sourceQueueName); + _managedDestinationQueue = _jmxUtils.getManagedQueue(_destinationQueueName); + } + + private String getContentForMessageNumber(int msgCount) + { + return "Message count " + msgCount; + } + + private final class RecordingNotificationListener implements NotificationListener + { + private final CountDownLatch _notificationReceivedLatch; + private final AtomicInteger _numberOfNotifications; + private final AtomicReference<Notification> _lastNotification; + + private RecordingNotificationListener(int expectedNumberOfNotifications) + { + _notificationReceivedLatch = new CountDownLatch(expectedNumberOfNotifications); + _numberOfNotifications = new AtomicInteger(0); + _lastNotification = new AtomicReference<Notification>(); + } + + @Override + public void handleNotification(Notification notification, Object handback) + { + _lastNotification.set(notification); + _numberOfNotifications.incrementAndGet(); + _notificationReceivedLatch.countDown(); + } + + public int getNumberOfNotificationsReceived() + { + return _numberOfNotifications.get(); + } + + public Notification getLastNotification() + { + return _lastNotification.get(); + } + + public void awaitExpectedNotifications(long timeout, TimeUnit timeunit) throws InterruptedException + { + _notificationReceivedLatch.await(timeout, timeunit); + } + } + +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/StatisticsTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/StatisticsTest.java new file mode 100644 index 0000000000..4ea071f3ac --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/StatisticsTest.java @@ -0,0 +1,211 @@ +/* + * 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.management.jmx; + +import java.util.List; + +import javax.jms.Connection; +import javax.jms.MessageConsumer; +import javax.jms.Queue; +import javax.jms.Session; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.management.common.mbeans.ManagedBroker; +import org.apache.qpid.management.common.mbeans.ManagedConnection; +import org.apache.qpid.management.common.mbeans.ServerInformation; +import org.apache.qpid.test.utils.JMXTestUtils; +import org.apache.qpid.test.utils.QpidBrokerTestCase; + +public class StatisticsTest extends QpidBrokerTestCase +{ + private static final String TEST_VIRTUALHOST1 = "test1"; + private static final String TEST_VIRTUALHOST2 = "test2"; + + private static final String TEST_USER = "admin"; + private static final String TEST_PASSWORD = "admin"; + private static final int MESSAGE_COUNT_TEST = 5; + private static final int MESSAGE_COUNT_DEV = 9; + + private JMXTestUtils _jmxUtils; + private Connection _vhost1Connection, _vhost2Connection; + private Session _vhost1Session, _vhost2Session; + private Queue _vhost1Queue, _vhost2Queue; + protected String _brokerUrl; + + @Override + public void setUp() throws Exception + { + createTestVirtualHostNode(0, TEST_VIRTUALHOST1); + createTestVirtualHostNode(0, TEST_VIRTUALHOST2); + + getBrokerConfiguration().addJmxManagementConfiguration(); + + _jmxUtils = new JMXTestUtils(this, TEST_USER, TEST_PASSWORD); + + super.setUp(); + + _brokerUrl = getBroker().toString(); + _vhost1Connection = new AMQConnection(_brokerUrl, TEST_USER, TEST_PASSWORD, "clientid", TEST_VIRTUALHOST1); + _vhost2Connection = new AMQConnection(_brokerUrl, TEST_USER, TEST_PASSWORD, "clientid", TEST_VIRTUALHOST2); + _vhost1Connection.start(); + _vhost2Connection.start(); + + _vhost1Session = _vhost1Connection.createSession(true, Session.SESSION_TRANSACTED); + _vhost2Session = _vhost2Connection.createSession(true, Session.SESSION_TRANSACTED); + + _vhost1Queue = _vhost2Session.createQueue(getTestQueueName()); + _vhost2Queue = _vhost1Session.createQueue(getTestQueueName()); + + //Create queues by opening and closing consumers + final MessageConsumer vhost1Consumer = _vhost1Session.createConsumer(_vhost2Queue); + vhost1Consumer.close(); + final MessageConsumer vhost2Consumer = _vhost2Session.createConsumer(_vhost1Queue); + vhost2Consumer.close(); + + _jmxUtils.open(); + } + + @Override + public void tearDown() throws Exception + { + _jmxUtils.close(); + + super.tearDown(); + } + + public void testInitialStatisticValues() throws Exception + { + //Check initial values + checkSingleConnectionOnVHostStatistics(TEST_VIRTUALHOST1, 0, 0, 0, 0); + checkVHostStatistics(TEST_VIRTUALHOST1, 0, 0, 0, 0); + checkSingleConnectionOnVHostStatistics(TEST_VIRTUALHOST2, 0, 0, 0, 0); + checkVHostStatistics(TEST_VIRTUALHOST2, 0, 0, 0, 0); + checkBrokerStatistics(0, 0, 0, 0); + } + + public void testSendOnSingleVHost() throws Exception + { + sendMessagesAndSync(_vhost1Session, _vhost2Queue, MESSAGE_COUNT_TEST); + + //Check values + checkSingleConnectionOnVHostStatistics(TEST_VIRTUALHOST1, MESSAGE_COUNT_TEST, 0, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE, 0); + checkVHostStatistics(TEST_VIRTUALHOST1, MESSAGE_COUNT_TEST, 0, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE, 0); + checkSingleConnectionOnVHostStatistics(TEST_VIRTUALHOST2, 0, 0, 0, 0); + checkVHostStatistics(TEST_VIRTUALHOST2, 0, 0, 0, 0); + checkBrokerStatistics(MESSAGE_COUNT_TEST, 0, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE, 0); + } + + public void testSendOnTwoVHosts() throws Exception + { + sendMessagesAndSync(_vhost1Session, _vhost2Queue, MESSAGE_COUNT_TEST); + sendMessagesAndSync(_vhost2Session, _vhost1Queue, MESSAGE_COUNT_DEV); + + //Check values + checkSingleConnectionOnVHostStatistics(TEST_VIRTUALHOST1, MESSAGE_COUNT_TEST, 0, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE, 0); + checkVHostStatistics(TEST_VIRTUALHOST1, MESSAGE_COUNT_TEST, 0, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE, 0); + checkSingleConnectionOnVHostStatistics(TEST_VIRTUALHOST2, MESSAGE_COUNT_DEV, 0, MESSAGE_COUNT_DEV * DEFAULT_MESSAGE_SIZE, 0); + checkVHostStatistics(TEST_VIRTUALHOST2, MESSAGE_COUNT_DEV, 0, MESSAGE_COUNT_DEV * DEFAULT_MESSAGE_SIZE, 0); + checkBrokerStatistics(MESSAGE_COUNT_TEST + MESSAGE_COUNT_DEV, 0, (MESSAGE_COUNT_TEST + MESSAGE_COUNT_DEV) * DEFAULT_MESSAGE_SIZE, 0); + } + + public void testSendAndConsumeOnSingleVHost() throws Exception + { + sendMessagesAndSync(_vhost1Session, _vhost2Queue, MESSAGE_COUNT_TEST); + consumeMessages(_vhost1Session, _vhost2Queue, MESSAGE_COUNT_TEST); + + //Check values + checkSingleConnectionOnVHostStatistics(TEST_VIRTUALHOST1, MESSAGE_COUNT_TEST, MESSAGE_COUNT_TEST, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE); + checkVHostStatistics(TEST_VIRTUALHOST1, MESSAGE_COUNT_TEST, MESSAGE_COUNT_TEST, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE); + checkSingleConnectionOnVHostStatistics(TEST_VIRTUALHOST2, 0, 0, 0, 0); + checkVHostStatistics(TEST_VIRTUALHOST2, 0, 0, 0, 0); + checkBrokerStatistics(MESSAGE_COUNT_TEST, MESSAGE_COUNT_TEST, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE); + } + + public void testSendAndConsumeOnTwoVHosts() throws Exception + { + sendMessagesAndSync(_vhost1Session, _vhost2Queue, MESSAGE_COUNT_TEST); + sendMessagesAndSync(_vhost2Session, _vhost1Queue, MESSAGE_COUNT_DEV); + consumeMessages(_vhost1Session, _vhost2Queue, MESSAGE_COUNT_TEST); + consumeMessages(_vhost2Session, _vhost1Queue, MESSAGE_COUNT_DEV); + + //Check values + checkSingleConnectionOnVHostStatistics(TEST_VIRTUALHOST1, MESSAGE_COUNT_TEST, MESSAGE_COUNT_TEST, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE); + checkVHostStatistics(TEST_VIRTUALHOST1, MESSAGE_COUNT_TEST, MESSAGE_COUNT_TEST, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE); + checkSingleConnectionOnVHostStatistics(TEST_VIRTUALHOST2, MESSAGE_COUNT_DEV, MESSAGE_COUNT_DEV, MESSAGE_COUNT_DEV * DEFAULT_MESSAGE_SIZE, MESSAGE_COUNT_DEV * DEFAULT_MESSAGE_SIZE); + checkVHostStatistics(TEST_VIRTUALHOST2, MESSAGE_COUNT_DEV, MESSAGE_COUNT_DEV, MESSAGE_COUNT_DEV * DEFAULT_MESSAGE_SIZE, MESSAGE_COUNT_DEV * DEFAULT_MESSAGE_SIZE); + checkBrokerStatistics(MESSAGE_COUNT_TEST + MESSAGE_COUNT_DEV, MESSAGE_COUNT_TEST + MESSAGE_COUNT_DEV, (MESSAGE_COUNT_TEST + MESSAGE_COUNT_DEV) * DEFAULT_MESSAGE_SIZE, (MESSAGE_COUNT_TEST + MESSAGE_COUNT_DEV) * DEFAULT_MESSAGE_SIZE); + } + + private void sendMessagesAndSync(Session session, Queue queue, int numberOfMessages) throws Exception + { + //Send messages via connection on and sync + sendMessage(session, queue, numberOfMessages); + ((AMQSession<?,?>)session).sync(); + } + + private void consumeMessages(Session session, Queue queue, int numberOfMessages) throws Exception + { + //consume the messages on the virtual host + final MessageConsumer consumer = session.createConsumer(queue); + for (int i = 0 ; i < numberOfMessages ; i++) + { + assertNotNull("an expected message was not received", consumer.receive(1500)); + } + session.commit(); + consumer.close(); + } + + private void checkSingleConnectionOnVHostStatistics(String vHostName, long messagesSent, long messagesReceived, long dataSent, long dataReceived) + { + List<ManagedConnection> managedConnections = _jmxUtils.getManagedConnections(vHostName); + assertEquals(1, managedConnections.size()); + + ManagedConnection managedConnection = managedConnections.get(0); + + assertEquals(messagesSent, managedConnection.getTotalMessagesReceived()); + assertEquals(messagesReceived, managedConnection.getTotalMessagesDelivered()); + + assertEquals(dataSent, managedConnection.getTotalDataReceived()); + assertEquals(dataReceived, managedConnection.getTotalDataDelivered()); + } + + private void checkVHostStatistics(String vHostName, long messagesSent, long messagesReceived, long dataSent, long dataReceived) + { + ManagedBroker vhost = _jmxUtils.getManagedBroker(vHostName); + + assertEquals(messagesSent, vhost.getTotalMessagesReceived()); + assertEquals(messagesReceived, vhost.getTotalMessagesDelivered()); + + assertEquals(dataSent, vhost.getTotalDataReceived()); + assertEquals(dataReceived, vhost.getTotalDataDelivered()); + } + + private void checkBrokerStatistics(long messagesSent, long messagesReceived, long dataSent, long dataReceived) + { + ServerInformation broker = _jmxUtils.getServerInformation(); + + assertEquals(messagesSent, broker.getTotalMessagesReceived()); + assertEquals(messagesReceived, broker.getTotalMessagesDelivered()); + + assertEquals(dataSent, broker.getTotalDataReceived()); + assertEquals(dataReceived, broker.getTotalDataDelivered()); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/UserManagementTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/UserManagementTest.java new file mode 100644 index 0000000000..25b09f04c3 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/UserManagementTest.java @@ -0,0 +1,261 @@ +/* + * 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.management.jmx; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.jms.Connection; +import javax.jms.JMSException; + +import org.apache.qpid.management.common.mbeans.UserManagement; +import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.security.auth.manager.PlainPasswordDatabaseAuthenticationManager; +import org.apache.qpid.test.utils.JMXTestUtils; +import org.apache.qpid.test.utils.QpidBrokerTestCase; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.tools.security.Passwd; + +/** + * System test for User Management. + * + */ +public class UserManagementTest extends QpidBrokerTestCase +{ + private static final String TEST_NEWPASSWORD = "newpassword"; + private static final String TEST_PASSWORD = "password"; + private JMXTestUtils _jmxUtils; + private String _testUserName; + private File _passwordFile; + private UserManagement _userManagement; + private Passwd _passwd; + + public void setUp() throws Exception + { + _passwd = createPasswordEncodingUtility(); + _passwordFile = createTemporaryPasswordFileWithJmxAdminUser(); + + Map<String, Object> newAttributes = new HashMap<String, Object>(); + newAttributes.put(AuthenticationProvider.TYPE, getAuthenticationManagerType()); + newAttributes.put("path", _passwordFile.getAbsolutePath()); + getBrokerConfiguration().setObjectAttributes(AuthenticationProvider.class,TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER, newAttributes); + getBrokerConfiguration().addJmxManagementConfiguration(); + + _jmxUtils = new JMXTestUtils(this); + + super.setUp(); + _jmxUtils.open(); + + _testUserName = getTestName() + System.currentTimeMillis(); + + _userManagement = _jmxUtils.getUserManagement(TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + } + + + public void tearDown() throws Exception + { + try + { + if (_jmxUtils != null) + { + _jmxUtils.close(); + } + } + finally + { + super.tearDown(); + } + } + + public void testCreateUser() throws Exception + { + final int initialNumberOfUsers = _userManagement.viewUsers().size(); + assertFileDoesNotContainsPasswordForUser(_testUserName); + + boolean success = _userManagement.createUser(_testUserName, TEST_PASSWORD); + assertTrue("Should have been able to create new user " + _testUserName, success); + assertEquals("Unexpected number of users after add", initialNumberOfUsers + 1, _userManagement.viewUsers().size()); + + assertFileContainsPasswordForUser(_testUserName); + } + + public void testJmsLoginForNewUser() throws Exception + { + assertJmsConnectionFails(_testUserName, TEST_PASSWORD); + testCreateUser(); + + assertJmsConnectionSucceeds(_testUserName, TEST_PASSWORD); + } + + public void testDeleteUser() throws Exception + { + final int initialNumberOfUsers = _userManagement.viewUsers().size(); + + testCreateUser(); + + boolean success = _userManagement.deleteUser(_testUserName); + assertTrue("Should have been able to delete new user " + _testUserName, success); + assertEquals("Unexpected number of users after delete", initialNumberOfUsers, _userManagement.viewUsers().size()); + assertFileDoesNotContainsPasswordForUser(_testUserName); + } + + public void testJmsLoginNotPossibleForDeletedUser() throws Exception + { + testDeleteUser(); + + assertJmsConnectionFails(_testUserName, TEST_PASSWORD); + } + + public void testSetPassword() throws Exception + { + testCreateUser(); + + _userManagement.setPassword(_testUserName, TEST_NEWPASSWORD); + + assertFileContainsPasswordForUser(_testUserName); + } + + public void testJmsLoginForPasswordChangedUser() throws Exception + { + testSetPassword(); + + assertJmsConnectionSucceeds(_testUserName, TEST_NEWPASSWORD); + assertJmsConnectionFails(_testUserName, TEST_PASSWORD); + } + + public void testReload() throws Exception + { + writePasswordFile(_passwordFile, JMXTestUtils.DEFAULT_USERID, JMXTestUtils.DEFAULT_PASSWORD, _testUserName, TEST_PASSWORD); + + assertJmsConnectionFails(_testUserName, TEST_PASSWORD); + + _userManagement.reloadData(); + + assertJmsConnectionSucceeds(_testUserName, TEST_PASSWORD); + } + + public void testGetAuthenticationProviderType() throws Exception + { + String actualType = _userManagement.getAuthenticationProviderType(); + assertEquals("unexpected authentication provider type", getAuthenticationManagerType(), actualType); + } + + protected Passwd createPasswordEncodingUtility() + { + return new Passwd() + { + @Override + public String getOutput(String username, String password) + { + return username + ":" + password; + } + }; + } + + protected String getAuthenticationManagerType() + { + return PlainPasswordDatabaseAuthenticationManager.PROVIDER_TYPE; + } + + private File createTemporaryPasswordFileWithJmxAdminUser() throws Exception + { + File passwordFile = File.createTempFile("passwd", "pwd"); + passwordFile.deleteOnExit(); + writePasswordFile(passwordFile, JMXTestUtils.DEFAULT_USERID, JMXTestUtils.DEFAULT_PASSWORD); + return passwordFile; + } + + private void writePasswordFile(File passwordFile, String... userNamePasswordPairs) throws Exception + { + FileWriter writer = null; + try + { + writer = new FileWriter(passwordFile); + for (int i = 0; i < userNamePasswordPairs.length; i=i+2) + { + String username = userNamePasswordPairs[i]; + String password = userNamePasswordPairs[i+1]; + writer.append(_passwd.getOutput(username, password) + "\n"); + } + } + finally + { + writer.close(); + } + } + + + private void assertFileContainsPasswordForUser(String username) throws IOException + { + assertTrue("Could not find password for user " + username + " within " + _passwordFile, passwordFileContainsUser(username)); + } + + private void assertFileDoesNotContainsPasswordForUser(String username) throws IOException + { + assertFalse("Could not find password for user " + username + " within " + _passwordFile, passwordFileContainsUser(username)); + } + + private boolean passwordFileContainsUser(String username) throws IOException + { + BufferedReader reader = null; + try + { + reader = new BufferedReader(new FileReader(_passwordFile)); + String line = reader.readLine(); + while(line != null) + { + if (line.startsWith(username)) + { + return true; + } + line = reader.readLine(); + } + + return false; + } + finally + { + reader.close(); + } + } + + private void assertJmsConnectionSucceeds(String username, String password) throws Exception + { + Connection connection = getConnection(username, password); + assertNotNull(connection); + } + + private void assertJmsConnectionFails(String username, String password) throws Exception + { + try + { + getConnection(username, password); + fail("Exception not thrown"); + } + catch (JMSException e) + { + // PASS + } + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/UserManagementWithBase64MD5PasswordsTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/UserManagementWithBase64MD5PasswordsTest.java new file mode 100644 index 0000000000..ff441169b3 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/UserManagementWithBase64MD5PasswordsTest.java @@ -0,0 +1,37 @@ +/* + * 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.management.jmx; + +import org.apache.qpid.server.security.auth.manager.Base64MD5PasswordDatabaseAuthenticationManager; +import org.apache.qpid.tools.security.Passwd; + +public class UserManagementWithBase64MD5PasswordsTest extends UserManagementTest +{ + @Override + protected Passwd createPasswordEncodingUtility() + { + return new Passwd(); + } + + @Override + protected String getAuthenticationManagerType() + { + return Base64MD5PasswordDatabaseAuthenticationManager.PROVIDER_TYPE; + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/AccessControlProviderRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/AccessControlProviderRestTest.java new file mode 100644 index 0000000000..4140c9c12c --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/AccessControlProviderRestTest.java @@ -0,0 +1,288 @@ +/* + * + * 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.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.apache.qpid.server.BrokerOptions; +import org.apache.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.server.model.AccessControlProvider; +import org.apache.qpid.server.model.Plugin; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.security.access.FileAccessControlProviderConstants; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.test.utils.TestFileUtils; + +public class AccessControlProviderRestTest extends QpidRestTestCase +{ + private static final String ALLOWED_USER = "allowed"; + private static final String DENIED_USER = "denied"; + private static final String OTHER_USER = "other"; + + private String _aclFileContent1 = + "ACL ALLOW-LOG " + ALLOWED_USER + " ACCESS MANAGEMENT\n" + + "ACL ALLOW-LOG " + ALLOWED_USER + " CONFIGURE BROKER\n" + + "ACL DENY-LOG ALL ALL"; + + private String _aclFileContent2 = + "ACL ALLOW-LOG " + ALLOWED_USER + " ACCESS MANAGEMENT\n" + + "ACL ALLOW-LOG " + OTHER_USER + " ACCESS MANAGEMENT\n" + + "ACL ALLOW-LOG " + ALLOWED_USER + " CONFIGURE BROKER\n" + + "ACL DENY-LOG ALL ALL"; + + @Override + protected void customizeConfiguration() throws IOException + { + super.customizeConfiguration(); + getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER, OTHER_USER); + + getBrokerConfiguration().setObjectAttribute(Plugin.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, + HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED, true); + } + + public void testCreateAccessControlProvider() throws Exception + { + String accessControlProviderName = getTestName(); + + //verify that the access control provider doesn't exist, and + //in doing so implicitly verify that the 'denied' user can + //actually currently connect because no ACL is in effect yet + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertAccessControlProviderExistence(accessControlProviderName, false); + + //create the access control provider using the 'allowed' user + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + int responseCode = createAccessControlProvider(accessControlProviderName, _aclFileContent1); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + //verify it exists with the 'allowed' user + assertAccessControlProviderExistence(accessControlProviderName, true); + + //verify the 'denied' user can no longer access the management interface + //due to the just-created ACL file now preventing it + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertCanAccessManagementInterface(accessControlProviderName, false); + } + + public void testRemoveAccessControlProvider() throws Exception + { + String accessControlProviderName = getTestName(); + + //verify that the access control provider doesn't exist, and + //in doing so implicitly verify that the 'denied' user can + //actually currently connect because no ACL is in effect yet + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertAccessControlProviderExistence(accessControlProviderName, false); + + //create the access control provider using the 'allowed' user + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + int responseCode = createAccessControlProvider(accessControlProviderName, _aclFileContent1); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + //verify it exists with the 'allowed' user + assertAccessControlProviderExistence(accessControlProviderName, true); + + //verify the 'denied' user can no longer access the management interface + //due to the just-created ACL file now preventing it + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertCanAccessManagementInterface(accessControlProviderName, false); + + //remove the access control provider using the 'allowed' user + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + responseCode = getRestTestHelper().submitRequest("accesscontrolprovider/" + accessControlProviderName, "DELETE"); + assertEquals("Access control provider deletion should be allowed", 200, responseCode); + assertAccessControlProviderExistence(accessControlProviderName, false); + + //verify it is gone again, using the 'denied' user to implicitly confirm it is + //now able to connect to the management interface again because the ACL was removed. + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertAccessControlProviderExistence(accessControlProviderName, false); + } + + public void testReplaceAccessControlProvider() throws Exception + { + String accessControlProviderName1 = getTestName() + "1"; + + //verify that the access control provider doesn't exist, and + //in doing so implicitly verify that the 'denied' user can + //actually currently connect because no ACL is in effect yet + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertAccessControlProviderExistence(accessControlProviderName1, false); + + //create the access control provider using the 'allowed' user + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + int responseCode = createAccessControlProvider(accessControlProviderName1, _aclFileContent1); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + //verify it exists with the 'allowed' user + assertAccessControlProviderExistence(accessControlProviderName1, true); + + //verify the 'denied' and 'other' user can no longer access the management + //interface due to the just-created ACL file now preventing them + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertCanAccessManagementInterface(accessControlProviderName1, false); + getRestTestHelper().setUsernameAndPassword(OTHER_USER, OTHER_USER); + assertCanAccessManagementInterface(accessControlProviderName1, false); + + //create the replacement access control provider using the 'allowed' user. + String accessControlProviderName2 = getTestName() + "2"; + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + responseCode = createAccessControlProvider(accessControlProviderName2, _aclFileContent2); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + //Verify that it took effect immediately, replacing the first access control provider + + //verify the 'denied' user still can't access the management interface, but the 'other' user now CAN. + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertCanAccessManagementInterface(accessControlProviderName2, false); + getRestTestHelper().setUsernameAndPassword(OTHER_USER, OTHER_USER); + assertCanAccessManagementInterface(accessControlProviderName2, true); + + //remove the original access control provider using the 'allowed' user + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + responseCode = getRestTestHelper().submitRequest("accesscontrolprovider/" + accessControlProviderName1, "DELETE"); + assertEquals("Access control provider deletion should be allowed", 200, responseCode); + assertAccessControlProviderExistence(accessControlProviderName1, false); + + //verify the 'denied' user still can't access the management interface, the 'other' user still can, thus + //confirming that the second access control provider is still in effect + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertCanAccessManagementInterface(accessControlProviderName2, false); + getRestTestHelper().setUsernameAndPassword(OTHER_USER, OTHER_USER); + assertCanAccessManagementInterface(accessControlProviderName2, true); + } + + + public void testAddAndRemoveSecondAccessControlProviderReinstatesOriginal() throws Exception + { + String accessControlProviderName1 = getTestName() + "1"; + + //verify that the access control provider doesn't exist, and + //in doing so implicitly verify that the 'denied' user can + //actually currently connect because no ACL is in effect yet + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertAccessControlProviderExistence(accessControlProviderName1, false); + + //create the access control provider using the 'allowed' user + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + int responseCode = createAccessControlProvider(accessControlProviderName1, _aclFileContent1); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + //verify it exists with the 'allowed' user + assertAccessControlProviderExistence(accessControlProviderName1, true); + + //verify the 'denied' and 'other' user can no longer access the management + //interface due to the just-created ACL file now preventing them + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertCanAccessManagementInterface(accessControlProviderName1, false); + getRestTestHelper().setUsernameAndPassword(OTHER_USER, OTHER_USER); + assertCanAccessManagementInterface(accessControlProviderName1, false); + + //create the replacement access control provider using the 'allowed' user. + String accessControlProviderName2 = getTestName() + "2"; + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + responseCode = createAccessControlProvider(accessControlProviderName2, _aclFileContent2); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + //Verify that it took effect immediately, replacing the first access control provider + + //verify the 'denied' user still can't access the management interface, but the 'other' user now CAN. + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertCanAccessManagementInterface(accessControlProviderName2, false); + getRestTestHelper().setUsernameAndPassword(OTHER_USER, OTHER_USER); + assertCanAccessManagementInterface(accessControlProviderName2, true); + + //remove the second access control provider using the 'allowed' user + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + responseCode = getRestTestHelper().submitRequest("accesscontrolprovider/" + accessControlProviderName2, "DELETE"); + assertEquals("Access control provider deletion should be allowed", 200, responseCode); + assertAccessControlProviderExistence(accessControlProviderName2, false); + + //verify the 'denied' user still can't access the management interface, the + //'other' now CANT again, the 'allowed' still can, thus confirming that the + //first access control provider is now in effect once again + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertCanAccessManagementInterface(accessControlProviderName2, false); + getRestTestHelper().setUsernameAndPassword(OTHER_USER, OTHER_USER); + assertCanAccessManagementInterface(accessControlProviderName2, false); + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + assertCanAccessManagementInterface(accessControlProviderName2, true); + } + + public void testRemovalOfAccessControlProviderInErrorStateUsingManagementMode() throws Exception + { + stopBroker(); + + File file = new File(TMP_FOLDER, getTestName()); + if (file.exists()) + { + file.delete(); + } + assertFalse("ACL file should not exist", file.exists()); + UUID id = getBrokerConfiguration().addAclFileConfiguration(file.getAbsolutePath()); + getBrokerConfiguration().setSaved(false); + startBroker(0, true); + + getRestTestHelper().setUsernameAndPassword(BrokerOptions.MANAGEMENT_MODE_USER_NAME, MANAGEMENT_MODE_PASSWORD); + + Map<String, Object> acl = getRestTestHelper().getJsonAsSingletonList("accesscontrolprovider/" + TestBrokerConfiguration.ENTRY_NAME_ACL_FILE); + assertEquals("Unexpected id", id.toString(), acl.get(AccessControlProvider.ID)); + assertEquals("Unexpected path", file.getAbsolutePath() , acl.get(FileAccessControlProviderConstants.PATH)); + assertEquals("Unexpected state", State.ERRORED.name() , acl.get(AccessControlProvider.STATE)); + + int status = getRestTestHelper().submitRequest("accesscontrolprovider/" + TestBrokerConfiguration.ENTRY_NAME_ACL_FILE, "DELETE"); + assertEquals("ACL was not deleted", 200, status); + + List<Map<String, Object>> acls = getRestTestHelper().getJsonAsList("accesscontrolprovider/" + TestBrokerConfiguration.ENTRY_NAME_ACL_FILE); + assertEquals("ACL exists", 0, acls.size()); + } + + private void assertCanAccessManagementInterface(String accessControlProviderName, boolean canAccess) throws Exception + { + int expected = canAccess ? 200 : 403; + int responseCode = getRestTestHelper().submitRequest("accesscontrolprovider/" + accessControlProviderName, "GET"); + assertEquals("Unexpected response code", expected, responseCode); + } + + private void assertAccessControlProviderExistence(String accessControlProviderName, boolean exists) throws Exception + { + String path = "accesscontrolprovider/" + accessControlProviderName; + List<Map<String, Object>> providers = getRestTestHelper().getJsonAsList(path); + assertEquals("Unexpected result", exists, !providers.isEmpty()); + } + + private int createAccessControlProvider(String accessControlProviderName, String content) throws Exception + { + File file = TestFileUtils.createTempFile(this, ".acl", content); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AccessControlProvider.NAME, accessControlProviderName); + attributes.put(AccessControlProvider.TYPE, FileAccessControlProviderConstants.ACL_FILE_PROVIDER_TYPE); + attributes.put(FileAccessControlProviderConstants.PATH, file.getAbsoluteFile()); + + return getRestTestHelper().submitRequest("accesscontrolprovider/" + accessControlProviderName, "PUT", attributes); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/AnonymousAccessRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/AnonymousAccessRestTest.java new file mode 100644 index 0000000000..3f944da8c7 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/AnonymousAccessRestTest.java @@ -0,0 +1,115 @@ +/* + * + * 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.util.HashMap; +import java.util.Map; + +import org.apache.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.Plugin; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.security.auth.manager.AnonymousAuthenticationManager; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class AnonymousAccessRestTest extends QpidRestTestCase +{ + @Override + public void startBroker() + { + // prevent broker from starting in setUp + } + + public void startBrokerNow() throws Exception + { + super.startBroker(); + } + + @Override + protected void customizeConfiguration() throws IOException + { + super.customizeConfiguration(); + TestBrokerConfiguration config = getBrokerConfiguration(); + + Map<String, Object> anonymousAuthProviderAttributes = new HashMap<String, Object>(); + anonymousAuthProviderAttributes.put(AuthenticationProvider.TYPE, AnonymousAuthenticationManager.PROVIDER_TYPE); + anonymousAuthProviderAttributes.put(AuthenticationProvider.NAME, TestBrokerConfiguration.ENTRY_NAME_ANONYMOUS_PROVIDER); + config.addObjectConfiguration(AuthenticationProvider.class, anonymousAuthProviderAttributes); + + // set anonymous authentication provider on http port for the tests + config.setObjectAttribute(Port.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, Port.AUTHENTICATION_PROVIDER, + TestBrokerConfiguration.ENTRY_NAME_ANONYMOUS_PROVIDER); + config.setObjectAttribute(Plugin.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED, false); + + // reset credentials + getRestTestHelper().setUsernameAndPassword(null, null); + } + + public void testGetWithAnonymousProvider() throws Exception + { + startBrokerNow(); + + Map<String, Object> brokerDetails = getRestTestHelper().getJsonAsSingletonList("broker"); + assertNotNull("Unexpected broker attributes", brokerDetails); + assertNotNull("Unexpected value of attribute " + Broker.ID, brokerDetails.get(Broker.ID)); + } + + public void testPutAnonymousProvider() throws Exception + { + startBrokerNow(); + + Map<String, Object> brokerAttributes = new HashMap<String, Object>(); + brokerAttributes.put(Broker.DEFAULT_VIRTUAL_HOST, TEST3_VIRTUALHOST); + + int response = getRestTestHelper().submitRequest("broker", "PUT", brokerAttributes); + assertEquals("Unexpected update response", 200, response); + + Map<String, Object> brokerDetails = getRestTestHelper().getJsonAsSingletonList("broker"); + assertNotNull("Unexpected broker attributes", brokerDetails); + assertNotNull("Unexpected value of attribute " + Broker.ID, brokerDetails.get(Broker.ID)); + assertEquals("Unexpected default virtual host", TEST3_VIRTUALHOST, brokerDetails.get(Broker.DEFAULT_VIRTUAL_HOST)); + } + + public void testGetWithPasswordAuthProvider() throws Exception + { + getBrokerConfiguration().setObjectAttribute(Port.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, Port.AUTHENTICATION_PROVIDER, + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + startBrokerNow(); + + int response = getRestTestHelper().submitRequest("broker", "GET"); + assertEquals("Anonymous access should be denied", 401, response); + } + + public void testPutWithPasswordAuthProvider() throws Exception + { + getBrokerConfiguration().setObjectAttribute(Port.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, Port.AUTHENTICATION_PROVIDER, + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + startBrokerNow(); + + Map<String, Object> brokerAttributes = new HashMap<String, Object>(); + brokerAttributes.put(Broker.DEFAULT_VIRTUAL_HOST, TEST3_VIRTUALHOST); + + int response = getRestTestHelper().submitRequest("broker", "PUT", brokerAttributes); + assertEquals("Anonymous access should be denied", 401, response); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/AuthenticationProviderRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/AuthenticationProviderRestTest.java new file mode 100644 index 0000000000..2467705903 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/AuthenticationProviderRestTest.java @@ -0,0 +1,337 @@ +/* + * + * 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.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.apache.qpid.server.BrokerOptions; +import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.model.BrokerModel; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.ExternalFileBasedAuthenticationManager; +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.User; +import org.apache.qpid.server.security.auth.manager.AnonymousAuthenticationManager; +import org.apache.qpid.server.security.auth.manager.PlainPasswordDatabaseAuthenticationManager; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class AuthenticationProviderRestTest extends QpidRestTestCase +{ + + public void testGet() throws Exception + { + List<Map<String, Object>> providerDetails = getRestTestHelper().getJsonAsList("authenticationprovider"); + assertNotNull("Providers details cannot be null", providerDetails); + assertEquals("Unexpected number of providers", 2, providerDetails.size()); + for (Map<String, Object> provider : providerDetails) + { + boolean managesPrincipals = true; + String type = PlainPasswordDatabaseAuthenticationManager.PROVIDER_TYPE; + if (ANONYMOUS_AUTHENTICATION_PROVIDER.equals(provider.get(AuthenticationProvider.NAME))) + { + type = AnonymousAuthenticationManager.PROVIDER_TYPE; + managesPrincipals = false; + } + assertProvider(managesPrincipals, type , provider); + Map<String, Object> data = getRestTestHelper().getJsonAsSingletonList("authenticationprovider/" + + provider.get(AuthenticationProvider.NAME)); + assertNotNull("Cannot load data for " + provider.get(AuthenticationProvider.NAME), data); + assertProvider(managesPrincipals, type, data); + } + } + + public void testPutCreateSecondPlainPrincipalDatabaseProviderSucceeds() throws Exception + { + File principalDatabase = getRestTestHelper().createTemporaryPasswdFile(new String[]{"admin2", "guest2", "test2"}); + + String providerName = "test-provider"; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AuthenticationProvider.NAME, providerName); + attributes.put(AuthenticationProvider.TYPE, PlainPasswordDatabaseAuthenticationManager.PROVIDER_TYPE); + attributes.put(ExternalFileBasedAuthenticationManager.PATH, principalDatabase.getAbsolutePath()); + + int responseCode = getRestTestHelper().submitRequest("authenticationprovider/" + providerName, "PUT", attributes); + assertEquals("failed to create authentication provider", 201, responseCode); + } + + public void testPutCreateNewAnonymousProvider() throws Exception + { + String providerName = "test-provider"; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AuthenticationProvider.NAME, providerName); + attributes.put(AuthenticationProvider.TYPE, AnonymousAuthenticationManager.PROVIDER_TYPE); + + int responseCode = getRestTestHelper().submitRequest("authenticationprovider/" + providerName, "PUT", attributes); + assertEquals("Unexpected response code", 201, responseCode); + + List<Map<String, Object>> providerDetails = getRestTestHelper().getJsonAsList("authenticationprovider/" + providerName); + assertNotNull("Providers details cannot be null", providerDetails); + assertEquals("Unexpected number of providers", 1, providerDetails.size()); + Map<String, Object> provider = providerDetails.get(0); + assertProvider(false, AnonymousAuthenticationManager.PROVIDER_TYPE, provider); + } + + public void testUpdateAuthenticationProviderIdFails() throws Exception + { + String providerName = "test-provider"; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AuthenticationProvider.NAME, providerName); + attributes.put(AuthenticationProvider.TYPE, AnonymousAuthenticationManager.PROVIDER_TYPE); + + int responseCode = getRestTestHelper().submitRequest("authenticationprovider/" + providerName, "PUT", attributes); + assertEquals("Unexpected response code", 201, responseCode); + + attributes.put(AuthenticationProvider.ID, UUID.randomUUID()); + + responseCode = getRestTestHelper().submitRequest("authenticationprovider/" + providerName, "PUT", attributes); + assertEquals("Update with new ID should fail", 409, responseCode); + } + + public void testDeleteOfUsedAuthenticationProviderFails() throws Exception + { + // create provider + String providerName = "test-provider"; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AuthenticationProvider.NAME, providerName); + attributes.put(AuthenticationProvider.TYPE, AnonymousAuthenticationManager.PROVIDER_TYPE); + + int responseCode = getRestTestHelper().submitRequest("authenticationprovider/" + providerName, "PUT", attributes); + assertEquals("Unexpected response code for provider creation", 201, responseCode); + + // create port + String portName = "test-port"; + Map<String, Object> portAttributes = new HashMap<String, Object>(); + portAttributes.put(Port.NAME, portName); + portAttributes.put(Port.AUTHENTICATION_PROVIDER, providerName); + portAttributes.put(Port.PORT, findFreePort()); + + responseCode = getRestTestHelper().submitRequest("port/" + portName, "PUT", portAttributes); + assertEquals("Unexpected response code for port creation", 201, responseCode); + + responseCode = getRestTestHelper().submitRequest("authenticationprovider/" + providerName , "DELETE"); + assertEquals("Unexpected response code for provider deletion", 409, responseCode); + + List<Map<String, Object>> providerDetails = getRestTestHelper().getJsonAsList("authenticationprovider/" + providerName); + assertNotNull("Providers details cannot be null", providerDetails); + assertEquals("Unexpected number of providers", 1, providerDetails.size()); + assertProvider(false, AnonymousAuthenticationManager.PROVIDER_TYPE, providerDetails.get(0)); + } + + public void testDeleteOfUnusedAuthenticationProvider() throws Exception + { + // create provider + String providerName = "test-provider"; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AuthenticationProvider.NAME, providerName); + attributes.put(AuthenticationProvider.TYPE, AnonymousAuthenticationManager.PROVIDER_TYPE); + + int responseCode = getRestTestHelper().submitRequest("authenticationprovider/" + providerName, "PUT", attributes); + assertEquals("Unexpected response code for provider creation", 201, responseCode); + + responseCode = getRestTestHelper().submitRequest("authenticationprovider/" + providerName , "DELETE"); + assertEquals("Unexpected response code for provider deletion", 200, responseCode); + + List<Map<String, Object>> providerDetails = getRestTestHelper().getJsonAsList("authenticationprovider/" + providerName); + assertNotNull("Providers details cannot be null", providerDetails); + assertEquals("Unexpected number of providers", 0, providerDetails.size()); + } + + public void testRemovalOfAuthenticationProviderInErrorStateUsingManagementMode() throws Exception + { + stopBroker(); + + File file = new File(TMP_FOLDER, getTestName()); + if (file.exists()) + { + file.delete(); + } + assertFalse("Group file should not exist", file.exists()); + + TestBrokerConfiguration config = getBrokerConfiguration(); + + String providerName = getTestName(); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AuthenticationProvider.TYPE, PlainPasswordDatabaseAuthenticationManager.PROVIDER_TYPE); + attributes.put(AuthenticationProvider.NAME, providerName); + attributes.put(ExternalFileBasedAuthenticationManager.PATH, file.getAbsoluteFile()); + + UUID id = config.addObjectConfiguration(AuthenticationProvider.class, attributes); + config.setSaved(false); + startBroker(0, true); + + getRestTestHelper().setUsernameAndPassword(BrokerOptions.MANAGEMENT_MODE_USER_NAME, MANAGEMENT_MODE_PASSWORD); + + Map<String, Object> provider = getRestTestHelper().getJsonAsSingletonList("authenticationprovider/" + providerName); + assertEquals("Unexpected id", id.toString(), provider.get(AuthenticationProvider.ID)); + assertEquals("Unexpected name", providerName, provider.get(AuthenticationProvider.NAME)); + assertEquals("Unexpected path", file.getAbsolutePath() , provider.get(ExternalFileBasedAuthenticationManager.PATH)); + assertEquals("Unexpected state", State.ERRORED.name() , provider.get(AuthenticationProvider.STATE)); + + int status = getRestTestHelper().submitRequest("authenticationprovider/" + providerName, "DELETE"); + assertEquals("ACL was not deleted", 200, status); + + List<Map<String, Object>> providers = getRestTestHelper().getJsonAsList("authenticationprovider/" + providerName); + assertEquals("Provider exists", 0, providers.size()); + } + + public void testUpdateOfAuthenticationProviderInErrorStateUsingManagementMode() throws Exception + { + stopBroker(); + + File file = new File(TMP_FOLDER, getTestName()); + if (file.exists()) + { + file.delete(); + } + assertFalse("Group file should not exist", file.exists()); + + TestBrokerConfiguration config = getBrokerConfiguration(); + + String providerName = getTestName(); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AuthenticationProvider.TYPE, PlainPasswordDatabaseAuthenticationManager.PROVIDER_TYPE); + attributes.put(AuthenticationProvider.NAME, providerName); + attributes.put(ExternalFileBasedAuthenticationManager.PATH, file.getAbsoluteFile()); + + UUID id = config.addObjectConfiguration(AuthenticationProvider.class, attributes); + config.setSaved(false); + startBroker(0, true); + + getRestTestHelper().setUsernameAndPassword(BrokerOptions.MANAGEMENT_MODE_USER_NAME, MANAGEMENT_MODE_PASSWORD); + + Map<String, Object> provider = getRestTestHelper().getJsonAsSingletonList("authenticationprovider/" + providerName); + assertEquals("Unexpected id", id.toString(), provider.get(AuthenticationProvider.ID)); + assertEquals("Unexpected name", providerName, provider.get(AuthenticationProvider.NAME)); + assertEquals("Unexpected path", file.getAbsolutePath() , provider.get(ExternalFileBasedAuthenticationManager.PATH)); + assertEquals("Unexpected state", State.ERRORED.name() , provider.get(AuthenticationProvider.STATE)); + + File principalDatabase = null; + try + { + principalDatabase = getRestTestHelper().createTemporaryPasswdFile(new String[]{"admin2", "guest2", "test2"}); + attributes = new HashMap<String, Object>(); + attributes.put(AuthenticationProvider.NAME, providerName); + attributes.put(AuthenticationProvider.ID, id); + attributes.put(AuthenticationProvider.TYPE, PlainPasswordDatabaseAuthenticationManager.PROVIDER_TYPE); + attributes.put(ExternalFileBasedAuthenticationManager.PATH, principalDatabase.getAbsolutePath()); + + int status = getRestTestHelper().submitRequest("authenticationprovider/" + providerName, "PUT", attributes); + assertEquals("ACL was not deleted", 200, status); + + provider = getRestTestHelper().getJsonAsSingletonList("authenticationprovider/" + providerName); + assertEquals("Unexpected id", id.toString(), provider.get(AuthenticationProvider.ID)); + assertEquals("Unexpected name", providerName, provider.get(AuthenticationProvider.NAME)); + assertEquals("Unexpected path", principalDatabase.getAbsolutePath() , provider.get( + ExternalFileBasedAuthenticationManager.PATH)); + assertEquals("Unexpected state", State.ACTIVE.name() , provider.get(AuthenticationProvider.STATE)); + } + finally + { + if (principalDatabase != null) + { + principalDatabase.delete(); + } + } + } + + public void testCreateAndDeletePasswordAuthenticationProviderWithNonExistingFile() throws Exception + { + stopBroker(); + getBrokerConfiguration().setSaved(false); + getBrokerConfiguration().removeObjectConfiguration(AuthenticationProvider.class, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + getBrokerConfiguration().setObjectAttribute(Port.class, TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT, Port.AUTHENTICATION_PROVIDER, ANONYMOUS_AUTHENTICATION_PROVIDER); + getBrokerConfiguration().setObjectAttribute(Port.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, Port.AUTHENTICATION_PROVIDER, ANONYMOUS_AUTHENTICATION_PROVIDER); + + startBroker(); + + File file = new File(TMP_FOLDER + File.separator + getTestName()); + if (file.exists()) + { + file.delete(); + } + assertFalse("File " + file.getAbsolutePath() + " should not exist", file.exists()); + + // create provider + String providerName = "test-provider"; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AuthenticationProvider.NAME, providerName); + attributes.put(AuthenticationProvider.TYPE, PlainPasswordDatabaseAuthenticationManager.PROVIDER_TYPE); + attributes.put(ExternalFileBasedAuthenticationManager.PATH, file.getAbsolutePath()); + + int responseCode = getRestTestHelper().submitRequest("authenticationprovider/" + providerName, "PUT", attributes); + assertEquals("Password provider was not created", 201, responseCode); + + + Map<String, Object> providerDetails = getRestTestHelper().getJsonAsSingletonList("authenticationprovider/" + providerName); + assertNotNull("Providers details cannot be null", providerDetails); + assertEquals("Unexpected name", providerName, providerDetails.get(AuthenticationProvider.NAME)); + assertEquals("Unexpected type", PlainPasswordDatabaseAuthenticationManager.PROVIDER_TYPE, providerDetails.get(AuthenticationProvider.TYPE)); + assertEquals("Unexpected path", file.getAbsolutePath(), providerDetails.get( + ExternalFileBasedAuthenticationManager.PATH)); + + assertTrue("User file should be created", file.exists()); + + responseCode = getRestTestHelper().submitRequest("authenticationprovider/" + providerName , "DELETE"); + assertEquals("Unexpected response code for provider deletion", 200, responseCode); + + List<Map<String, Object>> providers = getRestTestHelper().getJsonAsList("authenticationprovider/" + providerName); + assertNotNull("Providers details cannot be null", providers); + assertEquals("Unexpected number of providers", 0, providers.size()); + + assertFalse("File " + file.getAbsolutePath() + " should be deleted", file.exists()); + } + + private void assertProvider(boolean managesPrincipals, String type, Map<String, Object> provider) + { + Asserts.assertAttributesPresent(provider, BrokerModel.getInstance().getTypeRegistry().getAttributeNames( + AuthenticationProvider.class), + AuthenticationProvider.DESCRIPTION, ConfiguredObject.CONTEXT, + ConfiguredObject.DESIRED_STATE, ConfiguredObject.CREATED_BY, + ConfiguredObject.CREATED_TIME, ConfiguredObject.LAST_UPDATED_BY, ConfiguredObject.LAST_UPDATED_TIME); + 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)); + + if (managesPrincipals) + { + @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/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/BasicAuthRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/BasicAuthRestTest.java new file mode 100644 index 0000000000..c51457cdab --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/BasicAuthRestTest.java @@ -0,0 +1,122 @@ +/* + * + * 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 javax.servlet.http.HttpServletResponse; + +import org.apache.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.server.model.Plugin; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.Transport; +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 IOException + { + //do nothing, we will configure this locally + } + + private void configure(boolean useSsl) throws IOException + { + getRestTestHelper().setUseSsl(useSsl); + if (useSsl) + { + getBrokerConfiguration().setObjectAttribute(Port.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, Port.TRANSPORTS, Collections.singleton(Transport.SSL)); + getBrokerConfiguration().setObjectAttribute(Port.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); + } + super.customizeConfiguration(); + } + + private void verifyGetBrokerAttempt(int responseCode) throws IOException + { + assertEquals(responseCode, getRestTestHelper().submitRequest("broker", "GET")); + } + + public void testBasicAuthWhenEnabledWithHttps() 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 testBasicAuthWhenDisabledWithHttp() throws Exception + { + configure(false); + getBrokerConfiguration().setObjectAttribute(Plugin.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED, 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_UNAUTHORIZED); + } + + public void testEnablingForHttp() throws Exception + { + configure(false); + + getBrokerConfiguration().setObjectAttribute(Plugin.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED, 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(Plugin.class, 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_UNAUTHORIZED); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/BindingRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/BindingRestTest.java new file mode 100644 index 0000000000..368bc90d3d --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/BindingRestTest.java @@ -0,0 +1,131 @@ +/* + * + * 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.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.qpid.server.model.Binding; + +public class BindingRestTest extends QpidRestTestCase +{ + + @Override + public void setUp() throws Exception + { + super.setUp(); + getRestTestHelper().createTestQueues(); + } + + public void testGetAllBindings() throws Exception + { + List<Map<String, Object>> bindings = getRestTestHelper().getJsonAsList("binding/test"); + assertNotNull("Bindings cannot be null", bindings); + assertEquals("Unexpected number of bindings", RestTestHelper.EXPECTED_QUEUES.length, bindings.size()); + for (Map<String, Object> binding : bindings) + { + Asserts.assertBinding((String) binding.get(Binding.NAME), (String) binding.get(Binding.EXCHANGE), binding); + } + } + + public void testGetVirtualHostExchangeBindings() throws Exception + { + List<Map<String, Object>> bindings = getRestTestHelper().getJsonAsList("binding/test/test/amq.direct"); + assertNotNull("Bindings cannot be null", bindings); + assertEquals("Unexpected number of bindings", RestTestHelper.EXPECTED_QUEUES.length, bindings.size()); + for (String queueName : RestTestHelper.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("binding/test/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 + { + String bindingUrl = "binding/test/test/amq.direct/queue/queue"; + List<Map<String, Object>> bindings = getRestTestHelper().getJsonAsList(bindingUrl); + assertEquals("Unexpected number of bindings", 1, bindings.size()); + Asserts.assertBinding("queue", "amq.direct", bindings.get(0)); + + int responseCode = getRestTestHelper().submitRequest(bindingUrl, "DELETE"); + assertEquals("Unexpected response code", 200, responseCode); + + bindings = getRestTestHelper().getJsonAsList(bindingUrl); + assertEquals("Binding should be deleted", 0, bindings.size()); + } + + public void testDeleteBindingById() throws Exception + { + Map<String, Object> binding = getRestTestHelper().getJsonAsSingletonList("binding/test/test/amq.direct/queue"); + int responseCode = getRestTestHelper().submitRequest("binding/test/test/amq.direct?id=" + binding.get(Binding.ID), "DELETE"); + assertEquals("Unexpected response code", 200, responseCode); + List<Map<String, Object>> bindings = getRestTestHelper().getJsonAsList("binding/test/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"); + + String bindingUrl = "binding/test/test/amq.direct/queue/" + bindingName; + + int responseCode = getRestTestHelper().submitRequest(bindingUrl, "PUT", bindingData); + assertEquals("Unexpected response code", 201, responseCode); + + Map<String, Object> binding = getRestTestHelper().getJsonAsSingletonList(bindingUrl); + Asserts.assertBinding(bindingName, "queue", "amq.direct", binding); + } + + public void testSetBindingAttributesUnsupported() throws Exception + { + String bindingName = getTestName(); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Binding.NAME, bindingName); + attributes.put(Binding.QUEUE, "queue"); + attributes.put(Binding.EXCHANGE, "amq.direct"); + + String bindingUrl = "binding/test/test/amq.direct/queue/" + bindingName; + int responseCode = getRestTestHelper().submitRequest(bindingUrl, "PUT", attributes); + assertEquals("Unexpected response code", 201, responseCode); + + Map<String, Object> binding = getRestTestHelper().getJsonAsSingletonList(bindingUrl); + Asserts.assertBinding(bindingName, "queue", "amq.direct", binding); + + attributes.put(Binding.ARGUMENTS, "blah"); + + responseCode = getRestTestHelper().submitRequest(bindingUrl, "PUT", attributes); + assertEquals("Update should be unsupported", 409, responseCode); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/BrokerRestHttpAndHttpsTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/BrokerRestHttpAndHttpsTest.java new file mode 100644 index 0000000000..74db3e7040 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/BrokerRestHttpAndHttpsTest.java @@ -0,0 +1,85 @@ +/* + * + * 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.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.qpid.server.model.AuthenticationProvider; +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 BrokerRestHttpAndHttpsTest 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 IOException + { + super.customizeConfiguration(); + Map<String, Object> newAttributes = new HashMap<String, Object>(); + newAttributes.put(Port.PROTOCOLS, Collections.singleton(Protocol.HTTP)); + newAttributes.put(Port.TRANSPORTS, Arrays.asList(Transport.SSL, Transport.TCP)); + newAttributes.put(Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); + getBrokerConfiguration().setObjectAttributes(Port.class,TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT,newAttributes); + getBrokerConfiguration().setObjectAttribute(AuthenticationProvider.class, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER, + "secureOnlyMechanisms", + "[\"PLAIN\"]"); + + } + + public void testGetWithHttps() throws Exception + { + Collection<String> results = getMechanisms(true); + assertTrue("mechanisms did not contain PLAIN: " + results, results.contains("PLAIN")); + } + + + public void testGetWithHttp() throws Exception + { + Collection<String> results = getMechanisms(false); + assertFalse("mechanisms incorrectly contain PLAIN: " + results, results.contains("PLAIN")); + } + + + private Collection<String> getMechanisms(final boolean useSsl) throws IOException + { + getRestTestHelper().setUseSsl(useSsl); + Map<String, Object> mechanisms = getRestTestHelper().getJsonAsMap("/service/sasl"); + return (Collection<String>) mechanisms.get("mechanisms"); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/BrokerRestHttpsClientCertAuthTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/BrokerRestHttpsClientCertAuthTest.java new file mode 100644 index 0000000000..5b8d919d3e --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/BrokerRestHttpsClientCertAuthTest.java @@ -0,0 +1,86 @@ +/* + * + * 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.KEYSTORE; +import static org.apache.qpid.test.utils.TestSSLConstants.KEYSTORE_PASSWORD; +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.qpid.server.model.AuthenticationProvider; +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.server.security.auth.manager.ExternalAuthenticationManager; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class BrokerRestHttpsClientCertAuthTest 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); + setSystemProperty("javax.net.ssl.keystore", KEYSTORE); + setSystemProperty("javax.net.ssl.keyStorePassword", KEYSTORE_PASSWORD); + + } + + @Override + protected void customizeConfiguration() throws IOException + { + super.customizeConfiguration(); + getRestTestHelper().setUseSslAuth(true); + Map<String, Object> newAttributes = new HashMap<String, Object>(); + newAttributes.put(Port.PROTOCOLS, Collections.singleton(Protocol.HTTP)); + newAttributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); + newAttributes.put(Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); + newAttributes.put(Port.TRUST_STORES, Collections.singleton(TestBrokerConfiguration.ENTRY_NAME_SSL_TRUSTSTORE)); + newAttributes.put(Port.NEED_CLIENT_AUTH,"true"); + + + Map<String, Object> externalProviderAttributes = new HashMap<String, Object>(); + externalProviderAttributes.put(AuthenticationProvider.TYPE, ExternalAuthenticationManager.PROVIDER_TYPE); + externalProviderAttributes.put(AuthenticationProvider.NAME, EXTERNAL_AUTHENTICATION_PROVIDER); + getBrokerConfiguration().addObjectConfiguration(AuthenticationProvider.class, externalProviderAttributes); + + // set password authentication provider on http port for the tests + getBrokerConfiguration().setObjectAttribute(Port.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, Port.AUTHENTICATION_PROVIDER, + EXTERNAL_AUTHENTICATION_PROVIDER); + + getBrokerConfiguration().setObjectAttributes(Port.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, newAttributes); + } + + public void testGetWithHttps() throws Exception + { + Map<String, Object> saslData = getRestTestHelper().getJsonAsMap("/service/sasl"); + + Asserts.assertAttributesPresent(saslData, "user"); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/BrokerRestHttpsTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/BrokerRestHttpsTest.java new file mode 100644 index 0000000000..319cc1c9da --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/BrokerRestHttpsTest.java @@ -0,0 +1,77 @@ +/* + * + * 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.qpid.server.model.Broker; +import org.apache.qpid.server.model.BrokerModel; +import org.apache.qpid.server.model.ConfiguredObject; +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 IOException + { + super.customizeConfiguration(); + getRestTestHelper().setUseSsl(true); + Map<String, Object> newAttributes = new HashMap<String, Object>(); + newAttributes.put(Port.PROTOCOLS, Collections.singleton(Protocol.HTTP)); + newAttributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); + newAttributes.put(Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); + getBrokerConfiguration().setObjectAttributes(Port.class,TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT,newAttributes); + } + + public void testGetWithHttps() throws Exception + { + Map<String, Object> brokerDetails = getRestTestHelper().getJsonAsSingletonList("broker"); + + Asserts.assertAttributesPresent(brokerDetails, BrokerModel.getInstance().getTypeRegistry().getAttributeNames(Broker.class), + Broker.PROCESS_PID, + ConfiguredObject.TYPE, + ConfiguredObject.CREATED_BY, + ConfiguredObject.CREATED_TIME, + ConfiguredObject.LAST_UPDATED_BY, + ConfiguredObject.LAST_UPDATED_TIME, + ConfiguredObject.DESCRIPTION, + ConfiguredObject.CONTEXT, + ConfiguredObject.DESIRED_STATE); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/BrokerRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/BrokerRestTest.java new file mode 100644 index 0000000000..bae27b802c --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/BrokerRestTest.java @@ -0,0 +1,242 @@ +/* + * + * 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.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import javax.jms.Connection; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; + +import org.apache.qpid.common.QpidProperties; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.BrokerModel; +import org.apache.qpid.server.model.ConfiguredObject; +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.VirtualHostNode; +import org.apache.qpid.test.client.UnroutableMessageTestExceptionListener; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.util.SystemUtils; + +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_VIRTUALHOST_NODES_ATTRIBUTE = "virtualhostnodes"; + private static final String BROKER_STATISTICS_ATTRIBUTE = "statistics"; + + public void testGet() throws Exception + { + Map<String, Object> brokerDetails = getRestTestHelper().getJsonAsSingletonList("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>> nodes = (List<Map<String, Object>>) brokerDetails.get(BROKER_VIRTUALHOST_NODES_ATTRIBUTE); + assertEquals("Unexpected number of virtual hosts", 3, nodes.size()); + + for (String nodeName: EXPECTED_VIRTUALHOSTS) + { + Map<String, Object> nodeAttributes = getRestTestHelper().find(VirtualHostNode.NAME, nodeName, nodes); + assertNotNull("Node attributes are not found for node with name " + nodeName, nodeAttributes); + } + + @SuppressWarnings("unchecked") + List<Map<String, Object>> ports = (List<Map<String, Object>>) brokerDetails.get(BROKER_PORTS_ATTRIBUTE); + assertEquals("Unexpected number of ports", 2, 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 != null && 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)); + } + + public void testPutToUpdateWithValidAttributeValues() throws Exception + { + Map<String, Object> brokerAttributes = getValidBrokerAttributes(); + + int response = getRestTestHelper().submitRequest("broker", "PUT", brokerAttributes); + assertEquals("Unexpected update response", 200, response); + + restartBroker(); + Map<String, Object> brokerDetails = getRestTestHelper().getJsonAsSingletonList("broker"); + assertBrokerAttributes(brokerAttributes, brokerDetails); + } + + public void testPutUpdateWhereNumericAttributesAreSetAsStringValues() throws Exception + { + Map<String, Object> validAttributes = getValidBrokerAttributes(); + Map<String, Object> attributes = new HashMap<String, Object>(); + + for (Map.Entry<String, Object> entry : validAttributes.entrySet()) + { + Object value = entry.getValue(); + if (value instanceof Number) + { + value = String.valueOf(value); + } + attributes.put(entry.getKey(), value); + } + + int response = getRestTestHelper().submitRequest("broker", "PUT", attributes); + assertEquals("Unexpected update response", 200, response); + + Map<String, Object> brokerDetails = getRestTestHelper().getJsonAsSingletonList("broker"); + assertBrokerAttributes(validAttributes, brokerDetails); + } + + public void testPutToUpdateWithInvalidAttributeValues() throws Exception + { + Map<String, Object> invalidAttributes = new HashMap<String, Object>(); + invalidAttributes.put(Broker.DEFAULT_VIRTUAL_HOST, "non-existing-host"); + invalidAttributes.put(Broker.CONNECTION_SESSION_COUNT_LIMIT, -10); + invalidAttributes.put(Broker.CONNECTION_HEART_BEAT_DELAY, -11000); + invalidAttributes.put(Broker.STATISTICS_REPORTING_PERIOD, -12000); + + for (Map.Entry<String, Object> entry : invalidAttributes.entrySet()) + { + Map<String, Object> brokerAttributes = getValidBrokerAttributes(); + brokerAttributes.put(entry.getKey(), entry.getValue()); + int response = getRestTestHelper().submitRequest("broker", "PUT", brokerAttributes); + assertEquals("Unexpected update response for invalid attribute " + entry.getKey() + "=" + entry.getValue(), 409, response); + } + + } + + public void testSetCloseOnNoRoute() throws Exception + { + Map<String, Object> brokerDetails = getRestTestHelper().getJsonAsSingletonList("broker"); + assertTrue("closeOnNoRoute should be true", (Boolean)brokerDetails.get(Broker.CONNECTION_CLOSE_WHEN_NO_ROUTE)); + + Map<String, Object> brokerAttributes = new HashMap<String, Object>(); + brokerAttributes.put(Broker.CONNECTION_CLOSE_WHEN_NO_ROUTE, false); + + int response = getRestTestHelper().submitRequest("broker", "PUT", brokerAttributes); + assertEquals("Unexpected update response", 200, response); + + brokerDetails = getRestTestHelper().getJsonAsSingletonList("broker"); + assertFalse("closeOnNoRoute should be false", (Boolean)brokerDetails.get(Broker.CONNECTION_CLOSE_WHEN_NO_ROUTE)); + + Connection connection = getConnection(); + UnroutableMessageTestExceptionListener exceptionListener = new UnroutableMessageTestExceptionListener(); + connection.setExceptionListener(exceptionListener); + Session session = connection.createSession(true, Session.SESSION_TRANSACTED); + MessageProducer producer = session.createProducer(getTestQueue()); + TextMessage message = session.createTextMessage("Test"); + producer.send(message); + + session.commit(); + + exceptionListener.assertReceivedNoRouteWithReturnedMessage(message, getTestQueueName()); + } + + private Map<String, Object> getValidBrokerAttributes() + { + Map<String, Object> brokerAttributes = new HashMap<String, Object>(); + brokerAttributes.put(Broker.DEFAULT_VIRTUAL_HOST, TEST3_VIRTUALHOST); + brokerAttributes.put(Broker.CONNECTION_SESSION_COUNT_LIMIT, 10); + brokerAttributes.put(Broker.CONNECTION_HEART_BEAT_DELAY, 11000); + brokerAttributes.put(Broker.STATISTICS_REPORTING_PERIOD, 12000); + brokerAttributes.put(Broker.STATISTICS_REPORTING_RESET_ENABLED, true); + return brokerAttributes; + } + + private void assertBrokerAttributes(Map<String, Object> expectedAttributes, Map<String, Object> actualAttributes) + { + for (Map.Entry<String, Object> entry : expectedAttributes.entrySet()) + { + String attributeName = entry.getKey(); + Object attributeValue = entry.getValue(); + + Object currentValue = actualAttributes.get(attributeName); + assertEquals("Unexpected attribute " + attributeName + " value:", attributeValue, currentValue); + } + } + + protected void assertBrokerAttributes(Map<String, Object> brokerDetails) + { + Asserts.assertAttributesPresent(brokerDetails, BrokerModel.getInstance().getTypeRegistry().getAttributeNames( + Broker.class), + Broker.PROCESS_PID, + ConfiguredObject.TYPE, + ConfiguredObject.CREATED_BY, + ConfiguredObject.CREATED_TIME, + ConfiguredObject.LAST_UPDATED_BY, + ConfiguredObject.LAST_UPDATED_TIME, + ConfiguredObject.DESCRIPTION, + ConfiguredObject.CONTEXT, + ConfiguredObject.DESIRED_STATE); + + assertEquals("Unexpected value of attribute " + Broker.BUILD_VERSION, QpidProperties.getBuildVersion(), + brokerDetails.get(Broker.BUILD_VERSION)); + assertEquals("Unexpected value of attribute " + Broker.OPERATING_SYSTEM, SystemUtils.getOSString(), + 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, "Broker", 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 virtual host nodes", brokerDetails.get(BROKER_VIRTUALHOST_NODES_ATTRIBUTE)); + assertNotNull("Unexpected value of attribute ports", brokerDetails.get(BROKER_PORTS_ATTRIBUTE)); + assertNotNull("Unexpected value of attribute authenticationproviders", brokerDetails.get(BROKER_AUTHENTICATIONPROVIDERS_ATTRIBUTE)); + + assertNotNull("Unexpected value of attribute supportedVirtualHostTypes", brokerDetails.get(Broker.SUPPORTED_VIRTUALHOST_TYPES)); + assertNotNull("Unexpected value of attribute supportedVirtualHostNodeTypes", brokerDetails.get(Broker.SUPPORTED_VIRTUALHOSTNODE_TYPES)); + + } + +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/CompressedResponsesRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/CompressedResponsesRestTest.java new file mode 100644 index 0000000000..891b44cd25 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/CompressedResponsesRestTest.java @@ -0,0 +1,141 @@ +/* + * + * 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.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.util.LinkedHashMap; +import java.util.zip.GZIPInputStream; + +import org.codehaus.jackson.JsonParseException; +import org.codehaus.jackson.map.JsonMappingException; +import org.codehaus.jackson.map.ObjectMapper; + +import org.apache.qpid.server.model.Plugin; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class CompressedResponsesRestTest extends QpidRestTestCase +{ + + private boolean _compress; + + @Override + public void setUp() throws Exception + { + } + + @Override + protected void customizeConfiguration() throws IOException + { + super.customizeConfiguration(); + getBrokerConfiguration().setObjectAttribute(Plugin.class, + TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, + "compressResponses", + _compress); + } + + public void testCompressionOffAcceptOff() throws Exception + { + doCompressionTest(false, false); + } + + public void testCompressionOffAcceptOn() throws Exception + { + doCompressionTest(false, true); + } + + public void testCompressionOnAcceptOff() throws Exception + { + doCompressionTest(true, false); + } + + public void testCompressionOnAcceptOn() throws Exception + { + doCompressionTest(true, true); + + } + + private void doCompressionTest(final boolean allowCompression, + final boolean acceptCompressed) throws Exception + { + final boolean expectCompression = allowCompression && acceptCompressed; + _compress = allowCompression; + super.setUp(); + + HttpURLConnection conn = getRestTestHelper().openManagementConnection("/service/metadata", "GET"); + if(acceptCompressed) + { + conn.setRequestProperty("Accept-Encoding", "gzip"); + } + + conn.connect(); + + String contentEncoding = conn.getHeaderField("Content-Encoding"); + + if(expectCompression) + { + assertEquals("gzip", contentEncoding); + } + else + { + if(contentEncoding != null) + { + assertEquals("identity", contentEncoding); + } + } + + ByteArrayOutputStream contentBuffer = new ByteArrayOutputStream(); + + InputStream connectionInputStream = conn.getInputStream(); + byte[] buf = new byte[1024]; + int read; + while((read = connectionInputStream.read(buf))!= -1) + { + contentBuffer.write(buf,0,read); + } + + InputStream jsonStream; + + if(expectCompression) + { + jsonStream = new GZIPInputStream(new ByteArrayInputStream(contentBuffer.toByteArray())); + } + else + { + jsonStream = new ByteArrayInputStream(contentBuffer.toByteArray()); + } + + ObjectMapper mapper = new ObjectMapper(); + try + { + mapper.readValue(jsonStream, LinkedHashMap.class); + } + catch (JsonParseException | JsonMappingException e) + { + fail("Message was not in correct format"); + } + } + + +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/ConnectionRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/ConnectionRestTest.java new file mode 100644 index 0000000000..439e592a7e --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/ConnectionRestTest.java @@ -0,0 +1,281 @@ +/* + * + * 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 javax.servlet.http.HttpServletResponse; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.server.model.BrokerModel; +import org.apache.qpid.server.model.ConfiguredObject; +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("First 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("Subsequent messages were 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 after rollback", m); + } + + // Session left open + } + + public void testGetAllConnections() throws Exception + { + List<Map<String, Object>> connections = getRestTestHelper().getJsonAsList("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("connection/test/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("connection/test/test/" + + URLDecoder.decode(connectionName, "UTF-8")); + assertConnection(connectionDetails); + } + + public void testDeleteConnection() throws Exception + { + // get connection name + String connectionName = getConnectionName(); + + List<Map<String, Object>> connections = getRestTestHelper().getJsonAsList("connection/test/test"); + assertEquals("Unexpected number of connections before deletion", 1, connections.size()); + + String connectionUrl = "connection/test/test/" + URLDecoder.decode(connectionName, "UTF-8"); + getRestTestHelper().submitRequest(connectionUrl, "DELETE", HttpServletResponse.SC_OK); + + connections = getRestTestHelper().getJsonAsList("connection/test/test"); + assertEquals("Unexpected number of connections before deletion", 0, connections.size()); + + try + { + _connection.createSession(true, javax.jms.Session.SESSION_TRANSACTED); + fail("Exception not thrown"); + } + catch (JMSException je) + { + // PASS + } + } + + public void testGetAllSessions() throws Exception + { + List<Map<String, Object>> sessions = getRestTestHelper().getJsonAsList("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("session/test/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("session/test/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("session/test/test/" + + URLDecoder.decode(connectionName, "UTF-8") + "/" + ((AMQSession<?, ?>) _session).getChannelId()); + assertEquals("Unexpected number of sessions", 1, sessions.size()); + assertSession(sessions.get(0), (AMQSession<?, ?>) _session); + } + + public void testProducerSessionOpenHasTransactionStartAndUpdateTimes() throws Exception + { + Destination queue = _session.createQueue(getTestQueueName()); + MessageProducer producer = _session.createProducer(queue); + producer.send(_session.createMessage()); + // session left open + ((AMQSession)_session).sync(); + String connectionName = getConnectionName(); + + List<Map<String, Object>> sessions = getRestTestHelper().getJsonAsList("session/test/test/" + + URLDecoder.decode(connectionName, "UTF-8") + "/" + ((AMQSession<?, ?>) _session).getChannelId()); + assertEquals("Unexpected number of sessions", 1, sessions.size()); + + final Map<String, Object> sessionData = sessions.get(0); + + @SuppressWarnings("unchecked") + Map<String, Object> statistics = (Map<String, Object>) sessionData.get(Asserts.STATISTICS_ATTRIBUTE); + + long transactionStartTime = ((Number) statistics.get("transactionStartTime")).longValue(); + long transactionUpdateTime = ((Number) statistics.get("transactionUpdateTime")).longValue(); + + assertTrue("Unexpected transaction start value for open transaction " + transactionStartTime, transactionStartTime > 0); + assertTrue("Unexpected transaction update value for open transaction " + transactionUpdateTime, transactionUpdateTime > 0); + assertTrue("Expected transaction update value " + transactionUpdateTime + " to be greater than transaction start time " + transactionStartTime, transactionUpdateTime >= transactionStartTime); + + + } + + 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 " + "bytesIn", MESSAGE_NUMBER + * MESSAGE_SIZE, statistics.get("bytesIn")); + assertEquals("Unexpected value of connection statistics attribute " + "bytesOut", MESSAGE_SIZE + + ((MESSAGE_NUMBER - 1) * MESSAGE_SIZE) * 2, statistics.get("bytesOut")); + assertEquals("Unexpected value of connection statistics attribute " + "messagesIn", MESSAGE_NUMBER, + statistics.get("messagesIn")); + assertEquals("Unexpected value of connection statistics attribute " + "messagesOut", + MESSAGE_NUMBER * 2 - 1, statistics.get("messagesOut")); + + @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, BrokerModel.getInstance().getTypeRegistry().getAttributeNames( + Session.class), + ConfiguredObject.TYPE, + ConfiguredObject.CREATED_BY, + ConfiguredObject.CREATED_TIME, + ConfiguredObject.LAST_UPDATED_BY, + ConfiguredObject.LAST_UPDATED_TIME, + ConfiguredObject.DESCRIPTION, + ConfiguredObject.CONTEXT, + ConfiguredObject.DESIRED_STATE, + Session.STATE, + Session.DURABLE, + Session.LIFETIME_POLICY); + assertEquals("Unexpected value of attribute " + Session.NAME, session.getChannelId() + "", + sessionData.get(Session.NAME)); + assertEquals("Unexpected value of attribute " + Session.PRODUCER_FLOW_BLOCKED, Boolean.FALSE, + sessionData.get(Session.PRODUCER_FLOW_BLOCKED)); + assertEquals("Unexpected 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, "consumerCount", + "localTransactionBegins", "localTransactionOpen", + "localTransactionRollbacks", "unacknowledgedMessages", + "transactionStartTime", "transactionUpdateTime"); + + assertEquals("Unexpected value of statistic attribute " + "unacknowledgedMessages", MESSAGE_NUMBER - 1, + statistics.get("unacknowledgedMessages")); + assertEquals("Unexpected value of statistic attribute " + "localTransactionBegins", 4, + statistics.get("localTransactionBegins")); + assertEquals("Unexpected value of statistic attribute " + "localTransactionRollbacks", 1, + statistics.get("localTransactionRollbacks")); + assertEquals("Unexpected value of statistic attribute " + "consumerCount", 1, + statistics.get("consumerCount")); + } + + private String getConnectionName() throws IOException + { + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("virtualhost/test/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/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/ExchangeRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/ExchangeRestTest.java new file mode 100644 index 0000000000..51cb6dde1a --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/ExchangeRestTest.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.net.URLDecoder; +import java.util.HashMap; +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 +{ + @Override + public void setUp() throws Exception + { + super.setUp(); + getRestTestHelper().createTestQueues(); + } + + public void testGet() throws Exception + { + List<Map<String, Object>> exchanges = getRestTestHelper().getJsonAsList("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("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("exchange/test/test/" + + URLDecoder.decode(exchangeName, "UTF-8")); + assertExchange(exchangeName, exchange); + } + } + + public void testSetExchangeSupported() throws Exception + { + String exchangeName = getTestName(); + String exchangeUrl = "exchange/test/test/" + exchangeName; + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Exchange.NAME, exchangeName); + attributes.put(Exchange.TYPE, "direct"); + int responseCode = getRestTestHelper().submitRequest(exchangeUrl, "PUT", attributes); + assertEquals("Exchange should be created", 201, responseCode); + + Map<String, Object> exchange = getRestTestHelper().getJsonAsSingletonList(exchangeUrl); + assertNotNull("Exchange not found", exchange); + + attributes = new HashMap<String, Object>(); + attributes.put(Exchange.NAME, exchangeName); + attributes.put(Exchange.ALTERNATE_EXCHANGE, "amq.direct"); + + responseCode = getRestTestHelper().submitRequest(exchangeUrl, "PUT", attributes); + assertEquals("Exchange update should be supported", 200, responseCode); + exchange = getRestTestHelper().getJsonAsSingletonList(exchangeUrl); + assertNotNull("Exchange not found", exchange); + assertEquals("amq.direct",exchange.get(Exchange.ALTERNATE_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 : RestTestHelper.EXPECTED_QUEUES) + { + Map<String, Object> binding = getRestTestHelper().find(Binding.NAME, queueName, bindings); + Asserts.assertBinding(queueName, (String) exchange.get(Exchange.NAME), binding); + } + } + +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/GroupProviderRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/GroupProviderRestTest.java new file mode 100644 index 0000000000..4f1c1ad7a7 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/GroupProviderRestTest.java @@ -0,0 +1,373 @@ +/* + * + * 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.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.UUID; + +import org.apache.qpid.server.BrokerOptions; +import org.apache.qpid.server.model.BrokerModel; +import org.apache.qpid.server.model.ConfiguredObject; +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.adapter.FileBasedGroupProvider; +import org.apache.qpid.server.model.adapter.FileBasedGroupProviderImpl; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.test.utils.TestFileUtils; + +public class GroupProviderRestTest extends QpidRestTestCase +{ + private static final String FILE_GROUP_MANAGER = TestBrokerConfiguration.ENTRY_NAME_GROUP_FILE; + private File _groupFile; + + @Override + public void setUp() throws Exception + { + _groupFile = createTemporaryGroupFile(); + + getBrokerConfiguration().addGroupFileConfiguration(_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("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, FileBasedGroupProviderImpl.GROUP_FILE_PROVIDER_TYPE, provider); + Map<String, Object> data = getRestTestHelper().getJsonAsSingletonList("groupprovider/" + + provider.get(GroupProvider.NAME)); + assertNotNull("Cannot load data for " + provider.get(GroupProvider.NAME), data); + assertProvider(FILE_GROUP_MANAGER, FileBasedGroupProviderImpl.GROUP_FILE_PROVIDER_TYPE, data); + } + } + + public void testCreateNewGroup() throws Exception + { + String groupName = "newGroup"; + + Map<String, Object> data = getRestTestHelper().getJsonAsSingletonList("groupprovider/" + FILE_GROUP_MANAGER); + assertNotNull("Cannot load data for provider", data); + + getRestTestHelper().assertNumberOfGroups(data, 1); + + getRestTestHelper().createGroup(groupName, FILE_GROUP_MANAGER); + + data = getRestTestHelper().getJsonAsSingletonList("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("groupprovider/" + FILE_GROUP_MANAGER); + assertNotNull("Cannot load data for provider", data); + + getRestTestHelper().assertNumberOfGroups(data, 1); + + getRestTestHelper().removeGroup(groupName, FILE_GROUP_MANAGER); + + data = getRestTestHelper().getJsonAsSingletonList("groupprovider/" + FILE_GROUP_MANAGER); + assertNotNull("Cannot load data for provider", data); + + getRestTestHelper().assertNumberOfGroups(data, 0); + } + + public void testCreateNewFileGroupProviderFromExistingGroupFile() throws Exception + { + String[] groupMemberNames = {"test1","test2"}; + File groupFile = TestFileUtils.createTempFile(this, ".groups", "testusers.users=" + groupMemberNames[0] + "," + groupMemberNames[1]); + try + { + String providerName = getTestName(); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, providerName); + attributes.put(GroupProvider.TYPE, FileBasedGroupProviderImpl.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileBasedGroupProvider.PATH, groupFile.getAbsolutePath()); + + int responseCode = getRestTestHelper().submitRequest("groupprovider/" + providerName, "PUT", attributes); + assertEquals("Group provider was not created", 201, responseCode); + + Map<String, Object> data = getRestTestHelper().getJsonAsSingletonList("groupprovider/" + providerName + "?depth=2"); + assertProvider(providerName, FileBasedGroupProviderImpl.GROUP_FILE_PROVIDER_TYPE, data); + assertEquals("Unexpected name", providerName, data.get(GroupProvider.NAME)); + assertEquals("Unexpected path", groupFile.getAbsolutePath(), data.get(FileBasedGroupProvider.PATH)); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> groups = (List<Map<String, Object>>) data.get("groups"); + assertEquals("Unexpected group size", 1, groups.size()); + Map<String, Object> group = groups.get(0); + assertEquals("Unexpected group name", "testusers",group.get("name")); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> groupMemberList = (List<Map<String, Object>>) group.get("groupmembers"); + assertEquals("Unexpected group members size", 2, groupMemberList.size()); + + for (String memberName : groupMemberNames) + { + boolean found = false; + for (Map<String, Object> memberData : groupMemberList) + { + Object name = memberData.get("name"); + if (memberName.equals(name)) + { + found = true; + break; + } + } + assertTrue("Cannot find group member " + memberName + " in " + groupMemberList , found); + } + } + finally + { + groupFile.delete(); + } + } + + public void testCreationOfNewFileGroupProviderFailsWhenPathIsMissed() throws Exception + { + String providerName = getTestName(); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, providerName); + attributes.put(GroupProvider.TYPE, FileBasedGroupProviderImpl.GROUP_FILE_PROVIDER_TYPE); + + int responseCode = getRestTestHelper().submitRequest("groupprovider/" + providerName, "PUT", attributes); + assertEquals("Group provider was created", 409, responseCode); + } + + public void testCreateNewFileGroupProviderFromNonExistingGroupFile() throws Exception + { + File groupFile = new File(TMP_FOLDER + File.separator + getTestName() + File.separator + "groups"); + assertFalse("Group file should not exist", groupFile.exists()); + try + { + String providerName = getTestName(); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, providerName); + attributes.put(GroupProvider.TYPE, FileBasedGroupProviderImpl.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileBasedGroupProvider.PATH, groupFile.getAbsolutePath()); + + int responseCode = getRestTestHelper().submitRequest("groupprovider/" + providerName, "PUT", attributes); + assertEquals("Group provider was not created", 201, responseCode); + + Map<String, Object> data = getRestTestHelper().getJsonAsSingletonList("groupprovider/" + providerName); + assertEquals("Unexpected name", providerName, data.get(GroupProvider.NAME)); + assertEquals("Unexpected path", groupFile.getAbsolutePath(), data.get(FileBasedGroupProvider.PATH)); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> groups = (List<Map<String, Object>>) data.get("groups"); + assertNull("Unexpected groups", groups); + + assertTrue("Group file has not been created", groupFile.exists()); + } + finally + { + groupFile.delete(); + groupFile.getParentFile().delete(); + } + } + + public void testCreateNewFileGroupProviderForTheSameGroupFileFails() throws Exception + { + File groupFile = TestFileUtils.createTempFile(this, ".groups", "testusers.users=test1,test2"); + String providerName = getTestName(); + try + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, providerName); + attributes.put(GroupProvider.TYPE, FileBasedGroupProviderImpl.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileBasedGroupProvider.PATH, groupFile.getAbsolutePath()); + + int responseCode = getRestTestHelper().submitRequest("groupprovider/" + providerName, "PUT", attributes); + assertEquals("Group provider was not created", 201, responseCode); + + attributes.put(GroupProvider.NAME, providerName + 2); + responseCode = getRestTestHelper().submitRequest("groupprovider/" + providerName + 2, "PUT", attributes); + assertEquals("Group provider for the same group file was created", 409, responseCode); + } + finally + { + groupFile.delete(); + } + } + + public void testDeleteGroupProvider() throws Exception + { + File groupFile = TestFileUtils.createTempFile(this, ".groups", "testusers.users=test1,test2"); + String providerName = getTestName(); + try + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, providerName); + attributes.put(GroupProvider.TYPE, FileBasedGroupProviderImpl.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileBasedGroupProvider.PATH, groupFile.getAbsolutePath()); + + int responseCode = getRestTestHelper().submitRequest("groupprovider/" + providerName, "PUT", attributes); + assertEquals("Expected to fail because we can have only one password provider", 201, responseCode); + + responseCode = getRestTestHelper().submitRequest("groupprovider/" + providerName , "DELETE"); + assertEquals("Group provider was not deleted", 200, responseCode); + + List<Map<String, Object>> providerDetails = getRestTestHelper().getJsonAsList("groupprovider/" + providerName); + assertEquals("Provider was not deleted", 0, providerDetails.size()); + assertFalse("Groups file should be deleted", groupFile.exists()); + } + finally + { + groupFile.delete(); + } + } + + public void testUpdateGroupProviderAttributesFails() throws Exception + { + File groupFile = TestFileUtils.createTempFile(this, ".groups", "testusers.users=test1,test2"); + String providerName = getTestName(); + try + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, providerName); + attributes.put(GroupProvider.TYPE, FileBasedGroupProviderImpl.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileBasedGroupProvider.PATH, groupFile.getAbsolutePath()); + + int responseCode = getRestTestHelper().submitRequest("groupprovider/" + providerName, "PUT", attributes); + assertEquals("Expected to fail because we can have only one password provider", 201, responseCode); + + File newGroupFile = new File(TMP_FOLDER + File.separator + getTestName() + File.separator + "groups"); + attributes.put(FileBasedGroupProvider.PATH, newGroupFile.getAbsolutePath()); + + responseCode = getRestTestHelper().submitRequest("groupprovider/" + providerName, "PUT", attributes); + assertEquals("Expected to fail because we can have only one password provider", 409, responseCode); + } + finally + { + groupFile.delete(); + } + } + + public void testRemovalOfGroupProviderInErrorStateUsingManagementMode() throws Exception + { + stopBroker(); + + File file = new File(TMP_FOLDER, getTestName()); + if (file.exists()) + { + file.delete(); + } + assertFalse("Group file should not exist", file.exists()); + + TestBrokerConfiguration config = getBrokerConfiguration(); + config.removeObjectConfiguration(GroupProvider.class, TestBrokerConfiguration.ENTRY_NAME_GROUP_FILE); + UUID id = config.addGroupFileConfiguration(file.getAbsolutePath()); + config.setSaved(false); + startBroker(0, true); + + getRestTestHelper().setUsernameAndPassword(BrokerOptions.MANAGEMENT_MODE_USER_NAME, MANAGEMENT_MODE_PASSWORD); + + Map<String, Object> groupProvider = getRestTestHelper().getJsonAsSingletonList("groupprovider/" + TestBrokerConfiguration.ENTRY_NAME_GROUP_FILE); + assertEquals("Unexpected id", id.toString(), groupProvider.get(GroupProvider.ID)); + assertEquals("Unexpected path", file.getAbsolutePath() , groupProvider.get(FileBasedGroupProvider.PATH)); + assertEquals("Unexpected state", State.ERRORED.name() , groupProvider.get(GroupProvider.STATE)); + + int status = getRestTestHelper().submitRequest("groupprovider/" + TestBrokerConfiguration.ENTRY_NAME_GROUP_FILE, "DELETE"); + assertEquals("ACL was not deleted", 200, status); + + List<Map<String, Object>> providers = getRestTestHelper().getJsonAsList("groupprovider/" + TestBrokerConfiguration.ENTRY_NAME_GROUP_FILE); + assertEquals("Provider exists", 0, providers.size()); + } + + private void assertProvider(String name, String type, Map<String, Object> provider) + { + Asserts.assertAttributesPresent(provider, BrokerModel.getInstance().getTypeRegistry().getAttributeNames( + GroupProvider.class), + ConfiguredObject.TYPE, + ConfiguredObject.CREATED_BY, + ConfiguredObject.CREATED_TIME, + ConfiguredObject.LAST_UPDATED_BY, + ConfiguredObject.LAST_UPDATED_TIME, + ConfiguredObject.DESCRIPTION, + ConfiguredObject.CONTEXT, + ConfiguredObject.DESIRED_STATE); + 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)); + + assertEquals("Unexpected value of provider attribute " + GroupProvider.NAME, name, + (String) provider.get(GroupProvider.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)); + } + } + + 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/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/GroupRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/GroupRestTest.java new file mode 100644 index 0000000000..eeb9511289 --- /dev/null +++ b/qpid/java/systests/src/test/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.GroupMember; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class GroupRestTest extends QpidRestTestCase +{ + private static final String GROUP_NAME = "myGroup"; + private static final String FILE_GROUP_MANAGER = TestBrokerConfiguration.ENTRY_NAME_GROUP_FILE; + 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().addGroupFileConfiguration(_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("group/" + FILE_GROUP_MANAGER + "/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("group/" + FILE_GROUP_MANAGER + "/myGroup"); + getRestTestHelper().assertNumberOfGroupMembers(group, 1); + + getRestTestHelper().createNewGroupMember(FILE_GROUP_MANAGER, GROUP_NAME, NEW_MEMBER); + + group = getRestTestHelper().getJsonAsSingletonList("group/" + FILE_GROUP_MANAGER + "/myGroup"); + getRestTestHelper().assertNumberOfGroupMembers(group, 2); + } + + public void testRemoveMemberFromGroup() throws Exception + { + Map<String, Object> group = getRestTestHelper().getJsonAsSingletonList("group/" + FILE_GROUP_MANAGER + "/myGroup"); + getRestTestHelper().assertNumberOfGroupMembers(group, 1); + + getRestTestHelper().removeMemberFromGroup(FILE_GROUP_MANAGER, GROUP_NAME, EXISTING_MEMBER); + + group = getRestTestHelper().getJsonAsSingletonList("group/" + FILE_GROUP_MANAGER + "/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/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/HttpManagementRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/HttpManagementRestTest.java new file mode 100644 index 0000000000..abafb7fcaf --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/HttpManagementRestTest.java @@ -0,0 +1,91 @@ +/* + * + * 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.HashMap; +import java.util.Map; + +import org.apache.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class HttpManagementRestTest extends QpidRestTestCase +{ + + public void testGetHttpManagement() throws Exception + { + Map<String, Object> details = getRestTestHelper().getJsonAsSingletonList( + "plugin/" + TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT); + + assertEquals("Unexpected session timeout", HttpManagement.DEFAULT_TIMEOUT_IN_SECONDS, + details.get(HttpManagement.TIME_OUT)); + assertEquals("Unexpected http basic auth enabled", true, + details.get(HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED)); + assertEquals("Unexpected https basic auth enabled", true, + details.get(HttpManagement.HTTPS_BASIC_AUTHENTICATION_ENABLED)); + assertEquals("Unexpected http sasl auth enabled", true, + details.get(HttpManagement.HTTP_SASL_AUTHENTICATION_ENABLED)); + assertEquals("Unexpected https sasl auth enabled", true, + details.get(HttpManagement.HTTPS_SASL_AUTHENTICATION_ENABLED)); + } + + public void testUpdateAttributes() throws Exception + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(HttpManagement.NAME, TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT); + attributes.put(HttpManagement.HTTPS_BASIC_AUTHENTICATION_ENABLED, false); + attributes.put(HttpManagement.HTTPS_SASL_AUTHENTICATION_ENABLED, false); + attributes.put(HttpManagement.HTTP_SASL_AUTHENTICATION_ENABLED, false); + attributes.put(HttpManagement.TIME_OUT, 10000); + + getRestTestHelper().submitRequest("plugin/" + TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, "PUT", attributes); + + Map<String, Object> details = getRestTestHelper().getJsonAsSingletonList( + "plugin/" + TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT); + + assertEquals("Unexpected session timeout", 10000, details.get(HttpManagement.TIME_OUT)); + assertEquals("Unexpected http basic auth enabled", true, details.get(HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED)); + assertEquals("Unexpected https basic auth enabled", false, details.get(HttpManagement.HTTPS_BASIC_AUTHENTICATION_ENABLED)); + assertEquals("Unexpected http sasl auth enabled", false, details.get(HttpManagement.HTTP_SASL_AUTHENTICATION_ENABLED)); + assertEquals("Unexpected https sasl auth enabled", false, details.get(HttpManagement.HTTPS_SASL_AUTHENTICATION_ENABLED)); + } + + public void testUpdateAttributesWithInvalidValues() throws Exception + { + Map<String, Object> invalidAttributes = new HashMap<String, Object>(); + invalidAttributes.put(HttpManagement.HTTPS_BASIC_AUTHENTICATION_ENABLED, 1); + invalidAttributes.put(HttpManagement.HTTPS_SASL_AUTHENTICATION_ENABLED, 2); + invalidAttributes.put(HttpManagement.HTTP_SASL_AUTHENTICATION_ENABLED, 3); + invalidAttributes.put(HttpManagement.TIME_OUT, "undefined"); + + for (Map.Entry<String, Object> invalidAttribute : invalidAttributes.entrySet()) + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(invalidAttribute.getKey(), invalidAttribute.getValue()); + int response = getRestTestHelper().submitRequest("plugin/" + TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, "PUT", attributes); + assertEquals("Update should fail for attribute " + invalidAttribute.getKey() + " with value " + invalidAttribute.getValue() , 409, response); + } + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(HttpManagement.TIME_OUT, -1l); + int response = getRestTestHelper().submitRequest("plugin/" + TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, "PUT", attributes); + assertEquals("Update should fail for invalid session timeout", 409, response); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/KeyStoreRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/KeyStoreRestTest.java new file mode 100644 index 0000000000..4b881d1e9f --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/KeyStoreRestTest.java @@ -0,0 +1,272 @@ +/* + * + * 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.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.JsonParseException; +import org.codehaus.jackson.map.JsonMappingException; + +import org.apache.qpid.server.model.AbstractConfiguredObject; +import org.apache.qpid.server.model.KeyStore; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.Transport; +import org.apache.qpid.server.security.FileKeyStore; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.test.utils.TestSSLConstants; + +public class KeyStoreRestTest extends QpidRestTestCase +{ + @Override + public void setUp() throws Exception + { + // not calling super.setUp() to avoid broker start-up until + // after any necessary configuration + } + + public void testGet() throws Exception + { + super.setUp(); + + //verify existence of the default keystore used by the systests + List<Map<String, Object>> keyStores = assertNumberOfKeyStores(1); + + Map<String, Object> keystore = keyStores.get(0); + assertKeyStoreAttributes(keystore, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE, + System.getProperty(QPID_HOME) + "/../" + TestSSLConstants.BROKER_KEYSTORE, null); + } + + public void testCreate() throws Exception + { + super.setUp(); + + String name = getTestName(); + String certAlias = "app2"; + + assertNumberOfKeyStores(1); + createKeyStore(name, certAlias); + assertNumberOfKeyStores(2); + + List<Map<String, Object>> keyStores = getRestTestHelper().getJsonAsList("keystore/" + name); + assertNotNull("details cannot be null", keyStores); + + assertKeyStoreAttributes(keyStores.get(0), name, TestSSLConstants.KEYSTORE, certAlias); + } + + public void testDelete() throws Exception + { + super.setUp(); + + String name = getTestName(); + String certAlias = "app2"; + + assertNumberOfKeyStores(1); + createKeyStore(name, certAlias); + assertNumberOfKeyStores(2); + + int responseCode = getRestTestHelper().submitRequest("keystore/" + name , "DELETE"); + assertEquals("Unexpected response code for provider deletion", 200, responseCode); + + List<Map<String, Object>> keyStore = getRestTestHelper().getJsonAsList("keystore/" + name); + assertNotNull("details should not be null", keyStore); + assertTrue("details should be empty as the keystore no longer exists", keyStore.isEmpty()); + + //check only the default systests key store remains + List<Map<String, Object>> keyStores = assertNumberOfKeyStores(1); + Map<String, Object> keystore = keyStores.get(0); + assertKeyStoreAttributes(keystore, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE, + System.getProperty(QPID_HOME) + "/../" + TestSSLConstants.BROKER_KEYSTORE, null); + } + + public void testDeleteFailsWhenKeyStoreInUse() throws Exception + { + String name = "testDeleteFailsWhenKeyStoreInUse"; + + //add a new key store config to use + Map<String, Object> sslKeyStoreAttributes = new HashMap<String, Object>(); + sslKeyStoreAttributes.put(KeyStore.NAME, name); + sslKeyStoreAttributes.put(FileKeyStore.PATH, TestSSLConstants.BROKER_KEYSTORE); + sslKeyStoreAttributes.put(FileKeyStore.PASSWORD, TestSSLConstants.BROKER_KEYSTORE_PASSWORD); + getBrokerConfiguration().addObjectConfiguration(KeyStore.class,sslKeyStoreAttributes); + + //add the SSL port using it + Map<String, Object> sslPortAttributes = new HashMap<String, Object>(); + sslPortAttributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); + sslPortAttributes.put(Port.PORT, DEFAULT_SSL_PORT); + sslPortAttributes.put(Port.NAME, TestBrokerConfiguration.ENTRY_NAME_SSL_PORT); + sslPortAttributes.put(Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + sslPortAttributes.put(Port.KEY_STORE, name); + getBrokerConfiguration().addObjectConfiguration(Port.class,sslPortAttributes); + + super.setUp(); + + //verify the keystore is there + assertNumberOfKeyStores(2); + + List<Map<String, Object>> keyStore = getRestTestHelper().getJsonAsList("keystore/" + name); + assertNotNull("details should not be null", keyStore); + assertKeyStoreAttributes(keyStore.get(0), name, TestSSLConstants.BROKER_KEYSTORE, null); + + //try to delete it, which should fail as it is in use + int responseCode = getRestTestHelper().submitRequest("keystore/" + name , "DELETE"); + assertEquals("Unexpected response code for provider deletion", 409, responseCode); + + //check its still there + assertNumberOfKeyStores(2); + keyStore = getRestTestHelper().getJsonAsList("keystore/" + name); + assertNotNull("details should not be null", keyStore); + assertKeyStoreAttributes(keyStore.get(0), name, TestSSLConstants.BROKER_KEYSTORE, null); + } + + public void testUpdateWithGoodPathSucceeds() throws Exception + { + super.setUp(); + + String name = getTestName(); + + assertNumberOfKeyStores(1); + createKeyStore(name, null); + assertNumberOfKeyStores(2); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(KeyStore.NAME, name); + attributes.put(FileKeyStore.PATH, TestSSLConstants.UNTRUSTED_KEYSTORE); + + int responseCode = getRestTestHelper().submitRequest("keystore/" + name , "PUT", attributes); + assertEquals("Unexpected response code for keystore update", 200, responseCode); + + List<Map<String, Object>> keyStore = getRestTestHelper().getJsonAsList("keystore/" + name); + assertNotNull("details should not be null", keyStore); + + assertKeyStoreAttributes(keyStore.get(0), name, TestSSLConstants.UNTRUSTED_KEYSTORE, null); + } + + public void testUpdateWithNonExistentPathFails() throws Exception + { + super.setUp(); + + String name = getTestName(); + + assertNumberOfKeyStores(1); + createKeyStore(name, null); + assertNumberOfKeyStores(2); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(KeyStore.NAME, name); + attributes.put(FileKeyStore.PATH, "does.not.exist"); + + int responseCode = getRestTestHelper().submitRequest("keystore/" + name , "PUT", attributes); + assertEquals("Unexpected response code for keystore update", 409, responseCode); + + List<Map<String, Object>> keyStore = getRestTestHelper().getJsonAsList("keystore/" + name); + assertNotNull("details should not be null", keyStore); + + //verify the details remain unchanged + assertKeyStoreAttributes(keyStore.get(0), name, TestSSLConstants.KEYSTORE, null); + } + + public void testUpdateCertificateAlias() throws Exception + { + super.setUp(); + + String name = getTestName(); + + assertNumberOfKeyStores(1); + createKeyStore(name, "app1"); + assertNumberOfKeyStores(2); + + List<Map<String, Object>> keyStore = getRestTestHelper().getJsonAsList("keystore/" + name); + assertNotNull("details should not be null", keyStore); + assertKeyStoreAttributes(keyStore.get(0), name, TestSSLConstants.KEYSTORE, "app1"); + + //Update the certAlias from app1 to app2 + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(KeyStore.NAME, name); + attributes.put(FileKeyStore.CERTIFICATE_ALIAS, "app2"); + + int responseCode = getRestTestHelper().submitRequest("keystore/" + name , "PUT", attributes); + assertEquals("Unexpected response code for keystore update", 200, responseCode); + + keyStore = getRestTestHelper().getJsonAsList("keystore/" + name); + assertNotNull("details should not be null", keyStore); + + assertKeyStoreAttributes(keyStore.get(0), name, TestSSLConstants.KEYSTORE, "app2"); + + //Update the certAlias to clear it (i.e go from from app1 to null) + attributes = new HashMap<String, Object>(); + attributes.put(KeyStore.NAME, name); + attributes.put(FileKeyStore.CERTIFICATE_ALIAS, null); + + responseCode = getRestTestHelper().submitRequest("keystore/" + name , "PUT", attributes); + assertEquals("Unexpected response code for keystore update", 200, responseCode); + + keyStore = getRestTestHelper().getJsonAsList("keystore/" + name); + assertNotNull("details should not be null", keyStore); + + assertKeyStoreAttributes(keyStore.get(0), name, TestSSLConstants.KEYSTORE, null); + } + + private List<Map<String, Object>> assertNumberOfKeyStores(int numberOfKeystores) throws IOException, + JsonParseException, JsonMappingException + { + List<Map<String, Object>> keyStores = getRestTestHelper().getJsonAsList("keystore"); + assertNotNull("keystores should not be null", keyStores); + assertEquals("Unexpected number of keystores", numberOfKeystores, keyStores.size()); + + return keyStores; + } + + private void createKeyStore(String name, String certAlias) throws IOException, JsonGenerationException, JsonMappingException + { + Map<String, Object> keyStoreAttributes = new HashMap<String, Object>(); + keyStoreAttributes.put(KeyStore.NAME, name); + keyStoreAttributes.put(FileKeyStore.PATH, TestSSLConstants.KEYSTORE); + keyStoreAttributes.put(FileKeyStore.PASSWORD, TestSSLConstants.KEYSTORE_PASSWORD); + keyStoreAttributes.put(FileKeyStore.CERTIFICATE_ALIAS, certAlias); + + int responseCode = getRestTestHelper().submitRequest("keystore/" + name, "PUT", keyStoreAttributes); + assertEquals("Unexpected response code", 201, responseCode); + } + + private void assertKeyStoreAttributes(Map<String, Object> keystore, String name, String path, String certAlias) + { + assertEquals("default systests key store is missing", + name, keystore.get(KeyStore.NAME)); + assertEquals("unexpected path to key store", + path, keystore.get(FileKeyStore.PATH)); + assertEquals("unexpected (dummy) password of default systests key store", + AbstractConfiguredObject.SECURED_STRING_VALUE, keystore.get(FileKeyStore.PASSWORD)); + assertEquals("unexpected type of default systests key store", + java.security.KeyStore.getDefaultType(), keystore.get(FileKeyStore.KEY_STORE_TYPE)); + assertEquals("unexpected certificateAlias value", + certAlias, keystore.get(FileKeyStore.CERTIFICATE_ALIAS)); + if(certAlias == null) + { + assertFalse("should not be a certificateAlias attribute", + keystore.containsKey(FileKeyStore.CERTIFICATE_ALIAS)); + } + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/LogRecordsRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/LogRecordsRestTest.java new file mode 100644 index 0000000000..4d06c7b624 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/LogRecordsRestTest.java @@ -0,0 +1,63 @@ +/* + * + * 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("/service/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")); + } + + public void testGetLogsFromGivenId() throws Exception + { + List<Map<String, Object>> logs = getRestTestHelper().getJsonAsList("/service/logrecords"); + assertNotNull("Logs data cannot be null", logs); + assertTrue("Logs are not found", logs.size() > 0); + + Map<String, Object> lastLog = logs.get(logs.size() -1); + Object lastId = lastLog.get("id"); + + //make sure that new logs are created + getConnection(); + + List<Map<String, Object>> newLogs = getRestTestHelper().getJsonAsList("/service/logrecords?lastLogId=" + lastId); + assertNotNull("Logs data cannot be null", newLogs); + assertTrue("Logs are not found", newLogs.size() > 0); + + Object nextId = newLogs.get(0).get("id"); + + assertEquals("Unexpected next log id", ((Number)lastId).longValue() + 1, ((Number)nextId).longValue()); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/LogViewerTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/LogViewerTest.java new file mode 100644 index 0000000000..f2fb2581f7 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/LogViewerTest.java @@ -0,0 +1,105 @@ +/* + * + * 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.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.apache.qpid.server.BrokerOptions; + +public class LogViewerTest extends QpidRestTestCase +{ + public static final String DEFAULT_FILE_APPENDER_NAME = "FileAppender"; + private String _expectedLogFileName; + + public void setUp() throws Exception + { + setSystemProperty("logsuffix", "-" + getTestQueueName()); + _expectedLogFileName = System.getProperty("logprefix", "") + "qpid" + System.getProperty("logsuffix", "") + ".log"; + + // use real broker log file + File brokerLogFile = new File(System.getProperty(QPID_HOME), BrokerOptions.DEFAULT_LOG_CONFIG_FILE); + setBrokerCommandLog4JFile(brokerLogFile); + + super.setUp(); + } + + public void testGetLogFiles() throws Exception + { + List<Map<String, Object>> logFiles = getRestTestHelper().getJsonAsList("/service/logfilenames"); + assertNotNull("Log files data cannot be null", logFiles); + + // 1 file appender is configured in QPID default log4j xml: + assertTrue("Unexpected number of log files", logFiles.size() > 0); + + Map<String, Object> logFileDetails = logFiles.get(0); + assertEquals("Unexpected log file name", _expectedLogFileName, logFileDetails.get("name")); + assertEquals("Unexpected log file mime type", "text/plain", logFileDetails.get("mimeType")); + assertEquals("Unexpected log file appender",DEFAULT_FILE_APPENDER_NAME, logFileDetails.get("appenderName")); + assertTrue("Unexpected log file size", ((Number)logFileDetails.get("size")).longValue()>0); + assertTrue("Unexpected log file modification time", ((Number)logFileDetails.get("lastModified")).longValue()>0); + } + + public void testDownloadExistingLogFiles() throws Exception + { + byte[] bytes = getRestTestHelper().getBytes("/service/logfile?l=" + DEFAULT_FILE_APPENDER_NAME + "%2F" + _expectedLogFileName); + + ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(bytes)); + try + { + ZipEntry entry = zis.getNextEntry(); + assertEquals("Unexpected broker log file name", DEFAULT_FILE_APPENDER_NAME + "/" + _expectedLogFileName, entry.getName()); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int len; + while ((len = zis.read(buffer)) > 0) + { + baos.write(buffer, 0, len); + } + baos.close(); + assertTrue("Unexpected broker log file content", new String(baos.toByteArray()).contains("BRK-1004")); + assertNull("Unexpepected log file entry", zis.getNextEntry()); + } + finally + { + zis.close(); + } + } + + public void testDownloadNonExistingLogFiles() throws Exception + { + int responseCode = getRestTestHelper().submitRequest("/service/logfile?l=" + DEFAULT_FILE_APPENDER_NAME + "%2F" + + _expectedLogFileName + "_" + System.currentTimeMillis(), "GET"); + + assertEquals("Unexpected response code", 404, responseCode); + } + + public void testDownloadNonLogFiles() throws Exception + { + int responseCode = getRestTestHelper().submitRequest("/service/logfile?l=config.json", "GET"); + assertEquals("Unexpected response code", 400, responseCode); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/MessagesRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/MessagesRestTest.java new file mode 100644 index 0000000000..efa4776afd --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/MessagesRestTest.java @@ -0,0 +1,355 @@ +/* + * + * 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.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 javax.servlet.http.HttpServletResponse; + +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).close(); + _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("/service/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("/service/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("/service/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 + byte[] data = getRestTestHelper().getBytes("/service/message-content/test/" + queueName + "/" + lastMessageId); + 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 + + Map<String, Object> messagesData = new HashMap<String, Object>(); + messagesData.put("messages", movedMessageIds); + messagesData.put("destinationQueue", queueName2); + messagesData.put("move", Boolean.TRUE); + + getRestTestHelper().submitRequest("/service/message/test/" + queueName, "POST", messagesData, HttpServletResponse.SC_OK); + + // check messages on target queue + List<Map<String, Object>> messages = getRestTestHelper().getJsonAsList("/service/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("/service/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 + Map<String, Object> messagesData = new HashMap<String, Object>(); + messagesData.put("messages", copyMessageIds); + messagesData.put("destinationQueue", queueName2); + + getRestTestHelper().submitRequest("/service/message/test/" + queueName, "POST", messagesData, HttpServletResponse.SC_OK); + + // check messages on target queue + List<Map<String, Object>> messages = getRestTestHelper().getJsonAsList("/service/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("/service/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<>(); + 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 + getRestTestHelper().submitRequest("/service/message/test/" + queueName + "?" + queryString.toString(), "DELETE", HttpServletResponse.SC_OK); + + // check messages on queue + List<Map<String, Object>> messages = getRestTestHelper().getJsonAsList("/service/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); + } + } + + public void testClearQueue() throws Exception + { + String queueName = getTestQueueName(); + + // clear queue + getRestTestHelper().submitRequest("/service/message/test/" + queueName + "?clear=true", "DELETE", HttpServletResponse.SC_OK); + + // check messages on queue + List<Map<String, Object>> messages = getRestTestHelper().getJsonAsList("/service/message/test/" + queueName); + assertNotNull("Messages are not found", messages); + assertEquals("Unexpected number of messages", 0, messages.size()); + } + + + private List<Long> getMesssageIds(String queueName) throws IOException, JsonMappingException + { + List<Map<String, Object>> messages = getRestTestHelper().getJsonAsList("/service/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) + { + assertNull("Unexpected message attribute expirationTime", 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 persistent cannot be null", message.get("persistent")); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/PortRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/PortRestTest.java new file mode 100644 index 0000000000..8b86163aa6 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/PortRestTest.java @@ -0,0 +1,366 @@ +/* + * + * 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.ServerSocket; +import java.net.URLDecoder; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import org.apache.qpid.server.BrokerOptions; +import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.model.Plugin; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.Protocol; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.Transport; +import org.apache.qpid.server.model.port.JmxPort; +import org.apache.qpid.server.security.auth.manager.AnonymousAuthenticationManager; +import org.apache.qpid.test.utils.PortHelper; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class PortRestTest extends QpidRestTestCase +{ + + public void testGet() throws Exception + { + List<Map<String, Object>> ports = getRestTestHelper().getJsonAsList("port/"); + assertNotNull("Port data cannot be null", ports); + assertEquals("Unexpected number of ports", 2, 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("port/"); + assertNotNull("Ports data cannot be null", ports); + assertEquals("Unexpected number of ports", 2, 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("port/" + URLDecoder.decode(portName, "UTF-8")); + assertNotNull("Port " + portName + " is not found", portData); + Asserts.assertPortAttributes(portData); + } + } + + public void testPutAmqpPortWithMinimumAttributes() throws Exception + { + String portName = "test-port"; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, portName); + attributes.put(Port.PORT, findFreePort()); + attributes.put(Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + + int responseCode = getRestTestHelper().submitRequest("port/" + portName, "PUT", attributes); + assertEquals("Unexpected response code", 201, responseCode); + + List<Map<String, Object>> portDetails = getRestTestHelper().getJsonAsList("port/" + portName); + assertNotNull("Port details cannot be null", portDetails); + assertEquals("Unexpected number of ports with name " + portName, 1, portDetails.size()); + Map<String, Object> port = portDetails.get(0); + Asserts.assertPortAttributes(port); + + // make sure that port is there after broker restart + restartBroker(); + + portDetails = getRestTestHelper().getJsonAsList("port/" + portName); + assertNotNull("Port details cannot be null", portDetails); + assertEquals("Unexpected number of ports with name " + portName, 1, portDetails.size()); + } + + public void testPutRmiPortWithMinimumAttributes() throws Exception + { + String portNameRMI = "test-port-rmi"; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, portNameRMI); + int rmiPort = findFreePort(); + attributes.put(Port.PORT, rmiPort); + attributes.put(Port.PROTOCOLS, Collections.singleton(Protocol.RMI)); + + int responseCode = getRestTestHelper().submitRequest("port/" + portNameRMI, "PUT", attributes); + assertEquals("Unexpected response code", 201, responseCode); + + + List<Map<String, Object>> portDetails = getRestTestHelper().getJsonAsList("port/" + portNameRMI); + assertNotNull("Port details cannot be null", portDetails); + assertEquals("Unexpected number of ports with name " + portNameRMI, 1, portDetails.size()); + Map<String, Object> port = portDetails.get(0); + Asserts.assertPortAttributes(port, State.QUIESCED); + + String portNameJMX = "test-port-jmx"; + attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, portNameJMX); + int jmxPort = getNextAvailable(rmiPort + 1); + attributes.put(Port.PORT, jmxPort); + attributes.put(Port.PROTOCOLS, Collections.singleton(Protocol.JMX_RMI)); + attributes.put(JmxPort.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + + responseCode = getRestTestHelper().submitRequest("port/" + portNameJMX, "PUT", attributes); + assertEquals("Unexpected response code", 201, responseCode); + + + portDetails = getRestTestHelper().getJsonAsList("port/" + portNameJMX); + assertNotNull("Port details cannot be null", portDetails); + assertEquals("Unexpected number of ports with name " + portNameRMI, 1, portDetails.size()); + port = portDetails.get(0); + Asserts.assertPortAttributes(port, State.QUIESCED); + + + attributes.put(Plugin.TYPE, "MANAGEMENT-JMX"); + attributes.put(Plugin.NAME, "JmxPlugin"); + responseCode = getRestTestHelper().submitRequest("plugin/JmxPlugin", "PUT", attributes); + assertEquals("Unexpected response code", 201, responseCode); + + + // make sure that port is there after broker restart + stopBroker(); + + // We shouldn't need to await the ports to be free, but it seems sometimes an RMI TCP Accept + // thread is seen to close the socket *after* the broker has finished stopping. + new PortHelper().waitUntilPortsAreFree(new HashSet<Integer>(Arrays.asList(jmxPort, rmiPort))); + + startBroker(); + + portDetails = getRestTestHelper().getJsonAsList("port/" + portNameRMI); + assertNotNull("Port details cannot be null", portDetails); + assertEquals("Unexpected number of ports with name " + portNameRMI, 1, portDetails.size()); + port = portDetails.get(0); + Asserts.assertPortAttributes(port, State.ACTIVE); + + // try to add a second RMI port + portNameRMI = portNameRMI + "2"; + attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, portNameRMI); + attributes.put(Port.PORT, findFreePort()); + attributes.put(Port.PROTOCOLS, Collections.singleton(Protocol.RMI)); + + responseCode = getRestTestHelper().submitRequest("port/" + portNameRMI, "PUT", attributes); + assertEquals("Adding of a second RMI port should fail", 409, responseCode); + } + + public void testPutCreateAndUpdateAmqpPort() throws Exception + { + String portName = "test-port"; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, portName); + attributes.put(Port.PORT, findFreePort()); + attributes.put(Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + + int responseCode = getRestTestHelper().submitRequest("port/" + portName, "PUT", attributes); + assertEquals("Unexpected response code for port creation", 201, responseCode); + + List<Map<String, Object>> portDetails = getRestTestHelper().getJsonAsList("port/" + portName); + assertNotNull("Port details cannot be null", portDetails); + assertEquals("Unexpected number of ports with name " + portName, 1, portDetails.size()); + Map<String, Object> port = portDetails.get(0); + Asserts.assertPortAttributes(port); + + Map<String, Object> authProviderAttributes = new HashMap<String, Object>(); + authProviderAttributes.put(AuthenticationProvider.TYPE, AnonymousAuthenticationManager.PROVIDER_TYPE); + authProviderAttributes.put(AuthenticationProvider.NAME, TestBrokerConfiguration.ENTRY_NAME_ANONYMOUS_PROVIDER); + + responseCode = getRestTestHelper().submitRequest("authenticationprovider/" + TestBrokerConfiguration.ENTRY_NAME_ANONYMOUS_PROVIDER, "PUT", authProviderAttributes); + assertEquals("Unexpected response code for authentication provider creation", 201, responseCode); + + attributes = new HashMap<String, Object>(port); + attributes.put(Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_ANONYMOUS_PROVIDER); + attributes.put(Port.PROTOCOLS, Collections.singleton(Protocol.AMQP_0_9_1)); + + responseCode = getRestTestHelper().submitRequest("port/" + portName, "PUT", attributes); + assertEquals("Unexpected response code for port update", 200, responseCode); + } + + public void testUpdatePortTransportFromTCPToSSLWhenKeystoreIsConfigured() throws Exception + { + String portName = TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, portName); + attributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); + attributes.put(Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); + + int responseCode = getRestTestHelper().submitRequest("port/" + portName, "PUT", attributes); + assertEquals("Transport has not been changed to SSL " , 200, responseCode); + + Map<String, Object> port = getRestTestHelper().getJsonAsSingletonList("port/" + portName); + + @SuppressWarnings("unchecked") + Collection<String> transports = (Collection<String>) port.get(Port.TRANSPORTS); + assertEquals("Unexpected auth provider", new HashSet<String>(Arrays.asList(Transport.SSL.name())), + new HashSet<String>(transports)); + + String keyStore = (String) port.get(Port.KEY_STORE); + assertEquals("Unexpected auth provider", TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE, keyStore); + } + + public void testUpdateTransportFromTCPToSSLWithoutKeystoreConfiguredFails() throws Exception + { + String portName = TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, portName); + attributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); + + int responseCode = getRestTestHelper().submitRequest("port/" + portName, "PUT", attributes); + assertEquals("Creation of SSL port without keystore should fail", 409, responseCode); + } + + public void testUpdateWantNeedClientAuth() throws Exception + { + String portName = TestBrokerConfiguration.ENTRY_NAME_SSL_PORT; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, portName); + attributes.put(Port.PORT, DEFAULT_SSL_PORT); + attributes.put(Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + attributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); + attributes.put(Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); + attributes.put(Port.TRUST_STORES, Collections.singleton(TestBrokerConfiguration.ENTRY_NAME_SSL_TRUSTSTORE)); + + int responseCode = getRestTestHelper().submitRequest("port/" + portName, "PUT", attributes); + assertEquals("SSL port was not added", 201, responseCode); + + attributes.put(Port.NEED_CLIENT_AUTH, true); + attributes.put(Port.WANT_CLIENT_AUTH, true); + + responseCode = getRestTestHelper().submitRequest("port/" + portName, "PUT", attributes); + assertEquals("Attributes for need/want client auth are not set", 200, responseCode); + + Map<String, Object> port = getRestTestHelper().getJsonAsSingletonList("port/" + portName); + assertEquals("Unexpected " + Port.NEED_CLIENT_AUTH, true, port.get(Port.NEED_CLIENT_AUTH)); + assertEquals("Unexpected " + Port.WANT_CLIENT_AUTH, true, port.get(Port.WANT_CLIENT_AUTH)); + assertEquals("Unexpected " + Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE, port.get(Port.KEY_STORE)); + @SuppressWarnings("unchecked") + Collection<String> trustStores = (Collection<String>) port.get(Port.TRUST_STORES); + assertEquals("Unexpected auth provider", new HashSet<String>(Arrays.asList(TestBrokerConfiguration.ENTRY_NAME_SSL_TRUSTSTORE)), + new HashSet<String>(trustStores)); + + attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, portName); + attributes.put(Port.TRANSPORTS, Collections.singleton(Transport.TCP)); + + responseCode = getRestTestHelper().submitRequest("port/" + portName, "PUT", attributes); + assertEquals("Should not be able to change transport to TCP without reseting of attributes for need/want client auth", 409, responseCode); + + attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, portName); + attributes.put(Port.TRANSPORTS, Collections.singleton(Transport.TCP)); + attributes.put(Port.NEED_CLIENT_AUTH, false); + attributes.put(Port.WANT_CLIENT_AUTH, false); + + responseCode = getRestTestHelper().submitRequest("port/" + portName, "PUT", attributes); + assertEquals("Should be able to change transport to TCP ", 200, responseCode); + + port = getRestTestHelper().getJsonAsSingletonList("port/" + portName); + assertEquals("Unexpected " + Port.NEED_CLIENT_AUTH, false, port.get(Port.NEED_CLIENT_AUTH)); + assertEquals("Unexpected " + Port.WANT_CLIENT_AUTH, false, port.get(Port.WANT_CLIENT_AUTH)); + + @SuppressWarnings("unchecked") + Collection<String> transports = (Collection<String>) port.get(Port.TRANSPORTS); + assertEquals("Unexpected auth provider", new HashSet<String>(Arrays.asList(Transport.TCP.name())), + new HashSet<String>(transports)); + } + + public void testUpdateSettingWantNeedCertificateFailsForNonSSLPort() throws Exception + { + String portName = TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, portName); + attributes.put(Port.NEED_CLIENT_AUTH, true); + int responseCode = getRestTestHelper().submitRequest("port/" + portName, "PUT", attributes); + assertEquals("Unexpected response when trying to set 'needClientAuth' on non-SSL port", 409, responseCode); + + attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, portName); + attributes.put(Port.WANT_CLIENT_AUTH, true); + responseCode = getRestTestHelper().submitRequest("port/" + portName, "PUT", attributes); + assertEquals("Unexpected response when trying to set 'wantClientAuth' on non-SSL port", 409, responseCode); + } + + public void testUpdatePortAuthenticationProvider() throws Exception + { + String portName = TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, portName); + attributes.put(Port.AUTHENTICATION_PROVIDER, "non-existing"); + int responseCode = getRestTestHelper().submitRequest("port/" + portName, "PUT", attributes); + assertEquals("Unexpected response when trying to change auth provider to non-existing one", 409, responseCode); + + attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, portName); + attributes.put(Port.AUTHENTICATION_PROVIDER, ANONYMOUS_AUTHENTICATION_PROVIDER); + responseCode = getRestTestHelper().submitRequest("port/" + portName, "PUT", attributes); + assertEquals("Unexpected response when trying to change auth provider to existing one", 200, responseCode); + + Map<String, Object> port = getRestTestHelper().getJsonAsSingletonList("port/" + portName); + assertEquals("Unexpected auth provider", ANONYMOUS_AUTHENTICATION_PROVIDER, port.get(Port.AUTHENTICATION_PROVIDER)); + } + + public void testDefaultAmqpPortIsQuiescedWhenInManagementMode() throws Exception + { + // restart Broker in management port + stopBroker(); + startBroker(0, true); + getRestTestHelper().setUsernameAndPassword(BrokerOptions.MANAGEMENT_MODE_USER_NAME, MANAGEMENT_MODE_PASSWORD); + + String ampqPortName = TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT; + Map<String, Object> portData = getRestTestHelper().getJsonAsSingletonList("port/" + URLDecoder.decode(ampqPortName, "UTF-8")); + Asserts.assertPortAttributes(portData, State.QUIESCED); + } + + public void testNewPortErroredIfPortNumberInUse() throws Exception + { + String ampqPortName = TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT; + Map<String, Object> portData = getRestTestHelper().getJsonAsSingletonList("port/" + URLDecoder.decode(ampqPortName, "UTF-8")); + int amqpPort = (Integer)portData.get(Port.PORT); + + ServerSocket socket = new ServerSocket(0); + int occupiedPort = socket.getLocalPort(); + + int deleteResponseCode = getRestTestHelper().submitRequest("port/" + ampqPortName, "DELETE"); + assertEquals("Port deletion should be allowed", 200, deleteResponseCode); + + String newPortName = "reused-port"; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, newPortName); + attributes.put(Port.PORT, occupiedPort); // port in use + attributes.put(Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + + int responseCode = getRestTestHelper().submitRequest("port/" + newPortName, "PUT", attributes); + assertEquals("Unexpected response code for port creation", 201, responseCode); + + portData = getRestTestHelper().getJsonAsSingletonList("port/" + URLDecoder.decode(newPortName, "UTF-8")); + Asserts.assertPortAttributes(portData, State.ERRORED); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/PreferencesProviderRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/PreferencesProviderRestTest.java new file mode 100644 index 0000000000..6db204b9ca --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/PreferencesProviderRestTest.java @@ -0,0 +1,199 @@ +/* + * + * 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.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.model.BrokerModel; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.ExternalFileBasedAuthenticationManager; +import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.PreferencesProvider; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.adapter.FileSystemPreferencesProvider; +import org.apache.qpid.server.security.auth.manager.PlainPasswordDatabaseAuthenticationManager; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.test.utils.TestFileUtils; + +public class PreferencesProviderRestTest extends QpidRestTestCase +{ + private Map<String, File> _preferencesProviderFiles; + private File _authenticationProviderFile; + + public void setUp() throws Exception + { + _authenticationProviderFile = TestFileUtils.createTempFile(this, ".test.prefs.txt", "test:test"); + _preferencesProviderFiles = new HashMap<String, File>(); + super.setUp(); + } + + public void tearDown() throws Exception + { + try + { + super.tearDown(); + } + finally + { + if (_authenticationProviderFile != null) + { + _authenticationProviderFile.delete(); + } + for (File file : _preferencesProviderFiles.values()) + { + file.delete(); + } + } + } + + @Override + protected void customizeConfiguration() throws IOException + { + super.customizeConfiguration(); + Map<String, Object> anonymousAuthProviderAttributes = new HashMap<String, Object>(); + anonymousAuthProviderAttributes.put(AuthenticationProvider.TYPE, PlainPasswordDatabaseAuthenticationManager.PROVIDER_TYPE); + anonymousAuthProviderAttributes.put(AuthenticationProvider.NAME, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "-2"); + anonymousAuthProviderAttributes.put(ExternalFileBasedAuthenticationManager.PATH, _authenticationProviderFile.getAbsolutePath()); + getBrokerConfiguration().addObjectConfiguration(AuthenticationProvider.class,anonymousAuthProviderAttributes); + } + + public void testCreateAndGetProvider() throws Exception + { + List<Map<String, Object>> providerDetails = getRestTestHelper().getJsonAsList("preferencesprovider"); + assertEquals("Unexpected number of providers", 0, providerDetails.size()); + + createPreferencesProvider(TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER, "test1"); + createPreferencesProvider(TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "-2", "test2"); + + providerDetails = getRestTestHelper().getJsonAsList("preferencesprovider"); + assertEquals("Unexpected number of providers", 2, providerDetails.size()); + + for (Map<String, Object> provider : providerDetails) + { + assertProvider(provider); + } + + Map<String, Object> provider = getRestTestHelper().getJsonAsSingletonList( + "preferencesprovider/" + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/test1"); + assertProvider(provider); + assertEquals("Unexpected provider name ", "test1", provider.get(PreferencesProvider.NAME)); + + Map<String, Object> provider2 = getRestTestHelper().getJsonAsSingletonList( + "preferencesprovider/" + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "-2/test2"); + assertProvider(provider); + assertEquals("Unexpected provider name ", "test2", provider2.get(PreferencesProvider.NAME)); + } + + public void testDeleteProvider() throws Exception + { + createPreferencesProvider(TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER, "test1"); + String providerUrl = "preferencesprovider/" + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/test1"; + Map<String, Object> provider = getRestTestHelper().getJsonAsSingletonList(providerUrl); + assertProvider(provider); + assertEquals("Unexpected provider name ", "test1", provider.get(PreferencesProvider.NAME)); + + int responseCode = getRestTestHelper().submitRequest(providerUrl, "DELETE"); + assertEquals("Failed to delete preferences provider", 200, responseCode); + + List<Map<String, Object>> providerDetails = getRestTestHelper().getJsonAsList(providerUrl); + assertEquals("Unexpected number of providers", 0, providerDetails.size()); + } + + public void testUpdateProvider() throws Exception + { + createPreferencesProvider(TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER, "test1"); + String providerUrl = "preferencesprovider/" + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/test1"; + Map<String, Object> provider = getRestTestHelper().getJsonAsSingletonList(providerUrl); + assertProvider(provider); + assertEquals("Unexpected provider name ", "test1", provider.get(PreferencesProvider.NAME)); + + File file = TestFileUtils.createTempFile(this, ".prefs.json", "{\"admin\":{\"something\": \"somethingValue\"}}"); + _preferencesProviderFiles.put("new-test1", file); + Map<String, Object> newAttributes = new HashMap<String, Object>(); + newAttributes.put(FileSystemPreferencesProvider.PATH, file.getAbsolutePath()); + + int responseCode = getRestTestHelper().submitRequest(providerUrl, "PUT", newAttributes); + assertEquals("Failed to update preferences provider", 200, responseCode); + + List<Map<String, Object>> providerDetails = getRestTestHelper().getJsonAsList(providerUrl); + assertEquals("Unexpected number of providers", 1, providerDetails.size()); + + provider = providerDetails.get(0); + assertProviderCommonAttributes(provider); + String name = (String) provider.get(PreferencesProvider.NAME); + assertEquals("Unexpected name", "test1", name); + assertEquals("Unexpected path for provider " + name, (String) provider.get(FileSystemPreferencesProvider.PATH), + file.getAbsolutePath()); + } + + private void assertProvider(Map<String, Object> provider) + { + assertProviderCommonAttributes(provider); + + String name = (String) provider.get(PreferencesProvider.NAME); + assertNotNull("Name cannot be null", name); + assertEquals("Unexpected path for provider " + name, (String) provider.get(FileSystemPreferencesProvider.PATH), + _preferencesProviderFiles.get(name).getAbsolutePath()); + } + + public void assertProviderCommonAttributes(Map<String, Object> provider) + { + Asserts.assertAttributesPresent(provider, + BrokerModel.getInstance().getTypeRegistry().getAttributeNames( + PreferencesProvider.class), + ConfiguredObject.CREATED_BY, + ConfiguredObject.CREATED_TIME, + ConfiguredObject.LAST_UPDATED_BY, + ConfiguredObject.LAST_UPDATED_TIME, + ConfiguredObject.DESCRIPTION, + ConfiguredObject.CONTEXT, + ConfiguredObject.DESIRED_STATE); + assertEquals("Unexpected value of provider attribute " + PreferencesProvider.STATE, State.ACTIVE.name(), + provider.get(PreferencesProvider.STATE)); + assertEquals("Unexpected value of provider attribute " + PreferencesProvider.LIFETIME_POLICY, + LifetimePolicy.PERMANENT.name(), provider.get(PreferencesProvider.LIFETIME_POLICY)); + assertEquals("Unexpected value of provider attribute " + PreferencesProvider.DURABLE, Boolean.TRUE, + provider.get(PreferencesProvider.DURABLE)); + assertEquals("Unexpected value of provider attribute " + PreferencesProvider.TYPE, + FileSystemPreferencesProvider.PROVIDER_TYPE, provider.get(PreferencesProvider.TYPE)); + } + + private void createPreferencesProvider(String authenticationProvider, String providerName) throws Exception + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(PreferencesProvider.NAME, providerName); + attributes.put(PreferencesProvider.TYPE, FileSystemPreferencesProvider.PROVIDER_TYPE); + File file = TestFileUtils.createTempFile(this, ".prefs.json", "{\"admin\":{\"language\": \"en\"}}"); + _preferencesProviderFiles.put(providerName, file); + attributes.put(FileSystemPreferencesProvider.PATH, file.getAbsolutePath()); + + int responseCode = getRestTestHelper().submitRequest( + "preferencesprovider/" + authenticationProvider + "/" + providerName, "PUT", attributes); + assertEquals("Unexpected response code", 201, responseCode); + } + +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/PreferencesRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/PreferencesRestTest.java new file mode 100644 index 0000000000..bd72391522 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/PreferencesRestTest.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.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.qpid.server.model.PreferencesProvider; +import org.apache.qpid.server.model.adapter.FileSystemPreferencesProvider; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.test.utils.TestFileUtils; + +public class PreferencesRestTest extends QpidRestTestCase +{ + private File _preferencesProviderFile; + + public void setUp() throws Exception + { + _preferencesProviderFile = TestFileUtils.createTempFile(this, ".prefs.json", + "{\"webadmin\":{\"language\": \"en\", \"saveTabs\":true}}"); + super.setUp(); + } + + public void tearDown() throws Exception + { + try + { + super.tearDown(); + } + finally + { + if (_preferencesProviderFile != null) + { + _preferencesProviderFile.delete(); + } + } + } + + @Override + protected void customizeConfiguration() throws IOException + { + super.customizeConfiguration(); + + TestBrokerConfiguration brokerConfiguration = getBrokerConfiguration(); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(PreferencesProvider.NAME, "test"); + attributes.put(PreferencesProvider.TYPE, FileSystemPreferencesProvider.PROVIDER_TYPE); + attributes.put(FileSystemPreferencesProvider.PATH, _preferencesProviderFile.getAbsolutePath()); + brokerConfiguration.addPreferencesProviderConfiguration(TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER, + attributes); + + } + + public void testGetPreferences() throws Exception + { + Map<String, Object> preferences = getRestTestHelper().getJsonAsMap("/service/preferences"); + assertEquals("Unexpected number of preferences", 2, preferences.size()); + assertEquals("Unexpected language preference", "en", preferences.get("language")); + assertEquals("Unexpected saveTabs preference", true, preferences.get("saveTabs")); + } + + public void testUpdatePreferences() throws Exception + { + Map<String, Object> additionalPreferences = new HashMap<String, Object>(); + additionalPreferences.put("timezone", "Europe/London"); + additionalPreferences.put("test", 1); + + int status = getRestTestHelper().submitRequest("/service/preferences", "POST", additionalPreferences); + assertEquals("Unexpected response code", 200, status); + + Map<String, Object> preferences = getRestTestHelper().getJsonAsMap("/service/preferences"); + assertEquals("Unexpected number of preferences", 4, preferences.size()); + assertEquals("Unexpected language preference", "en", preferences.get("language")); + assertEquals("Unexpected saveTabs preference", true, preferences.get("saveTabs")); + assertEquals("Unexpected timezone preference", "Europe/London", preferences.get("timezone")); + assertEquals("Unexpected test preference", 1, preferences.get("test")); + } + + public void testReplacePreferences() throws Exception + { + Map<String, Object> additionalPreferences = new HashMap<String, Object>(); + additionalPreferences.put("timezone", "Europe/London"); + additionalPreferences.put("test", 1); + + int status = getRestTestHelper().submitRequest("/service/preferences", "PUT", additionalPreferences); + assertEquals("Unexpected response code", 200, status); + + Map<String, Object> preferences = getRestTestHelper().getJsonAsMap("/service/preferences"); + assertEquals("Unexpected number of preferences", 2, preferences.size()); + assertEquals("Unexpected timezone preference", "Europe/London", preferences.get("timezone")); + assertEquals("Unexpected test preference", 1, preferences.get("test")); + } + +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/QueueRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/QueueRestTest.java new file mode 100644 index 0000000000..baebc9a28e --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/QueueRestTest.java @@ -0,0 +1,262 @@ +/* + * + * 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.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.BrokerModel; +import org.apache.qpid.server.model.ConfiguredObject; +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("queue/test"); + assertEquals("Unexpected number of queues", 1, queues.size()); + String[] expectedQueues = new String[]{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", 1, bindings.size()); + + Map<String, Object> directExchangeBinding = getRestTestHelper().find(Binding.EXCHANGE, "amq.direct", bindings); + Asserts.assertBinding(name, "amq.direct", directExchangeBinding); + } + } + + public void testGetByName() throws Exception + { + String queueName = getTestQueueName(); + Map<String, Object> queueDetails = getRestTestHelper().getJsonAsSingletonList("queue/test/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", 1, bindings.size()); + + Map<String, Object> directExchangeBinding = getRestTestHelper().find(Binding.EXCHANGE, "amq.direct", bindings); + 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 testUpdateQueue() throws Exception + { + String queueName = getTestName(); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Queue.NAME, queueName); + + String queueUrl = "queue/test/test/" + queueName; + int responseCode = getRestTestHelper().submitRequest(queueUrl, "PUT", attributes); + assertEquals("Queue was not created", 201, responseCode); + + Map<String, Object> queueDetails = getRestTestHelper().getJsonAsSingletonList(queueUrl); + Asserts.assertQueue(queueName, "standard", queueDetails); + + attributes = new HashMap<String, Object>(); + attributes.put(Queue.NAME, queueName); + attributes.put(Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES, 100000); + attributes.put(Queue.QUEUE_FLOW_RESUME_SIZE_BYTES, 80000); + attributes.put(Queue.ALERT_REPEAT_GAP, 10000); + attributes.put(Queue.ALERT_THRESHOLD_MESSAGE_AGE, 20000); + attributes.put(Queue.ALERT_THRESHOLD_MESSAGE_SIZE, 30000); + attributes.put(Queue.ALERT_THRESHOLD_QUEUE_DEPTH_BYTES, 40000); + attributes.put(Queue.ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, 50000); + attributes.put(Queue.MAXIMUM_DELIVERY_ATTEMPTS, 10); + + responseCode = getRestTestHelper().submitRequest(queueUrl, "PUT", attributes); + assertEquals("Setting of queue attributes should be allowed", 200, responseCode); + + Map<String, Object> queueData = getRestTestHelper().getJsonAsSingletonList(queueUrl); + assertEquals("Unexpected " + Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES, 100000, queueData.get(Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES) ); + assertEquals("Unexpected " + Queue.QUEUE_FLOW_RESUME_SIZE_BYTES, 80000, queueData.get(Queue.QUEUE_FLOW_RESUME_SIZE_BYTES) ); + assertEquals("Unexpected " + Queue.ALERT_REPEAT_GAP, 10000, queueData.get(Queue.ALERT_REPEAT_GAP) ); + assertEquals("Unexpected " + Queue.ALERT_THRESHOLD_MESSAGE_AGE, 20000, queueData.get(Queue.ALERT_THRESHOLD_MESSAGE_AGE) ); + assertEquals("Unexpected " + Queue.ALERT_THRESHOLD_MESSAGE_SIZE, 30000, queueData.get(Queue.ALERT_THRESHOLD_MESSAGE_SIZE) ); + assertEquals("Unexpected " + Queue.ALERT_THRESHOLD_QUEUE_DEPTH_BYTES, 40000, queueData.get(Queue.ALERT_THRESHOLD_QUEUE_DEPTH_BYTES) ); + assertEquals("Unexpected " + Queue.ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, 50000, queueData.get(Queue.ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES) ); + } + + public void testPutCreateBinding() throws Exception + { + String queueName = getTestQueueName(); + String bindingName = queueName + 2; + String[] exchanges = { "amq.direct", "amq.fanout", "amq.topic", "amq.match" }; + + for (int i = 0; i < exchanges.length; i++) + { + createBinding(bindingName, exchanges[i], queueName); + } + + Map<String, Object> queueDetails = getRestTestHelper().getJsonAsSingletonList("queue/test/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 + 1, 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 + { + Map<String, Object> bindingData = new HashMap<String, Object>(); + bindingData.put(Binding.NAME, bindingName); + bindingData.put(Binding.EXCHANGE, exchangeName); + bindingData.put(Binding.QUEUE, queueName); + + String url = "binding/test/test/" + URLDecoder.decode(exchangeName, "UTF-8") + "/" + queueName + "/" + bindingName; + int responseCode = getRestTestHelper().submitRequest(url, "PUT", bindingData); + assertEquals("Unexpected response code", 201, responseCode); + } + + private void assertConsumer(Map<String, Object> consumer) + { + assertNotNull("Consumer map should not be null", consumer); + Asserts.assertAttributesPresent(consumer, + BrokerModel.getInstance().getTypeRegistry().getAttributeNames(Consumer.class), Consumer.STATE, + Consumer.SETTLEMENT_MODE, Consumer.EXCLUSIVE, Consumer.SELECTOR, + Consumer.NO_LOCAL, + ConfiguredObject.TYPE, + ConfiguredObject.CREATED_BY, + ConfiguredObject.CREATED_TIME, + ConfiguredObject.LAST_UPDATED_BY, + ConfiguredObject.LAST_UPDATED_TIME, + ConfiguredObject.DESCRIPTION, + ConfiguredObject.CONTEXT, + ConfiguredObject.DESIRED_STATE); + + 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.DELETE_ON_SESSION_END.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, "bytesOut", "messagesOut", "unacknowledgedBytes", "unacknowledgedMessages"); + } + + 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 " + "persistentDequeuedMessages", DEQUEUED_MESSAGES, + statistics.get("persistentDequeuedMessages")); + assertEquals("Unexpected queue statistics attribute " + "queueDepthMessages", ENQUEUED_MESSAGES, + statistics.get("queueDepthMessages")); + assertEquals("Unexpected queue statistics attribute " + "consumerCount", 1, + statistics.get("consumerCount")); + assertEquals("Unexpected queue statistics attribute " + "consumerCountWithCredit", 1, + statistics.get("consumerCountWithCredit")); + assertEquals("Unexpected queue statistics attribute " + "bindingCount", 1, statistics.get("bindingCount")); + assertEquals("Unexpected queue statistics attribute " + "persistentDequeuedMessages", DEQUEUED_MESSAGES, + statistics.get("persistentDequeuedMessages")); + assertEquals("Unexpected queue statistics attribute " + "totalDequeuedMessages", DEQUEUED_MESSAGES, + statistics.get("totalDequeuedMessages")); + assertEquals("Unexpected queue statistics attribute " + "totalDequeuedBytes", DEQUEUED_BYTES, + statistics.get("totalDequeuedBytes")); + assertEquals("Unexpected queue statistics attribute " + "persistentDequeuedBytes", DEQUEUED_BYTES, + statistics.get("totalDequeuedBytes")); + assertEquals("Unexpected queue statistics attribute " + "persistentEnqueuedBytes", ENQUEUED_BYTES + + DEQUEUED_BYTES, statistics.get("persistentEnqueuedBytes")); + assertEquals("Unexpected queue statistics attribute " + "totalEnqueuedBytes", ENQUEUED_BYTES + DEQUEUED_BYTES, + statistics.get("totalEnqueuedBytes")); + assertEquals("Unexpected queue statistics attribute " + "queueDepthBytes", ENQUEUED_BYTES, + statistics.get("queueDepthBytes")); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/SaslRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/SaslRestTest.java new file mode 100644 index 0000000000..547b7b1b00 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/SaslRestTest.java @@ -0,0 +1,384 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.systest.rest; + +import static org.apache.qpid.server.security.auth.sasl.SaslUtil.generateCramMD5ClientResponse; +import static org.apache.qpid.server.security.auth.sasl.SaslUtil.generateCramMD5HexClientResponse; +import static org.apache.qpid.server.security.auth.sasl.SaslUtil.generatePlainClientResponse; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.codec.binary.Base64; +import org.codehaus.jackson.JsonParseException; +import org.codehaus.jackson.map.JsonMappingException; + +import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.security.auth.manager.Base64MD5PasswordDatabaseAuthenticationManager; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.tools.security.Passwd; + +public class SaslRestTest extends QpidRestTestCase +{ + @Override + public void startBroker() + { + // prevent broker from starting in setUp + } + + public void startBrokerNow() throws Exception + { + super.startBroker(); + + getRestTestHelper().setUsernameAndPassword(null,null); + } + + public void testGetMechanismsWithBrokerPlainPasswordPrincipalDatabase() throws Exception + { + startBrokerNow(); + + Map<String, Object> saslData = getRestTestHelper().getJsonAsMap("/service/sasl"); + assertNotNull("mechanisms attribute is not found", saslData.get("mechanisms")); + + @SuppressWarnings("unchecked") + List<String> mechanisms = (List<String>) saslData.get("mechanisms"); + String[] expectedMechanisms = { "CRAM-MD5" }; + for (String mechanism : expectedMechanisms) + { + assertTrue("Mechanism " + mechanism + " is not found", mechanisms.contains(mechanism)); + } + assertNull("Unexpected user was returned: " + saslData.get("user"), saslData.get("user")); + } + + public void testGetMechanismsWithBrokerBase64MD5FilePrincipalDatabase() throws Exception + { + configureBase64MD5FilePrincipalDatabase(); + startBrokerNow(); + + Map<String, Object> saslData = getRestTestHelper().getJsonAsMap("/service/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"), 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("/service/sasl", "POST"); + OutputStream os = connection.getOutputStream(); + os.write(parameters.getBytes()); + os.flush(); + + int code = getRestTestHelper().submitRequest("/service/sasl", "POST", parameters.getBytes()); + assertEquals("Unexpected response code", 200, code); + + List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); + + // request authenticated user details + connection = getRestTestHelper().openManagementConnection("/service/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("/service/sasl", "POST"); + OutputStream os = connection.getOutputStream(); + os.write(parameters.getBytes()); + os.flush(); + + int code = connection.getResponseCode(); + assertEquals("Unexpected response code", 401, code); + + List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); + + // request authenticated user details + connection = getRestTestHelper().openManagementConnection("/service/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("/service/sasl", "POST"); + OutputStream os = connection.getOutputStream(); + os.write(parameters.getBytes()); + os.flush(); + + int code = connection.getResponseCode(); + assertEquals("Unexpected response code", 401, code); + + List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); + + // request authenticated user details + connection = getRestTestHelper().openManagementConnection("/service/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("/service/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", 401, code); + + // request authenticated user details + connection = getRestTestHelper().openManagementConnection("/service/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", 401, code); + + // request authenticated user details + connection = getRestTestHelper().openManagementConnection("/service/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("/service/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", 401, code); + + // request authenticated user details + connection = getRestTestHelper().openManagementConnection("/service/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", 401, code); + + // request authenticated user details + connection = getRestTestHelper().openManagementConnection("/service/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("/service/sasl", "POST"); + OutputStream os = connection.getOutputStream(); + os.write(("mechanism=" + mechanism).getBytes()); + os.flush(); + return connection; + } + + public 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("/service/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 void configureBase64MD5FilePrincipalDatabase() throws IOException + { + // generate user password entry + String passwordFileEntry; + try + { + passwordFileEntry = new Passwd().getOutput("admin", "admin"); + } + catch (NoSuchAlgorithmException e) + { + throw new RuntimeException(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("path", passwordFile.getAbsolutePath()); + newAttributes.put(AuthenticationProvider.TYPE, Base64MD5PasswordDatabaseAuthenticationManager.PROVIDER_TYPE); + getBrokerConfiguration().setObjectAttributes(AuthenticationProvider.class,TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER, newAttributes); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/StructureRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/StructureRestTest.java new file mode 100644 index 0000000000..daefc05e2a --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/StructureRestTest.java @@ -0,0 +1,129 @@ +/* + * + * 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.exchange.ExchangeDefaults; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class StructureRestTest extends QpidRestTestCase +{ + + @Override + public void setUp() throws Exception + { + super.setUp(); + getRestTestHelper().createTestQueues(); + } + + public void testGet() throws Exception + { + Map<String, Object> structure = getRestTestHelper().getJsonAsMap("/service/structure"); + assertNotNull("Structure data cannot be null", structure); + assertNode(structure, "Broker"); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> virtualhostnodes = (List<Map<String, Object>>) structure.get("virtualhostnodes"); + assertEquals("Unexpected number of virtual hosts", 3, virtualhostnodes.size()); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> ports = (List<Map<String, Object>>) structure.get("ports"); + assertEquals("Unexpected number of ports", 2, ports.size()); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> providers = (List<Map<String, Object>>) structure.get("authenticationproviders"); + assertEquals("Unexpected number of authentication providers", 2, providers.size()); + + for (String nodeName : EXPECTED_VIRTUALHOSTS) + { + Map<String, Object> node = getRestTestHelper().find("name", nodeName, virtualhostnodes); + assertNotNull("Node " + nodeName + " is not found ", node); + assertNode(node, nodeName); + } + + String hostName = "test"; + Map<String, Object> node = getRestTestHelper().find("name", hostName, virtualhostnodes); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> virtualhosts = (List<Map<String, Object>>) node.get("virtualhosts"); + + Map<String, Object> host = getRestTestHelper().find("name", hostName, virtualhosts); + @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 : RestTestHelper.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 (ExchangeDefaults.DIRECT_EXCHANGE_NAME.equalsIgnoreCase(exchangeName) || + ExchangeDefaults.DEFAULT_EXCHANGE_NAME.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 : RestTestHelper.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/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/TrustStoreRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/TrustStoreRestTest.java new file mode 100644 index 0000000000..5d2e9de3fa --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/TrustStoreRestTest.java @@ -0,0 +1,263 @@ +/* + * + * 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.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.JsonParseException; +import org.codehaus.jackson.map.JsonMappingException; + +import org.apache.qpid.server.model.AbstractConfiguredObject; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.Transport; +import org.apache.qpid.server.model.TrustStore; +import org.apache.qpid.server.security.FileTrustStore; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.test.utils.TestSSLConstants; + +public class TrustStoreRestTest extends QpidRestTestCase +{ + @Override + public void setUp() throws Exception + { + // not calling super.setUp() to avoid broker start-up until + // after any necessary configuration + } + + public void testGet() throws Exception + { + super.setUp(); + + //verify existence of the default trust store used by the systests + List<Map<String, Object>> trustStores = assertNumberOfTrustStores(1); + + Map<String, Object> truststore = trustStores.get(0); + assertTrustStoreAttributes(truststore, TestBrokerConfiguration.ENTRY_NAME_SSL_TRUSTSTORE, + System.getProperty(QPID_HOME) + "/../" + TestSSLConstants.BROKER_TRUSTSTORE, false); + } + + public void testCreate() throws Exception + { + super.setUp(); + + String name = getTestName(); + + assertNumberOfTrustStores(1); + createTrustStore(name, true); + assertNumberOfTrustStores(2); + + List<Map<String, Object>> trustStores = getRestTestHelper().getJsonAsList("truststore/" + name); + assertNotNull("details cannot be null", trustStores); + + assertTrustStoreAttributes(trustStores.get(0), name, TestSSLConstants.TRUSTSTORE, true); + } + + public void testDelete() throws Exception + { + super.setUp(); + + String name = getTestName(); + + assertNumberOfTrustStores(1); + createTrustStore(name, false); + assertNumberOfTrustStores(2); + + int responseCode = getRestTestHelper().submitRequest("truststore/" + name , "DELETE"); + assertEquals("Unexpected response code for provider deletion", 200, responseCode); + + List<Map<String, Object>> trustStore = getRestTestHelper().getJsonAsList("truststore/" + name); + assertNotNull("details should not be null", trustStore); + assertTrue("details should be empty as the truststore no longer exists", trustStore.isEmpty()); + + //check only the default systests trust store remains + List<Map<String, Object>> trustStores = assertNumberOfTrustStores(1); + Map<String, Object> truststore = trustStores.get(0); + assertTrustStoreAttributes(truststore, TestBrokerConfiguration.ENTRY_NAME_SSL_TRUSTSTORE, + System.getProperty(QPID_HOME) + "/../" + TestSSLConstants.BROKER_TRUSTSTORE, false); + } + + public void testDeleteFailsWhenTrustStoreInUse() throws Exception + { + String name = "testDeleteFailsWhenTrustStoreInUse"; + + //add a new trust store config to use + Map<String, Object> sslTrustStoreAttributes = new HashMap<String, Object>(); + sslTrustStoreAttributes.put(TrustStore.NAME, name); + sslTrustStoreAttributes.put(FileTrustStore.PATH, TestSSLConstants.TRUSTSTORE); + sslTrustStoreAttributes.put(FileTrustStore.PASSWORD, TestSSLConstants.TRUSTSTORE_PASSWORD); + getBrokerConfiguration().addObjectConfiguration(TrustStore.class,sslTrustStoreAttributes); + + //add the SSL port using it + Map<String, Object> sslPortAttributes = new HashMap<String, Object>(); + sslPortAttributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); + sslPortAttributes.put(Port.PORT, DEFAULT_SSL_PORT); + sslPortAttributes.put(Port.NAME, TestBrokerConfiguration.ENTRY_NAME_SSL_PORT); + sslPortAttributes.put(Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + sslPortAttributes.put(Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); + sslPortAttributes.put(Port.TRUST_STORES, Collections.singleton(name)); + getBrokerConfiguration().addObjectConfiguration(Port.class, sslPortAttributes); + + super.setUp(); + + //verify the truststore is there + assertNumberOfTrustStores(2); + + List<Map<String, Object>> trustStore = getRestTestHelper().getJsonAsList("truststore/" + name); + assertNotNull("details should not be null", trustStore); + assertTrustStoreAttributes(trustStore.get(0), name, TestSSLConstants.TRUSTSTORE, false); + + //try to delete it, which should fail as it is in use + int responseCode = getRestTestHelper().submitRequest("truststore/" + name , "DELETE"); + assertEquals("Unexpected response code for provider deletion", 409, responseCode); + + //check its still there + assertNumberOfTrustStores(2); + trustStore = getRestTestHelper().getJsonAsList("truststore/" + name); + assertNotNull("details should not be null", trustStore); + assertTrustStoreAttributes(trustStore.get(0), name, TestSSLConstants.TRUSTSTORE, false); + } + + public void testUpdateWithGoodPathSucceeds() throws Exception + { + super.setUp(); + + String name = getTestName(); + + assertNumberOfTrustStores(1); + createTrustStore(name, false); + assertNumberOfTrustStores(2); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(TrustStore.NAME, name); + attributes.put(FileTrustStore.PATH, TestSSLConstants.TRUSTSTORE); + + int responseCode = getRestTestHelper().submitRequest("truststore/" + name , "PUT", attributes); + assertEquals("Unexpected response code for truststore update", 200, responseCode); + + List<Map<String, Object>> trustStore = getRestTestHelper().getJsonAsList("truststore/" + name); + assertNotNull("details should not be null", trustStore); + + assertTrustStoreAttributes(trustStore.get(0), name, TestSSLConstants.TRUSTSTORE, false); + } + + public void testUpdateWithNonExistentPathFails() throws Exception + { + super.setUp(); + + String name = getTestName(); + + assertNumberOfTrustStores(1); + createTrustStore(name, false); + assertNumberOfTrustStores(2); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(TrustStore.NAME, name); + attributes.put(FileTrustStore.PATH, "does.not.exist"); + + int responseCode = getRestTestHelper().submitRequest("truststore/" + name , "PUT", attributes); + assertEquals("Unexpected response code for trust store update", 409, responseCode); + + List<Map<String, Object>> trustStore = getRestTestHelper().getJsonAsList("truststore/" + name); + assertNotNull("details should not be null", trustStore); + + //verify the details remain unchanged + assertTrustStoreAttributes(trustStore.get(0), name, TestSSLConstants.TRUSTSTORE, false); + } + + public void testUpdatePeersOnly() throws Exception + { + super.setUp(); + + String name = getTestName(); + + assertNumberOfTrustStores(1); + createTrustStore(name, false); + assertNumberOfTrustStores(2); + + //update the peersOnly attribute from false to true + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(TrustStore.NAME, name); + attributes.put(FileTrustStore.PEERS_ONLY, true); + + int responseCode = getRestTestHelper().submitRequest("truststore/" + name , "PUT", attributes); + assertEquals("Unexpected response code for trust store update", 200, responseCode); + + List<Map<String, Object>> trustStore = getRestTestHelper().getJsonAsList("truststore/" + name); + assertNotNull("details should not be null", trustStore); + + assertTrustStoreAttributes(trustStore.get(0), name, TestSSLConstants.TRUSTSTORE, true); + + //Update peersOnly to clear it (i.e go from from true to null, which will default to false) + attributes = new HashMap<String, Object>(); + attributes.put(TrustStore.NAME, name); + attributes.put(FileTrustStore.PEERS_ONLY, null); + + responseCode = getRestTestHelper().submitRequest("truststore/" + name , "PUT", attributes); + assertEquals("Unexpected response code for trust store update", 200, responseCode); + + trustStore = getRestTestHelper().getJsonAsList("truststore/" + name); + assertNotNull("details should not be null", trustStore); + + assertTrustStoreAttributes(trustStore.get(0), name, TestSSLConstants.TRUSTSTORE, false); + } + + private List<Map<String, Object>> assertNumberOfTrustStores(int numberOfTrustStores) throws IOException, + JsonParseException, JsonMappingException + { + List<Map<String, Object>> trustStores = getRestTestHelper().getJsonAsList("truststore"); + assertNotNull("trust stores should not be null", trustStores); + assertEquals("Unexpected number of trust stores", numberOfTrustStores, trustStores.size()); + + return trustStores; + } + + private void createTrustStore(String name, boolean peersOnly) throws IOException, JsonGenerationException, JsonMappingException + { + Map<String, Object> trustStoreAttributes = new HashMap<String, Object>(); + trustStoreAttributes.put(TrustStore.NAME, name); + //deliberately using the client trust store to differentiate from the one we are already for broker + trustStoreAttributes.put(FileTrustStore.PATH, TestSSLConstants.TRUSTSTORE); + trustStoreAttributes.put(FileTrustStore.PASSWORD, TestSSLConstants.TRUSTSTORE_PASSWORD); + trustStoreAttributes.put(FileTrustStore.PEERS_ONLY, peersOnly); + + int responseCode = getRestTestHelper().submitRequest("truststore/" + name, "PUT", trustStoreAttributes); + assertEquals("Unexpected response code", 201, responseCode); + } + + private void assertTrustStoreAttributes(Map<String, Object> truststore, String name, String path, boolean peersOnly) + { + assertEquals("default systests trust store is missing", + name, truststore.get(TrustStore.NAME)); + assertEquals("unexpected path to trust store", + path, truststore.get(FileTrustStore.PATH)); + assertEquals("unexpected (dummy) password of default systests trust store", + AbstractConfiguredObject.SECURED_STRING_VALUE, truststore.get(FileTrustStore.PASSWORD)); + assertEquals("unexpected type of default systests trust store", + java.security.KeyStore.getDefaultType(), truststore.get(FileTrustStore.TRUST_STORE_TYPE)); + assertEquals("unexpected peersOnly value", + peersOnly, truststore.get(FileTrustStore.PEERS_ONLY)); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/UserPreferencesRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/UserPreferencesRestTest.java new file mode 100644 index 0000000000..a0902912ce --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/UserPreferencesRestTest.java @@ -0,0 +1,150 @@ +/* + * + * 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.URLEncoder; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.qpid.server.model.PreferencesProvider; +import org.apache.qpid.server.model.User; +import org.apache.qpid.server.model.adapter.FileSystemPreferencesProvider; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.test.utils.TestFileUtils; + +public class UserPreferencesRestTest extends QpidRestTestCase +{ + private File _preferencesProviderFile; + + public void setUp() throws Exception + { + _preferencesProviderFile = TestFileUtils.createTempFile(this, ".prefs.json", + "{\"webadmin\":{\"language\": \"en\", \"saveTabs\":true}," + + " \"admin\":{\"language\": \"fr\", \"saveTabs\":false}" + "}"); + super.setUp(); + } + + public void tearDown() throws Exception + { + try + { + super.tearDown(); + } + finally + { + if (_preferencesProviderFile != null) + { + _preferencesProviderFile.delete(); + } + } + } + + @Override + protected void customizeConfiguration() throws IOException + { + super.customizeConfiguration(); + + TestBrokerConfiguration brokerConfiguration = getBrokerConfiguration(); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(PreferencesProvider.NAME, "test"); + attributes.put(PreferencesProvider.TYPE, FileSystemPreferencesProvider.PROVIDER_TYPE); + attributes.put(FileSystemPreferencesProvider.PATH, _preferencesProviderFile.getAbsolutePath()); + brokerConfiguration.addPreferencesProviderConfiguration(TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER, + attributes); + + } + + public void testGetUserPreferences() throws Exception + { + Map<String, Object> preferences = getRestTestHelper().getJsonAsMap( + "/service/userpreferences/" + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/webadmin"); + assertEquals("Unexpected number of preferences", 2, preferences.size()); + assertEquals("Unexpected language preference", "en", preferences.get("language")); + assertEquals("Unexpected saveTabs preference", true, preferences.get("saveTabs")); + } + + public void testGetUserListForAuthenticationProvider() throws Exception + { + List<Map<String, Object>> users = getRestTestHelper().getJsonAsList( + "/service/userpreferences/" + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + assertEquals("Unexpected number of users", 2, users.size()); + String[] expectedUsers = { "webadmin", "admin" }; + for (int i = 0; i < expectedUsers.length; i++) + { + Map<String, Object> user = findUser(expectedUsers[i], users); + assertNotNull(String.format("User %s is not found", expectedUsers[i]), user); + } + } + + public void testGetUserList() throws Exception + { + List<Map<String, Object>> users = getRestTestHelper().getJsonAsList("/service/userpreferences"); + assertEquals("Unexpected number of users", 2, users.size()); + String[] expectedUsers = { "webadmin", "admin" }; + for (int i = 0; i < expectedUsers.length; i++) + { + Map<String, Object> user = findUser(expectedUsers[i], users); + assertNotNull(String.format("User %s is not found", expectedUsers[i]), user); + } + } + + public void testDeleteUser() throws Exception + { + int status = getRestTestHelper().submitRequest( + "/service/userpreferences?user=" + + URLEncoder.encode(TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/webadmin", + "UTF-8"), "DELETE"); + assertEquals("Unexpected status ", 200, status); + List<Map<String, Object>> users = getRestTestHelper().getJsonAsList("/service/userpreferences"); + assertEquals("Unexpected number of users", 1, users.size()); + Map<String, Object> user = findUser("admin", users); + assertNotNull("User admin is not found", user); + assertNull("User webadmin is found", findUser("webadmin", users)); + } + + public void testDeleteMultipleUser() throws Exception + { + int status = getRestTestHelper().submitRequest("/service/userpreferences?user=" + + URLEncoder.encode(TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/webadmin", "UTF-8") + + "&user=" + URLEncoder.encode(TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/admin", "UTF-8"), + "DELETE"); + assertEquals("Unexpected status ", 200, status); + List<Map<String, Object>> users = getRestTestHelper().getJsonAsList("/service/userpreferences"); + assertEquals("Unexpected number of users", 0, users.size()); + } + + private Map<String, Object> findUser(String userName, List<Map<String, Object>> users) + { + for (Map<String, Object> map : users) + { + if (userName.equals(map.get(User.NAME))) + { + return map; + } + } + return null; + } + +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/UserRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/UserRestTest.java new file mode 100644 index 0000000000..5df8a4ed9a --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/UserRestTest.java @@ -0,0 +1,100 @@ +/* + * + * 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 + getRestTestHelper().setUsernameAndPassword("user1", "user1"); + } + + public void testGet() throws Exception + { + List<Map<String, Object>> users = getRestTestHelper().getJsonAsList("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("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("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("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("user/" + + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/" + userName); + String id = (String) userDetails.get(User.ID); + + getRestTestHelper().removeUserById(id); + + List<Map<String, Object>> users = getRestTestHelper().getJsonAsList("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/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/VirtualHostNodeRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/VirtualHostNodeRestTest.java new file mode 100644 index 0000000000..9569b90251 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/VirtualHostNodeRestTest.java @@ -0,0 +1,191 @@ +/* + * + * 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.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.VirtualHostNode; +import org.apache.qpid.server.virtualhostnode.JsonVirtualHostNode; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +/** + * + * TODO: Add test to test the mutation of the storePath. If the store path is mutated + * whilst active then the store should be deleted next time we stop or close. + */ +public class VirtualHostNodeRestTest extends QpidRestTestCase +{ + public void testGet() throws Exception + { + List<Map<String, Object>> virtualhostNodes = getRestTestHelper().getJsonAsList("virtualhostnode"); + assertNotNull("Virtualhostnodes data cannot be null", virtualhostNodes); + assertEquals("Unexpected number of hosts", EXPECTED_VIRTUALHOSTS.length, virtualhostNodes.size()); + for (String nodeName : EXPECTED_VIRTUALHOSTS) + { + Map<String, Object> node = getRestTestHelper().find("name", nodeName, virtualhostNodes); + Asserts.assertVirtualHostNode(nodeName, node); + } + } + + public void testCreateAndDeleteVirtualHostNode() throws Exception + { + String virtualhostNodeType = getTestProfileVirtualHostNodeType(); + String nodeName = "virtualhostnode-" + getTestName(); + File storePathAsFile = new File(getStoreLocation(nodeName)); + + createAndDeleteVirtualHostNode(virtualhostNodeType, nodeName, storePathAsFile); + assertFalse("Store should not exist after deletion", storePathAsFile.exists()); + } + + public void testCreateVirtualHostNodeWithDefaultStorePath() throws Exception + { + String virtualhostNodeType = getTestProfileVirtualHostNodeType(); + String nodeName = "virtualhostnode-" + getTestName(); + + createVirtualHostNode(nodeName, virtualhostNodeType); + + String restUrl = "virtualhostnode/" + nodeName; + Map<String, Object> virtualhostNode = getRestTestHelper().getJsonAsSingletonList(restUrl); + Asserts.assertVirtualHostNode(nodeName, virtualhostNode); + assertNull("Virtualhostnode should not automatically get a virtualhost child", + virtualhostNode.get("virtualhosts")); + + getRestTestHelper().submitRequest(restUrl, "DELETE", HttpServletResponse.SC_OK); + + List<Map<String, Object>> virtualHostNodes = getRestTestHelper().getJsonAsList(restUrl); + assertEquals("Host should be deleted", 0, virtualHostNodes.size()); + } + + public void testRecoverVirtualHostNodeWithDesiredStateStopped() throws Exception + { + stopBroker(); + + TestBrokerConfiguration config = getBrokerConfiguration(); + config.setObjectAttribute(VirtualHostNode.class, TEST3_VIRTUALHOST, ConfiguredObject.DESIRED_STATE, "STOPPED"); + config.setSaved(false); + + startBroker(); + + String restUrl = "virtualhostnode/" + TEST3_VIRTUALHOST; + assertActualAndDesireStates(restUrl, "STOPPED", "STOPPED"); + } + + public void testMutateState() throws Exception + { + String restUrl = "virtualhostnode/" + TEST3_VIRTUALHOST; + + assertActualAndDesireStates(restUrl, "ACTIVE", "ACTIVE"); + + mutateVirtualHostNodeDesiredState(restUrl, "STOPPED"); + assertActualAndDesireStates(restUrl, "STOPPED", "STOPPED"); + + mutateVirtualHostNodeDesiredState(restUrl, "ACTIVE"); + assertActualAndDesireStates(restUrl, "ACTIVE", "ACTIVE"); + } + + public void testMutateAttributes() throws Exception + { + String restUrl = "virtualhostnode/" + TEST3_VIRTUALHOST; + + Map<String, Object> virtualhostNode = getRestTestHelper().getJsonAsSingletonList(restUrl); + assertNull(virtualhostNode.get(VirtualHostNode.DESCRIPTION)); + + String newDescription = "My virtualhost node"; + Map<String, Object> newAttributes = new HashMap<String, Object>(); + newAttributes.put(VirtualHostNode.DESCRIPTION, newDescription); + + getRestTestHelper().submitRequest(restUrl, "PUT", newAttributes, HttpServletResponse.SC_OK); + + virtualhostNode = getRestTestHelper().getJsonAsSingletonList(restUrl); + assertEquals(newDescription, virtualhostNode.get(VirtualHostNode.DESCRIPTION)); + } + + private void createAndDeleteVirtualHostNode(final String virtualhostNodeType, + final String nodeName, + final File storePathAsFile) throws Exception + { + assertFalse("Store should not exist", storePathAsFile.exists()); + + createVirtualHostNode(nodeName, storePathAsFile.getAbsolutePath(), virtualhostNodeType); + assertTrue("Store should exist after creation of node", storePathAsFile.exists()); + + String restUrl = "virtualhostnode/" + nodeName; + Map<String, Object> virtualhostNode = getRestTestHelper().getJsonAsSingletonList(restUrl); + Asserts.assertVirtualHostNode(nodeName, virtualhostNode); + assertNull("Virtualhostnode should not automatically get a virtualhost child", + virtualhostNode.get("virtualhosts")); + + getRestTestHelper().submitRequest(restUrl, "DELETE", HttpServletResponse.SC_OK); + + List<Map<String, Object>> virtualHostNodes = getRestTestHelper().getJsonAsList(restUrl); + assertEquals("Host should be deleted", 0, virtualHostNodes.size()); + } + + private void assertActualAndDesireStates(final String restUrl, + final String expectedDesiredState, + final String expectedActualState) throws IOException + { + Map<String, Object> virtualhostNode = getRestTestHelper().getJsonAsSingletonList(restUrl); + Asserts.assertActualAndDesiredState(expectedDesiredState, expectedActualState, virtualhostNode); + } + + private void mutateVirtualHostNodeDesiredState(final String restUrl, final String newState) throws IOException + { + Map<String, Object> newAttributes = new HashMap<String, Object>(); + newAttributes.put(VirtualHostNode.DESIRED_STATE, newState); + + getRestTestHelper().submitRequest(restUrl, "PUT", newAttributes, HttpServletResponse.SC_OK); + } + + private void createVirtualHostNode(String nodeName, String configStorePath, final String storeType) throws Exception + { + Map<String, Object> nodeData = new HashMap<String, Object>(); + nodeData.put(VirtualHostNode.NAME, nodeName); + nodeData.put(VirtualHostNode.TYPE, storeType); + if (configStorePath != null) + { + nodeData.put(JsonVirtualHostNode.STORE_PATH, configStorePath); + } + + getRestTestHelper().submitRequest("virtualhostnode/" + nodeName, + "PUT", + nodeData, + HttpServletResponse.SC_CREATED); + } + + private void createVirtualHostNode(String nodeName, final String storeType) throws Exception + { + createVirtualHostNode(nodeName, null, storeType); + } + + private String getStoreLocation(String hostName) + { + return new File(TMP_FOLDER, "store-" + hostName + "-" + System.currentTimeMillis()).getAbsolutePath(); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/VirtualHostRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/VirtualHostRestTest.java new file mode 100644 index 0000000000..45cbee205d --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/VirtualHostRestTest.java @@ -0,0 +1,631 @@ +/* + * + * 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.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.Session; +import javax.servlet.http.HttpServletResponse; + +import org.apache.qpid.server.virtualhost.ProvidedStoreVirtualHostImpl; +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.LastValueQueue; +import org.apache.qpid.server.queue.PriorityQueue; +import org.apache.qpid.server.queue.SortedQueue; +import org.apache.qpid.server.virtualhost.AbstractVirtualHost; + +import org.apache.qpid.server.virtualhost.derby.DerbyVirtualHostImpl; +import org.apache.qpid.server.virtualhostnode.JsonVirtualHostNodeImpl; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +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"; + + public static final String EMPTY_VIRTUALHOSTNODE_NAME = "emptyVHN"; + + private AMQConnection _connection; + + @Override + protected void customizeConfiguration() throws IOException + { + super.customizeConfiguration(); + + TestBrokerConfiguration config = getBrokerConfiguration(); + createTestVirtualHostNode(0, EMPTY_VIRTUALHOSTNODE_NAME, false); + } + + public void testGet() throws Exception + { + List<Map<String, Object>> hosts = getRestTestHelper().getJsonAsList("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(); + Session session = _connection.createSession(true, Session.SESSION_TRANSACTED); + session.createConsumer(getTestQueue()); + + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("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( + "exchangeCount")); + assertEquals("Unexpected number of queues in statistics", 1, statistics.get("queueCount")); + assertEquals("Unexpected number of connections in statistics", 1, statistics.get("connectionCount")); + + @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)); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> queues = (List<Map<String, Object>>) hostDetails.get(VIRTUALHOST_QUEUES_ATTRIBUTE); + assertEquals("Unexpected number of queues", 1, queues.size()); + Map<String, Object> queue = getRestTestHelper().find(Queue.NAME, getTestQueueName(), queues); + Asserts.assertQueue(getTestQueueName(), "standard", queue); + assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, queue.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 testPutCreateProvidedVirtualHost() throws Exception + { + String hostName = getTestName(); + createVirtualHost(hostName, ProvidedStoreVirtualHostImpl.VIRTUAL_HOST_TYPE); + + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("virtualhost/" + EMPTY_VIRTUALHOSTNODE_NAME + "/" + hostName); + Asserts.assertVirtualHost(hostName, hostDetails); + + assertNewVirtualHost(hostDetails); + } + + public void testPutCreateVirtualHost() throws Exception + { + String hostName = getTestName(); + String vhnType = getTestProfileVirtualHostNodeType(); + if (JsonVirtualHostNodeImpl.VIRTUAL_HOST_NODE_TYPE.equals(vhnType)) + { + vhnType = DerbyVirtualHostImpl.VIRTUAL_HOST_TYPE; + } + createVirtualHost(hostName, vhnType); + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("virtualhost/" + EMPTY_VIRTUALHOSTNODE_NAME + "/" + hostName); + Asserts.assertVirtualHost(hostName, hostDetails); + + assertNewVirtualHost(hostDetails); + } + + public void testDeleteHost() throws Exception + { + getRestTestHelper().submitRequest("virtualhost/" + TEST3_VIRTUALHOST + "/" + TEST3_VIRTUALHOST, + "DELETE", + HttpServletResponse.SC_OK); + + List<Map<String, Object>> hosts = getRestTestHelper().getJsonAsList("virtualhost/" + TEST3_VIRTUALHOST); + assertEquals("Host should be deleted", 0, hosts.size()); + } + + public void testDeleteDefaultHostFails() throws Exception + { + getRestTestHelper().submitRequest("virtualhost/" + TEST1_VIRTUALHOST, "DELETE", HttpServletResponse.SC_CONFLICT); + } + + public void testMutateAttributes() throws Exception + { + String hostToUpdate = TEST3_VIRTUALHOST; + String restHostUrl = "virtualhost/" + hostToUpdate + "/" + hostToUpdate; + + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList(restHostUrl); + Asserts.assertVirtualHost(hostToUpdate, hostDetails); + + Map<String, Object> newAttributes = Collections.<String, Object>singletonMap(VirtualHost.DESCRIPTION, "This is a virtual host"); + getRestTestHelper().submitRequest(restHostUrl, "PUT", newAttributes, HttpServletResponse.SC_OK); + + Map<String, Object> rereadHostDetails = getRestTestHelper().getJsonAsSingletonList(restHostUrl); + Asserts.assertVirtualHost(hostToUpdate, rereadHostDetails); + assertEquals("This is a virtual host", rereadHostDetails.get(VirtualHost.DESCRIPTION)); + } + + public void testMutateState() throws Exception + { + String restHostUrl = "virtualhost/" + TEST1_VIRTUALHOST + "/" + TEST1_VIRTUALHOST; + + waitForAttributeChanged(restHostUrl, VirtualHost.STATE, "ACTIVE"); + assertActualAndDesireStates(restHostUrl, "ACTIVE", "ACTIVE"); + + Map<String, Object> newAttributes = Collections.<String, Object>singletonMap(VirtualHost.DESIRED_STATE, "STOPPED"); + getRestTestHelper().submitRequest(restHostUrl, "PUT", newAttributes, HttpServletResponse.SC_OK); + + waitForAttributeChanged(restHostUrl, VirtualHost.STATE, "STOPPED"); + assertActualAndDesireStates(restHostUrl, "STOPPED", "STOPPED"); + + newAttributes = Collections.<String, Object>singletonMap(VirtualHost.DESIRED_STATE, "ACTIVE"); + getRestTestHelper().submitRequest(restHostUrl, "PUT", newAttributes, HttpServletResponse.SC_OK); + + waitForAttributeChanged(restHostUrl, VirtualHost.STATE, "ACTIVE"); + + assertActualAndDesireStates(restHostUrl, "ACTIVE", "ACTIVE"); + } + + public void testMutateStateOfVirtualHostWithQueuesAndMessages() throws Exception + { + String testQueueName = getTestQueueName(); + String restHostUrl = "virtualhost/" + TEST1_VIRTUALHOST + "/" + TEST1_VIRTUALHOST; + String restQueueUrl = "queue/" + TEST1_VIRTUALHOST + "/" + TEST1_VIRTUALHOST + "/" + testQueueName; + + waitForAttributeChanged(restHostUrl, VirtualHost.STATE, "ACTIVE"); + assertActualAndDesireStates(restHostUrl, "ACTIVE", "ACTIVE"); + + Connection connection = getConnection(); + Session session = connection.createSession(true, Session.SESSION_TRANSACTED); + Destination dest = session.createQueue(testQueueName); + session.createConsumer(dest).close(); + session.createProducer(dest).send(session.createTextMessage("My test message")); + session.commit(); + connection.close(); + + assertQueueDepth(restQueueUrl, "Unexpected number of messages before stopped", 1); + + Map<String, Object> newAttributes = Collections.<String, Object>singletonMap(VirtualHost.DESIRED_STATE, "STOPPED"); + getRestTestHelper().submitRequest(restHostUrl, "PUT", newAttributes, HttpServletResponse.SC_OK); + + waitForAttributeChanged(restHostUrl, VirtualHost.STATE, "STOPPED"); + assertActualAndDesireStates(restHostUrl, "STOPPED", "STOPPED"); + + newAttributes = Collections.<String, Object>singletonMap(VirtualHost.DESIRED_STATE, "ACTIVE"); + getRestTestHelper().submitRequest(restHostUrl, "PUT", newAttributes, HttpServletResponse.SC_OK); + + waitForAttributeChanged(restHostUrl, VirtualHost.STATE, "ACTIVE"); + + assertActualAndDesireStates(restHostUrl, "ACTIVE", "ACTIVE"); + + assertQueueDepth(restQueueUrl, "Unexpected number of messages after restart", 1); + } + + public void testRecoverVirtualHostInDesiredStateStoppedWithDescription() throws Exception + { + String hostToUpdate = TEST3_VIRTUALHOST; + String restUrl = "virtualhost/" + hostToUpdate + "/" + hostToUpdate; + + assertActualAndDesireStates(restUrl, "ACTIVE", "ACTIVE"); + + Map<String, Object> newAttributes = new HashMap<>(); + newAttributes.put(VirtualHost.DESIRED_STATE, "STOPPED"); + newAttributes.put(VirtualHost.DESCRIPTION, "My description"); + + getRestTestHelper().submitRequest(restUrl, "PUT", newAttributes, HttpServletResponse.SC_OK); + + assertActualAndDesireStates(restUrl, "STOPPED", "STOPPED"); + + restartBroker(); + + Map<String, Object> rereadVirtualhost = getRestTestHelper().getJsonAsSingletonList(restUrl); + Asserts.assertActualAndDesiredState("STOPPED", "STOPPED", rereadVirtualhost); + + assertEquals("Unexpected description after restart", "My description", rereadVirtualhost.get(VirtualHost.DESCRIPTION)); + } + + public void testPutCreateQueue() throws Exception + { + String queueName = getTestQueueName(); + + createQueue(queueName + "-standard", "standard", null); + + Map<String, Object> sortedQueueAttributes = new HashMap<String, Object>(); + sortedQueueAttributes.put(SortedQueue.SORT_KEY, "sortme"); + createQueue(queueName + "-sorted", "sorted", sortedQueueAttributes); + + Map<String, Object> priorityQueueAttributes = new HashMap<String, Object>(); + priorityQueueAttributes.put(PriorityQueue.PRIORITIES, 10); + createQueue(queueName + "-priority", "priority", priorityQueueAttributes); + + Map<String, Object> lvqQueueAttributes = new HashMap<String, Object>(); + lvqQueueAttributes.put(LastValueQueue.LVQ_KEY, "LVQ"); + createQueue(queueName + "-lvq", "lvq", lvqQueueAttributes); + + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("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(SortedQueue.SORT_KEY)); + assertEquals("Unexpected lvq key attribute", "LVQ", lvqQueue.get(LastValueQueue.LVQ_KEY)); + assertEquals("Unexpected priorities key attribute", 10, priorityQueue.get(PriorityQueue.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("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("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", LastValueQueue.DEFAULT_LVQ_KEY, lvqQueue.get(LastValueQueue.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("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("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(PriorityQueue.PRIORITIES)); + } + + public void testPutCreateStandardQueueWithoutType() throws Exception + { + String queueName = getTestQueueName(); + createQueue(queueName, null, null); + + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("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("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); + + String queueUrl = "queue/test/test/" + queueName; + List<Map<String, Object>> queues = getRestTestHelper().getJsonAsList(queueUrl); + assertEquals("Queue should exist", 1, queues.size()); + + int statusCode = getRestTestHelper().submitRequest(queueUrl, "DELETE"); + assertEquals("Unexpected response code", 200, statusCode); + queues = getRestTestHelper().getJsonAsList(queueUrl); + 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("queue/test/test/" + queueName); + int statusCode = getRestTestHelper().submitRequest("queue/test/test?id=" + queueDetails.get(Queue.ID), "DELETE"); + assertEquals("Unexpected response code", 200, statusCode); + List<Map<String, Object>> queues = getRestTestHelper().getJsonAsList("queue/test/test/" + queueName); + assertEquals("Queue should be deleted", 0, queues.size()); + } + + public void testDeleteExchange() throws Exception + { + String exchangeName = getTestName(); + createExchange(exchangeName, "direct"); + + int statusCode = getRestTestHelper().submitRequest("exchange/test/test/" + exchangeName, "DELETE"); + + assertEquals("Unexpected response code", 200, statusCode); + List<Map<String, Object>> queues = getRestTestHelper().getJsonAsList("exchange/test/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("exchange/test/test/" + exchangeName); + + int statusCode = getRestTestHelper().submitRequest("exchange/test/test?id=" + echangeDetails.get(Exchange.ID), "DELETE"); + + assertEquals("Unexpected response code", 200, statusCode); + List<Map<String, Object>> queues = getRestTestHelper().getJsonAsList("exchange/test/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(SortedQueue.SORT_KEY, "sortme"); + createQueue(queueName + "-sorted", "sorted", sortedQueueAttributes); + + Map<String, Object> priorityQueueAttributes = new HashMap<String, Object>(); + priorityQueueAttributes.putAll(attributes); + priorityQueueAttributes.put(PriorityQueue.PRIORITIES, 10); + createQueue(queueName + "-priority", "priority", priorityQueueAttributes); + + Map<String, Object> lvqQueueAttributes = new HashMap<String, Object>(); + lvqQueueAttributes.putAll(attributes); + lvqQueueAttributes.put(LastValueQueue.LVQ_KEY, "LVQ"); + createQueue(queueName + "-lvq", "lvq", lvqQueueAttributes); + + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("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(SortedQueue.SORT_KEY)); + assertEquals("Unexpected lvq key attribute", "LVQ", lvqQueue.get(LastValueQueue.LVQ_KEY)); + assertEquals("Unexpected priorities key attribute", 10, priorityQueue.get(PriorityQueue.PRIORITIES)); + } + + @SuppressWarnings("unchecked") + public void testCreateQueueWithDLQEnabled() throws Exception + { + String queueName = getTestQueueName(); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AbstractVirtualHost.CREATE_DLQ_ON_CREATION, true); + + //verify the starting state + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("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 "+ queueName + " should not have already been present", getRestTestHelper().find(Queue.NAME, queueName , queues)); + assertNull("queue "+ queueName + "_DLQ 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("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 have been present", queue); + assertNotNull("queue should have been present", dlqQueue); + assertNotNull("exchange should 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 + { + Map<String, Object> queueData = new HashMap<String, Object>(); + queueData.put(Exchange.NAME, exchangeName); + queueData.put(Exchange.DURABLE, Boolean.TRUE); + queueData.put(Exchange.TYPE, exchangeType); + + int statusCode = getRestTestHelper().submitRequest("exchange/test/test/" + exchangeName, "PUT", queueData); + assertEquals("Unexpected response code", 201, statusCode); + } + + private void createQueue(String queueName, String queueType, Map<String, Object> attributes) throws Exception + { + int responseCode = tryCreateQueue(queueName, queueType, attributes); + assertEquals("Unexpected response code", 201, responseCode); + } + + private int tryCreateQueue(String queueName, String queueType, Map<String, Object> attributes) throws Exception + { + 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); + } + + return getRestTestHelper().submitRequest("queue/test/test/" + queueName, "PUT", queueData); + } + + private void createVirtualHost(final String virtualHostName, + final String virtualHostType) throws IOException + { + Map<String, Object> virtualhostData = new HashMap<>(); + virtualhostData.put(VirtualHost.NAME, virtualHostName); + virtualhostData.put(VirtualHost.TYPE, virtualHostType); + + getRestTestHelper().submitRequest("virtualhost/" + EMPTY_VIRTUALHOSTNODE_NAME + "/" + virtualHostName, + "PUT", + virtualhostData, + HttpServletResponse.SC_CREATED); + } + + 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("exchangeCount")); + assertEquals("Unexpected number of queues in statistics", 0, statistics.get("queueCount")); + assertEquals("Unexpected number of connections in statistics", 0, statistics.get("connectionCount")); + + @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)); + + assertNull("Unexpected queues", hostDetails.get(VIRTUALHOST_QUEUES_ATTRIBUTE)); + assertNull("Unexpected connections", hostDetails.get(VIRTUALHOST_CONNECTIONS_ATTRIBUTE)); + } + + private void assertActualAndDesireStates(final String restUrl, + final String expectedDesiredState, + final String expectedActualState) throws IOException + { + Map<String, Object> virtualhost = getRestTestHelper().getJsonAsSingletonList(restUrl); + Asserts.assertActualAndDesiredState(expectedDesiredState, expectedActualState, virtualhost); + } + + private void assertQueueDepth(String restQueueUrl, String message, int expectedDepth) throws IOException + { + Map<String, Object> queueDetails = getRestTestHelper().getJsonAsSingletonList(restQueueUrl); + assertNotNull(queueDetails); + Map<String, Object> statistics = (Map<String, Object>) queueDetails.get(Asserts.STATISTICS_ATTRIBUTE); + assertNotNull(statistics); + + assertEquals(message, expectedDepth, statistics.get("queueDepthMessages")); + } + +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java new file mode 100644 index 0000000000..8c4effd685 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java @@ -0,0 +1,1009 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.systest.rest.acl; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.map.JsonMappingException; +import org.apache.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.server.model.AccessControlProvider; +import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.ExternalFileBasedAuthenticationManager; +import org.apache.qpid.server.model.GroupProvider; +import org.apache.qpid.server.model.KeyStore; +import org.apache.qpid.server.model.Plugin; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.Protocol; +import org.apache.qpid.server.model.TrustStore; +import org.apache.qpid.server.model.adapter.FileBasedGroupProvider; +import org.apache.qpid.server.model.adapter.FileBasedGroupProviderImpl; +import org.apache.qpid.server.security.FileKeyStore; +import org.apache.qpid.server.security.FileTrustStore; +import org.apache.qpid.server.security.access.FileAccessControlProviderConstants; +import org.apache.qpid.server.security.acl.AbstractACLTestCase; +import org.apache.qpid.server.security.auth.manager.AnonymousAuthenticationManager; +import org.apache.qpid.server.security.auth.manager.PlainPasswordDatabaseAuthenticationManager; +import org.apache.qpid.systest.rest.QpidRestTestCase; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.test.utils.TestFileUtils; +import org.apache.qpid.test.utils.TestSSLConstants; + +public class BrokerACLTest extends QpidRestTestCase +{ + private static final String ALLOWED_USER = "user1"; + private static final String DENIED_USER = "user2"; + private String _secondaryAclFileContent = ""; + + @Override + protected void customizeConfiguration() throws IOException + { + super.customizeConfiguration(); + getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); + + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + "ACL ALLOW-LOG " + ALLOWED_USER + " CONFIGURE BROKER", + "ACL DENY-LOG " + DENIED_USER + " CONFIGURE BROKER", + "ACL DENY-LOG ALL ALL"); + + _secondaryAclFileContent = + "ACL ALLOW-LOG ALL ACCESS MANAGEMENT\n" + + "ACL ALLOW-LOG " + ALLOWED_USER + " CONFIGURE BROKER\n" + + "ACL DENY-LOG " + DENIED_USER + " CONFIGURE BROKER\n" + + "ACL DENY-LOG ALL ALL"; + + getBrokerConfiguration().setObjectAttribute(Plugin.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, + HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED, true); + } + + /* === AuthenticationProvider === */ + + public void testCreateAuthenticationProviderAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String authenticationProviderName = getTestName(); + + int responseCode = createAuthenticationProvider(authenticationProviderName); + assertEquals("Provider creation should be allowed", 201, responseCode); + + assertAuthenticationProviderExists(authenticationProviderName); + } + + public void testCreateAuthenticationProviderDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + String authenticationProviderName = getTestName(); + + int responseCode = createAuthenticationProvider(authenticationProviderName); + assertEquals("Provider creation should be denied", 403, responseCode); + + assertAuthenticationProviderDoesNotExist(authenticationProviderName); + } + + public void testDeleteAuthenticationProviderAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String providerName = getTestName(); + + int responseCode = createAuthenticationProvider(providerName); + assertEquals("Provider creation should be allowed", 201, responseCode); + + assertAuthenticationProviderExists(providerName); + + responseCode = getRestTestHelper().submitRequest("authenticationprovider/" + providerName, "DELETE"); + assertEquals("Provider deletion should be allowed", 200, responseCode); + + assertAuthenticationProviderDoesNotExist(TEST2_VIRTUALHOST); + } + + public void testDeleteAuthenticationProviderDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String providerName = getTestName(); + + int responseCode = createAuthenticationProvider(providerName); + assertEquals("Provider creation should be allowed", 201, responseCode); + + assertAuthenticationProviderExists(providerName); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + responseCode = getRestTestHelper().submitRequest("authenticationprovider/" + providerName, "DELETE"); + assertEquals("Provider deletion should be denied", 403, responseCode); + + assertAuthenticationProviderExists(providerName); + } + + public void testSetAuthenticationProviderAttributesAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String providerName = TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER; + + assertAuthenticationProviderExists(providerName); + + File file = TestFileUtils.createTempFile(this, ".users", "guest:guest\n" + ALLOWED_USER + ":" + ALLOWED_USER + "\n" + + DENIED_USER + ":" + DENIED_USER); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AuthenticationProvider.NAME, providerName); + attributes.put(AuthenticationProvider.TYPE, PlainPasswordDatabaseAuthenticationManager.PROVIDER_TYPE); + attributes.put(ExternalFileBasedAuthenticationManager.PATH, file.getAbsolutePath()); + + int responseCode = getRestTestHelper().submitRequest("authenticationprovider/" + providerName, "PUT", attributes); + assertEquals("Setting of provider attribites should be allowed", 200, responseCode); + } + + public void testSetAuthenticationProviderAttributesDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String providerName = TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER; + + Map<String, Object> providerData = getRestTestHelper().getJsonAsSingletonList("authenticationprovider/" + providerName); + + File file = TestFileUtils.createTempFile(this, ".users", "guest:guest\n" + ALLOWED_USER + ":" + ALLOWED_USER + "\n" + + DENIED_USER + ":" + DENIED_USER); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AuthenticationProvider.NAME, providerName); + attributes.put(AuthenticationProvider.TYPE, AnonymousAuthenticationManager.PROVIDER_TYPE); + attributes.put(ExternalFileBasedAuthenticationManager.PATH, file.getAbsolutePath()); + + int responseCode = getRestTestHelper().submitRequest("authenticationprovider/" + providerName, "PUT", attributes); + assertEquals("Setting of provider attribites should be allowed", 403, responseCode); + + Map<String, Object> provider = getRestTestHelper().getJsonAsSingletonList("authenticationprovider/" + providerName); + assertEquals("Unexpected PATH attribute value", + providerData.get(ExternalFileBasedAuthenticationManager.PATH), + provider.get(ExternalFileBasedAuthenticationManager.PATH)); + } + + /* === Port === */ + + public void testCreatePortAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String portName = getTestName(); + + int responseCode = createPort(portName); + assertEquals("Port creation should be allowed", 201, responseCode); + + assertPortExists(portName); + } + + public void testCreatePortDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + String portName = getTestName(); + + int responseCode = createPort(portName); + assertEquals("Port creation should be denied", 403, responseCode); + + assertPortDoesNotExist(portName); + } + + public void testDeletePortDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String portName = TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT; + assertPortExists(portName); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + int responseCode = getRestTestHelper().submitRequest("port/" + portName, "DELETE"); + assertEquals("Port deletion should be denied", 403, responseCode); + + assertPortExists(portName); + } + + public void testDeletePortAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String portName = TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT; + assertPortExists(portName); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + int responseCode = getRestTestHelper().submitRequest("port/" + portName, "DELETE"); + assertEquals("Port deletion should be allowed", 200, responseCode); + + assertPortDoesNotExist(portName); + } + + // TODO: test disabled until allowing the updating of active ports outside management mode + public void DISABLED_testSetPortAttributesAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String portName = getTestName(); + + int responseCode = createPort(portName); + assertEquals("Port creation should be allowed", 201, responseCode); + + assertPortExists(portName); + + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, portName); + attributes.put(Port.AUTHENTICATION_PROVIDER, ANONYMOUS_AUTHENTICATION_PROVIDER); + responseCode = getRestTestHelper().submitRequest("port/" + portName, "PUT", attributes); + assertEquals("Setting of port attribites should be allowed", 200, responseCode); + + Map<String, Object> port = getRestTestHelper().getJsonAsSingletonList("port/" + portName); + assertEquals("Unexpected authentication provider attribute value", ANONYMOUS_AUTHENTICATION_PROVIDER, + port.get(Port.AUTHENTICATION_PROVIDER)); + } + + public void testSetPortAttributesDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String portName = getTestName(); + + int responseCode = createPort(portName); + assertEquals("Port creation should be allowed", 201, responseCode); + + assertPortExists(portName); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, portName); + attributes.put(Port.PROTOCOLS, Arrays.asList(Protocol.AMQP_0_9)); + attributes.put(Port.AUTHENTICATION_PROVIDER, ANONYMOUS_AUTHENTICATION_PROVIDER); + responseCode = getRestTestHelper().submitRequest("port/" + portName, "PUT", attributes); + assertEquals("Setting of port attribites should be denied", 403, responseCode); + + Map<String, Object> port = getRestTestHelper().getJsonAsSingletonList("port/" + portName); + assertEquals("Unexpected authentication provider attribute value", + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER, port.get(Port.AUTHENTICATION_PROVIDER)); + } + + /* === KeyStore === */ + + public void testCreateKeyStoreAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String keyStoreName = getTestName(); + + assertKeyStoreExistence(keyStoreName, false); + + int responseCode = createKeyStore(keyStoreName, "app1"); + assertEquals("keyStore creation should be allowed", 201, responseCode); + + assertKeyStoreExistence(keyStoreName, true); + } + + public void testCreateKeyStoreDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + String keyStoreName = getTestName(); + + assertKeyStoreExistence(keyStoreName, false); + + int responseCode = createKeyStore(keyStoreName, "app1"); + assertEquals("keyStore creation should be allowed", 403, responseCode); + + assertKeyStoreExistence(keyStoreName, false); + } + + public void testDeleteKeyStoreDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String keyStoreName = getTestName(); + + assertKeyStoreExistence(keyStoreName, false); + + int responseCode = createKeyStore(keyStoreName, "app1"); + assertEquals("keyStore creation should be allowed", 201, responseCode); + + assertKeyStoreExistence(keyStoreName, true); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + responseCode = getRestTestHelper().submitRequest("keystore/" + keyStoreName, "DELETE"); + assertEquals("keystore deletion should be denied", 403, responseCode); + + assertKeyStoreExistence(keyStoreName, true); + } + + public void testDeleteKeyStoreAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String keyStoreName = getTestName(); + + assertKeyStoreExistence(keyStoreName, false); + + int responseCode = createKeyStore(keyStoreName, "app1"); + assertEquals("keyStore creation should be allowed", 201, responseCode); + + assertKeyStoreExistence(keyStoreName, true); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + responseCode = getRestTestHelper().submitRequest("keystore/" + keyStoreName, "DELETE"); + assertEquals("keystore deletion should be allowed", 200, responseCode); + + assertKeyStoreExistence(keyStoreName, false); + } + + public void testSetKeyStoreAttributesAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String keyStoreName = getTestName(); + String initialCertAlias = "app1"; + String updatedCertAlias = "app2"; + + assertKeyStoreExistence(keyStoreName, false); + + int responseCode = createKeyStore(keyStoreName, initialCertAlias); + assertEquals("keyStore creation should be allowed", 201, responseCode); + + assertKeyStoreExistence(keyStoreName, true); + Map<String, Object> keyStore = getRestTestHelper().getJsonAsSingletonList("keystore/" + keyStoreName); + assertEquals("Unexpected certificateAlias attribute value", initialCertAlias, keyStore.get(FileKeyStore.CERTIFICATE_ALIAS)); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(KeyStore.NAME, keyStoreName); + attributes.put(FileKeyStore.CERTIFICATE_ALIAS, updatedCertAlias); + responseCode = getRestTestHelper().submitRequest("keystore/" + keyStoreName, "PUT", attributes); + assertEquals("Setting of keystore attributes should be allowed", 200, responseCode); + + keyStore = getRestTestHelper().getJsonAsSingletonList("keystore/" + keyStoreName); + assertEquals("Unexpected certificateAlias attribute value", updatedCertAlias, keyStore.get(FileKeyStore.CERTIFICATE_ALIAS)); + } + + public void testSetKeyStoreAttributesDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String keyStoreName = getTestName(); + String initialCertAlias = "app1"; + String updatedCertAlias = "app2"; + + assertKeyStoreExistence(keyStoreName, false); + + int responseCode = createKeyStore(keyStoreName, initialCertAlias); + assertEquals("keyStore creation should be allowed", 201, responseCode); + + assertKeyStoreExistence(keyStoreName, true); + Map<String, Object> keyStore = getRestTestHelper().getJsonAsSingletonList("keystore/" + keyStoreName); + assertEquals("Unexpected certificateAlias attribute value", initialCertAlias, keyStore.get(FileKeyStore.CERTIFICATE_ALIAS)); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(KeyStore.NAME, keyStoreName); + attributes.put(FileKeyStore.CERTIFICATE_ALIAS, updatedCertAlias); + responseCode = getRestTestHelper().submitRequest("keystore/" + keyStoreName, "PUT", attributes); + assertEquals("Setting of keystore attributes should be denied", 403, responseCode); + + keyStore = getRestTestHelper().getJsonAsSingletonList("keystore/" + keyStoreName); + assertEquals("Unexpected certificateAlias attribute value", initialCertAlias, keyStore.get(FileKeyStore.CERTIFICATE_ALIAS)); + } + + /* === TrustStore === */ + + public void testCreateTrustStoreAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String trustStoreName = getTestName(); + + assertTrustStoreExistence(trustStoreName, false); + + int responseCode = createTrustStore(trustStoreName, false); + assertEquals("trustStore creation should be allowed", 201, responseCode); + + assertTrustStoreExistence(trustStoreName, true); + } + + public void testCreateTrustStoreDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + String trustStoreName = getTestName(); + + assertTrustStoreExistence(trustStoreName, false); + + int responseCode = createTrustStore(trustStoreName, false); + assertEquals("trustStore creation should be allowed", 403, responseCode); + + assertTrustStoreExistence(trustStoreName, false); + } + + public void testDeleteTrustStoreDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String trustStoreName = getTestName(); + + assertTrustStoreExistence(trustStoreName, false); + + int responseCode = createTrustStore(trustStoreName, false); + assertEquals("trustStore creation should be allowed", 201, responseCode); + + assertTrustStoreExistence(trustStoreName, true); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + responseCode = getRestTestHelper().submitRequest("truststore/" + trustStoreName, "DELETE"); + assertEquals("truststore deletion should be denied", 403, responseCode); + + assertTrustStoreExistence(trustStoreName, true); + } + + public void testDeleteTrustStoreAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String trustStoreName = getTestName(); + + assertTrustStoreExistence(trustStoreName, false); + + int responseCode = createTrustStore(trustStoreName, false); + assertEquals("trustStore creation should be allowed", 201, responseCode); + + assertTrustStoreExistence(trustStoreName, true); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + responseCode = getRestTestHelper().submitRequest("truststore/" + trustStoreName, "DELETE"); + assertEquals("truststore deletion should be allowed", 200, responseCode); + + assertTrustStoreExistence(trustStoreName, false); + } + + public void testSetTrustStoreAttributesAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String trustStoreName = getTestName(); + boolean initialPeersOnly = false; + boolean updatedPeersOnly = true; + + assertTrustStoreExistence(trustStoreName, false); + + int responseCode = createTrustStore(trustStoreName, initialPeersOnly); + assertEquals("trustStore creation should be allowed", 201, responseCode); + + assertTrustStoreExistence(trustStoreName, true); + Map<String, Object> trustStore = getRestTestHelper().getJsonAsSingletonList("truststore/" + trustStoreName); + assertEquals("Unexpected peersOnly attribute value", initialPeersOnly, trustStore.get(FileTrustStore.PEERS_ONLY)); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(TrustStore.NAME, trustStoreName); + attributes.put(FileTrustStore.PEERS_ONLY, updatedPeersOnly); + responseCode = getRestTestHelper().submitRequest("truststore/" + trustStoreName, "PUT", attributes); + assertEquals("Setting of truststore attributes should be allowed", 200, responseCode); + + trustStore = getRestTestHelper().getJsonAsSingletonList("truststore/" + trustStoreName); + assertEquals("Unexpected peersOnly attribute value", updatedPeersOnly, trustStore.get(FileTrustStore.PEERS_ONLY)); + } + + public void testSetTrustStoreAttributesDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String trustStoreName = getTestName(); + boolean initialPeersOnly = false; + boolean updatedPeersOnly = true; + + assertTrustStoreExistence(trustStoreName, false); + + int responseCode = createTrustStore(trustStoreName, initialPeersOnly); + assertEquals("trustStore creation should be allowed", 201, responseCode); + + assertTrustStoreExistence(trustStoreName, true); + Map<String, Object> trustStore = getRestTestHelper().getJsonAsSingletonList("truststore/" + trustStoreName); + assertEquals("Unexpected peersOnly attribute value", initialPeersOnly, trustStore.get(FileTrustStore.PEERS_ONLY)); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(TrustStore.NAME, trustStoreName); + attributes.put(FileTrustStore.PEERS_ONLY, updatedPeersOnly); + responseCode = getRestTestHelper().submitRequest("truststore/" + trustStoreName, "PUT", attributes); + assertEquals("Setting of truststore attributes should be denied", 403, responseCode); + + trustStore = getRestTestHelper().getJsonAsSingletonList("truststore/" + trustStoreName); + assertEquals("Unexpected peersOnly attribute value", initialPeersOnly, trustStore.get(FileTrustStore.PEERS_ONLY)); + } + + /* === Broker === */ + + public void testSetBrokerAttributesAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + int initialSessionCountLimit = 256; + int updatedSessionCountLimit = 299; + + Map<String, Object> brokerAttributes = getRestTestHelper().getJsonAsSingletonList("broker"); + assertEquals("Unexpected alert repeat gap", initialSessionCountLimit, + brokerAttributes.get(Broker.CONNECTION_SESSION_COUNT_LIMIT)); + + Map<String, Object> newAttributes = new HashMap<String, Object>(); + newAttributes.put(Broker.CONNECTION_SESSION_COUNT_LIMIT, updatedSessionCountLimit); + + int responseCode = getRestTestHelper().submitRequest("broker", "PUT", newAttributes); + assertEquals("Setting of port attribites should be allowed", 200, responseCode); + + brokerAttributes = getRestTestHelper().getJsonAsSingletonList("broker"); + assertEquals("Unexpected default alert repeat gap", updatedSessionCountLimit, + brokerAttributes.get(Broker.CONNECTION_SESSION_COUNT_LIMIT)); + } + + public void testSetBrokerAttributesDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + int initialSessionCountLimit = 256; + int updatedSessionCountLimit = 299; + + Map<String, Object> brokerAttributes = getRestTestHelper().getJsonAsSingletonList("broker"); + assertEquals("Unexpected alert repeat gap", initialSessionCountLimit, + brokerAttributes.get(Broker.CONNECTION_SESSION_COUNT_LIMIT)); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + Map<String, Object> newAttributes = new HashMap<String, Object>(); + newAttributes.put(Broker.CONNECTION_SESSION_COUNT_LIMIT, updatedSessionCountLimit); + + int responseCode = getRestTestHelper().submitRequest("broker", "PUT", newAttributes); + assertEquals("Setting of port attribites should be allowed", 403, responseCode); + + brokerAttributes = getRestTestHelper().getJsonAsSingletonList("broker"); + assertEquals("Unexpected default alert repeat gap", initialSessionCountLimit, + brokerAttributes.get(Broker.CONNECTION_SESSION_COUNT_LIMIT)); + } + + /* === GroupProvider === */ + + public void testCreateGroupProviderAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String groupProviderName = getTestName(); + + assertGroupProviderExistence(groupProviderName, false); + + int responseCode = createGroupProvider(groupProviderName); + assertEquals("Group provider creation should be allowed", 201, responseCode); + + assertGroupProviderExistence(groupProviderName, true); + } + + public void testCreateGroupProviderDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + String groupProviderName = getTestName(); + + assertGroupProviderExistence(groupProviderName, false); + + int responseCode = createGroupProvider(groupProviderName); + assertEquals("Group provider creation should be denied", 403, responseCode); + + assertGroupProviderExistence(groupProviderName, false); + } + + public void testDeleteGroupProviderDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String groupProviderName = getTestName(); + + assertGroupProviderExistence(groupProviderName, false); + + int responseCode = createGroupProvider(groupProviderName); + assertEquals("Group provider creation should be allowed", 201, responseCode); + + assertGroupProviderExistence(groupProviderName, true); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + responseCode = getRestTestHelper().submitRequest("groupprovider/" + groupProviderName, "DELETE"); + assertEquals("Group provider deletion should be denied", 403, responseCode); + + assertGroupProviderExistence(groupProviderName, true); + } + + public void testDeleteGroupProviderAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String groupProviderName = getTestName(); + + assertGroupProviderExistence(groupProviderName, false); + + int responseCode = createGroupProvider(groupProviderName); + assertEquals("Group provider creation should be allowed", 201, responseCode); + + assertGroupProviderExistence(groupProviderName, true); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + responseCode = getRestTestHelper().submitRequest("groupprovider/" + groupProviderName, "DELETE"); + assertEquals("Group provider deletion should be allowed", 200, responseCode); + + assertGroupProviderExistence(groupProviderName, false); + } + + public void testSetGroupProviderAttributesAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String groupProviderName = getTestName(); + + assertGroupProviderExistence(groupProviderName, false); + + int responseCode = createGroupProvider(groupProviderName); + assertEquals("Group provider creation should be allowed", 201, responseCode); + + assertGroupProviderExistence(groupProviderName, true); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, groupProviderName); + attributes.put(GroupProvider.TYPE, FileBasedGroupProviderImpl.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileBasedGroupProvider.PATH, "/path/to/file"); + responseCode = getRestTestHelper().submitRequest("groupprovider/" + groupProviderName, "PUT", attributes); + assertEquals("Setting of group provider attributes should be allowed but not supported", 409, responseCode); + } + + public void testSetGroupProviderAttributesDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String groupProviderName = getTestName(); + + assertGroupProviderExistence(groupProviderName, false); + + int responseCode = createGroupProvider(groupProviderName); + assertEquals("Group provider creation should be allowed", 201, responseCode); + + assertGroupProviderExistence(groupProviderName, true); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, groupProviderName); + attributes.put(GroupProvider.TYPE, FileBasedGroupProviderImpl.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileBasedGroupProvider.PATH, "/path/to/file"); + responseCode = getRestTestHelper().submitRequest("groupprovider/" + groupProviderName, "PUT", attributes); + assertEquals("Setting of group provider attributes should be denied", 403, responseCode); + } + + /* === AccessControlProvider === */ + + public void testCreateAccessControlProviderAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String accessControlProviderName = getTestName(); + + assertAccessControlProviderExistence(accessControlProviderName, false); + + int responseCode = createAccessControlProvider(accessControlProviderName); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + assertAccessControlProviderExistence(accessControlProviderName, true); + } + + public void testCreateAccessControlProviderDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + String accessControlProviderName = getTestName(); + + assertAccessControlProviderExistence(accessControlProviderName, false); + + int responseCode = createAccessControlProvider(accessControlProviderName); + assertEquals("Access control provider creation should be denied", 403, responseCode); + + assertAccessControlProviderExistence(accessControlProviderName, false); + } + + public void testDeleteAccessControlProviderDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String accessControlProviderName = getTestName(); + + assertAccessControlProviderExistence(accessControlProviderName, false); + + int responseCode = createAccessControlProvider(accessControlProviderName); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + assertAccessControlProviderExistence(accessControlProviderName, true); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + responseCode = getRestTestHelper().submitRequest("accesscontrolprovider/" + accessControlProviderName, "DELETE"); + assertEquals("Access control provider deletion should be denied", 403, responseCode); + + assertAccessControlProviderExistence(accessControlProviderName, true); + } + + public void testDeleteAccessControlProviderAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String accessControlProviderName = getTestName(); + + assertAccessControlProviderExistence(accessControlProviderName, false); + + int responseCode = createAccessControlProvider(accessControlProviderName); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + assertAccessControlProviderExistence(accessControlProviderName, true); + + responseCode = getRestTestHelper().submitRequest("accesscontrolprovider/" + accessControlProviderName, "DELETE"); + assertEquals("Access control provider deletion should be allowed", 200, responseCode); + + assertAccessControlProviderExistence(accessControlProviderName, false); + } + + public void testSetAccessControlProviderAttributesAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String accessControlProviderName = getTestName(); + + assertAccessControlProviderExistence(accessControlProviderName, false); + + int responseCode = createAccessControlProvider(accessControlProviderName); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + assertAccessControlProviderExistence(accessControlProviderName, true); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, accessControlProviderName); + attributes.put(GroupProvider.TYPE, FileBasedGroupProviderImpl.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileBasedGroupProvider.PATH, "/path/to/file"); + responseCode = getRestTestHelper().submitRequest("accesscontrolprovider/" + accessControlProviderName, "PUT", attributes); + assertEquals("Setting of access control provider attributes should be allowed", 200, responseCode); + } + + public void testSetAccessControlProviderAttributesDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String accessControlProviderName = getTestName(); + + assertAccessControlProviderExistence(accessControlProviderName, false); + + int responseCode = createAccessControlProvider(accessControlProviderName); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + assertAccessControlProviderExistence(accessControlProviderName, true); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, accessControlProviderName); + attributes.put(GroupProvider.TYPE, FileBasedGroupProviderImpl.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileBasedGroupProvider.PATH, "/path/to/file"); + responseCode = getRestTestHelper().submitRequest("accesscontrolprovider/" + accessControlProviderName, "PUT", attributes); + assertEquals("Setting of access control provider attributes should be denied", 403, responseCode); + } + + /* === HTTP management === */ + + public void testSetHttpManagementAttributesAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(HttpManagement.NAME, TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT); + attributes.put(HttpManagement.HTTPS_BASIC_AUTHENTICATION_ENABLED, false); + attributes.put(HttpManagement.HTTPS_SASL_AUTHENTICATION_ENABLED, false); + attributes.put(HttpManagement.HTTP_SASL_AUTHENTICATION_ENABLED, false); + attributes.put(HttpManagement.TIME_OUT, 10000); + + int responseCode = getRestTestHelper().submitRequest( + "plugin/" + TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, "PUT", attributes); + assertEquals("Setting of http management should be allowed", 200, responseCode); + + Map<String, Object> details = getRestTestHelper().getJsonAsSingletonList( + "plugin/" + TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT); + + assertEquals("Unexpected session timeout", 10000, details.get(HttpManagement.TIME_OUT)); + assertEquals("Unexpected http basic auth enabled", true, details.get(HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED)); + assertEquals("Unexpected https basic auth enabled", false, details.get(HttpManagement.HTTPS_BASIC_AUTHENTICATION_ENABLED)); + assertEquals("Unexpected http sasl auth enabled", false, details.get(HttpManagement.HTTP_SASL_AUTHENTICATION_ENABLED)); + assertEquals("Unexpected https sasl auth enabled", false, details.get(HttpManagement.HTTPS_SASL_AUTHENTICATION_ENABLED)); + } + + public void testSetHttpManagementAttributesDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(HttpManagement.NAME, TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT); + attributes.put(HttpManagement.HTTPS_BASIC_AUTHENTICATION_ENABLED, false); + attributes.put(HttpManagement.HTTPS_SASL_AUTHENTICATION_ENABLED, false); + attributes.put(HttpManagement.HTTP_SASL_AUTHENTICATION_ENABLED, false); + attributes.put(HttpManagement.TIME_OUT, 10000); + + int responseCode = getRestTestHelper().submitRequest( + "plugin/" + TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, "PUT", attributes); + assertEquals("Setting of http management should be denied", 403, responseCode); + + Map<String, Object> details = getRestTestHelper().getJsonAsSingletonList( + "plugin/" + TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT); + + assertEquals("Unexpected session timeout", HttpManagement.DEFAULT_TIMEOUT_IN_SECONDS, + details.get(HttpManagement.TIME_OUT)); + assertEquals("Unexpected http basic auth enabled", true, + details.get(HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED)); + assertEquals("Unexpected https basic auth enabled", true, + details.get(HttpManagement.HTTPS_BASIC_AUTHENTICATION_ENABLED)); + assertEquals("Unexpected http sasl auth enabled", true, + details.get(HttpManagement.HTTP_SASL_AUTHENTICATION_ENABLED)); + assertEquals("Unexpected https sasl auth enabled", true, + details.get(HttpManagement.HTTPS_SASL_AUTHENTICATION_ENABLED)); + } + + /* === Utility Methods === */ + + private int createPort(String portName) throws Exception + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, portName); + attributes.put(Port.PORT, findFreePort()); + attributes.put(Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + + return getRestTestHelper().submitRequest("port/" + portName, "PUT", attributes); + } + + private void assertPortExists(String portName) throws Exception + { + assertPortExistence(portName, true); + } + + private void assertPortDoesNotExist(String portName) throws Exception + { + assertPortExistence(portName, false); + } + + private void assertPortExistence(String portName, boolean exists) throws Exception + { + List<Map<String, Object>> hosts = getRestTestHelper().getJsonAsList("port/" + portName); + assertEquals("Unexpected result", exists, !hosts.isEmpty()); + } + + private void assertKeyStoreExistence(String keyStoreName, boolean exists) throws Exception + { + List<Map<String, Object>> keyStores = getRestTestHelper().getJsonAsList("keystore/" + keyStoreName); + assertEquals("Unexpected result", exists, !keyStores.isEmpty()); + } + + private void assertTrustStoreExistence(String trustStoreName, boolean exists) throws Exception + { + List<Map<String, Object>> trustStores = getRestTestHelper().getJsonAsList("truststore/" + trustStoreName); + assertEquals("Unexpected result", exists, !trustStores.isEmpty()); + } + + private int createAuthenticationProvider(String authenticationProviderName) throws Exception + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AuthenticationProvider.NAME, authenticationProviderName); + attributes.put(AuthenticationProvider.TYPE, AnonymousAuthenticationManager.PROVIDER_TYPE); + + return getRestTestHelper().submitRequest("authenticationprovider/" + authenticationProviderName, "PUT", attributes); + } + + private void assertAuthenticationProviderDoesNotExist(String authenticationProviderName) throws Exception + { + assertAuthenticationProviderExistence(authenticationProviderName, false); + } + + private void assertAuthenticationProviderExists(String authenticationProviderName) throws Exception + { + assertAuthenticationProviderExistence(authenticationProviderName, true); + } + + private void assertAuthenticationProviderExistence(String authenticationProviderName, boolean exists) throws Exception + { + String path = "authenticationprovider/" + authenticationProviderName; + List<Map<String, Object>> providers = getRestTestHelper().getJsonAsList(path); + assertEquals("Unexpected result", exists, !providers.isEmpty()); + } + + private int createKeyStore(String name, String certAlias) throws IOException, JsonGenerationException, JsonMappingException + { + Map<String, Object> keyStoreAttributes = new HashMap<String, Object>(); + keyStoreAttributes.put(KeyStore.NAME, name); + keyStoreAttributes.put(FileKeyStore.PATH, TestSSLConstants.KEYSTORE); + keyStoreAttributes.put(FileKeyStore.PASSWORD, TestSSLConstants.KEYSTORE_PASSWORD); + keyStoreAttributes.put(FileKeyStore.CERTIFICATE_ALIAS, certAlias); + + return getRestTestHelper().submitRequest("keystore/" + name, "PUT", keyStoreAttributes); + } + + private int createTrustStore(String name, boolean peersOnly) throws IOException, JsonGenerationException, JsonMappingException + { + Map<String, Object> trustStoreAttributes = new HashMap<String, Object>(); + trustStoreAttributes.put(TrustStore.NAME, name); + trustStoreAttributes.put(FileTrustStore.PATH, TestSSLConstants.KEYSTORE); + trustStoreAttributes.put(FileTrustStore.PASSWORD, TestSSLConstants.KEYSTORE_PASSWORD); + trustStoreAttributes.put(FileTrustStore.PEERS_ONLY, peersOnly); + + return getRestTestHelper().submitRequest("truststore/" + name, "PUT", trustStoreAttributes); + } + + private void assertGroupProviderExistence(String groupProviderName, boolean exists) throws Exception + { + String path = "groupprovider/" + groupProviderName; + List<Map<String, Object>> providers = getRestTestHelper().getJsonAsList(path); + assertEquals("Unexpected result", exists, !providers.isEmpty()); + } + + private int createGroupProvider(String groupProviderName) throws Exception + { + File file = TestFileUtils.createTempFile(this, ".groups"); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, groupProviderName); + attributes.put(GroupProvider.TYPE, FileBasedGroupProviderImpl.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileBasedGroupProvider.PATH, file.getAbsoluteFile()); + + return getRestTestHelper().submitRequest("groupprovider/" + groupProviderName, "PUT", attributes); + } + + private void assertAccessControlProviderExistence(String accessControlProviderName, boolean exists) throws Exception + { + String path = "accesscontrolprovider/" + accessControlProviderName; + List<Map<String, Object>> providers = getRestTestHelper().getJsonAsList(path); + assertEquals("Unexpected result", exists, !providers.isEmpty()); + } + + private int createAccessControlProvider(String accessControlProviderName) throws Exception + { + File file = TestFileUtils.createTempFile(this, ".acl", _secondaryAclFileContent); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AccessControlProvider.NAME, accessControlProviderName); + attributes.put(AccessControlProvider.TYPE, FileAccessControlProviderConstants.ACL_FILE_PROVIDER_TYPE); + attributes.put(FileAccessControlProviderConstants.PATH, file.getAbsoluteFile()); + + return getRestTestHelper().submitRequest("accesscontrolprovider/" + accessControlProviderName, "PUT", attributes); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/ExchangeRestACLTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/ExchangeRestACLTest.java new file mode 100644 index 0000000000..b0c66cb3af --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/ExchangeRestACLTest.java @@ -0,0 +1,244 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.systest.rest.acl; + +import org.apache.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.server.model.Binding; +import org.apache.qpid.server.model.Exchange; +import org.apache.qpid.server.model.Plugin; +import org.apache.qpid.server.model.Queue; +import org.apache.qpid.server.security.acl.AbstractACLTestCase; +import org.apache.qpid.systest.rest.QpidRestTestCase; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.map.JsonMappingException; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ExchangeRestACLTest extends QpidRestTestCase +{ + private static final String ALLOWED_USER = "user1"; + private static final String DENIED_USER = "user2"; + private String _queueName; + private String _exchangeName; + private String _exchangeUrl; + + @Override + protected void customizeConfiguration() throws IOException + { + super.customizeConfiguration(); + getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); + + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + "ACL ALLOW-LOG " + ALLOWED_USER + " CREATE QUEUE", + "ACL ALLOW-LOG " + ALLOWED_USER + " CREATE EXCHANGE", + "ACL DENY-LOG " + DENIED_USER + " CREATE EXCHANGE", + "ACL ALLOW-LOG " + ALLOWED_USER + " UPDATE EXCHANGE", + "ACL DENY-LOG " + DENIED_USER + " UPDATE EXCHANGE", + "ACL ALLOW-LOG " + ALLOWED_USER + " DELETE EXCHANGE", + "ACL DENY-LOG " + DENIED_USER + " DELETE EXCHANGE", + "ACL ALLOW-LOG " + ALLOWED_USER + " BIND EXCHANGE", + "ACL DENY-LOG " + DENIED_USER + " BIND EXCHANGE", + "ACL ALLOW-LOG " + ALLOWED_USER + " UNBIND EXCHANGE", + "ACL DENY-LOG " + DENIED_USER + " UNBIND EXCHANGE", + "ACL DENY-LOG ALL ALL"); + + getBrokerConfiguration().setObjectAttribute(Plugin.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, + HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED, true); + } + + @Override + public void setUp() throws Exception + { + super.setUp(); + _queueName = getTestQueueName(); + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + Map<String, Object> queueData = new HashMap<String, Object>(); + queueData.put(Queue.NAME, _queueName); + queueData.put(Queue.DURABLE, Boolean.TRUE); + int status = getRestTestHelper().submitRequest("queue/test/test/" + _queueName, "PUT", queueData); + assertEquals("Unexpected status", 201, status); + + _exchangeName = getTestName(); + _exchangeUrl = "exchange/test/test/" + _exchangeName; + } + + public void testCreateExchangeAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + int responseCode = createExchange(); + assertEquals("Exchange creation should be allowed", 201, responseCode); + + assertExchangeExists(); + } + + public void testCreateExchangeDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + int responseCode = createExchange(); + assertEquals("Exchange creation should be denied", 403, responseCode); + + assertExchangeDoesNotExist(); + } + + public void testDeleteExchangeAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + int responseCode = createExchange(); + assertEquals("Exchange creation should be allowed", 201, responseCode); + + assertExchangeExists(); + + + responseCode = getRestTestHelper().submitRequest(_exchangeUrl, "DELETE"); + assertEquals("Exchange deletion should be allowed", 200, responseCode); + + assertExchangeDoesNotExist(); + } + + public void testDeleteExchangeDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + int responseCode = createExchange(); + assertEquals("Exchange creation should be allowed", 201, responseCode); + + assertExchangeExists(); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + responseCode = getRestTestHelper().submitRequest(_exchangeUrl, "DELETE"); + assertEquals("Exchange deletion should be denied", 403, responseCode); + + assertExchangeExists(); + } + + public void testSetExchangeAttributesAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + int responseCode = createExchange(); + + assertExchangeExists(); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Exchange.NAME, _exchangeName); + attributes.put(Exchange.ALTERNATE_EXCHANGE, "my-alternate-exchange"); + + responseCode = getRestTestHelper().submitRequest(_exchangeUrl, "PUT", attributes); + assertEquals("Setting of exchange attribites should be allowed but it is currently unsupported", 409, responseCode); + } + + public void testSetExchangeAttributesDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + int responseCode = createExchange(); + assertExchangeExists(); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Exchange.NAME, _exchangeName); + attributes.put(Exchange.ALTERNATE_EXCHANGE, "my-alternate-exchange"); + + responseCode = getRestTestHelper().submitRequest(_exchangeUrl, "PUT", attributes); + assertEquals("Setting of exchange attribites should be allowed", 403, responseCode); + } + + public void testBindToExchangeAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String bindingName = getTestName(); + int responseCode = createBinding(bindingName); + assertEquals("Binding creation should be allowed", 201, responseCode); + + assertBindingExists(bindingName); + } + + public void testBindToExchangeDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + String bindingName = getTestName(); + int responseCode = createBinding(bindingName); + assertEquals("Binding creation should be denied", 403, responseCode); + + assertBindingDoesNotExist(bindingName); + } + + private int createExchange() throws Exception + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Exchange.NAME, _exchangeName); + attributes.put(Exchange.TYPE, "direct"); + return getRestTestHelper().submitRequest(_exchangeUrl, "PUT", attributes); + } + + private void assertExchangeDoesNotExist() throws Exception + { + assertExchangeExistence(false); + } + + private void assertExchangeExists() throws Exception + { + assertExchangeExistence(true); + } + + private void assertExchangeExistence(boolean exists) throws Exception + { + List<Map<String, Object>> exchanges = getRestTestHelper().getJsonAsList(_exchangeUrl); + assertEquals("Unexpected result", exists, !exchanges.isEmpty()); + } + + private int createBinding(String bindingName) throws IOException, JsonGenerationException, JsonMappingException + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Binding.NAME, bindingName); + attributes.put(Binding.QUEUE, _queueName); + attributes.put(Binding.EXCHANGE, "amq.direct"); + + int responseCode = getRestTestHelper().submitRequest("binding/test/test/amq.direct/" + _queueName + "/" + bindingName, "PUT", attributes); + return responseCode; + } + + private void assertBindingDoesNotExist(String bindingName) throws Exception + { + assertBindingExistence(bindingName, false); + } + + private void assertBindingExists(String bindingName) throws Exception + { + assertBindingExistence(bindingName, true); + } + + private void assertBindingExistence(String bindingName, boolean exists) throws Exception + { + List<Map<String, Object>> bindings = getRestTestHelper().getJsonAsList("binding/test/test/amq.direct/" + _queueName + "/" + bindingName); + assertEquals("Unexpected result", exists, !bindings.isEmpty()); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java new file mode 100644 index 0000000000..3ebfafb8da --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java @@ -0,0 +1,193 @@ +/* + * + * 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.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.server.model.Plugin; +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 = TestBrokerConfiguration.ENTRY_NAME_GROUP_FILE; + + 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().addGroupFileConfiguration(_groupFile.getAbsolutePath()); + + //DONT call super.setUp(), the tests will start the broker after configuring it + } + + @Override + protected void customizeConfiguration() throws IOException + { + super.customizeConfiguration(); + getBrokerConfiguration().setObjectAttribute(Plugin.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED, 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, "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("groupprovider/" + FILE_GROUP_MANAGER); + getRestTestHelper().assertNumberOfGroups(data, 3); + + getRestTestHelper().createGroup("newGroup", FILE_GROUP_MANAGER); + + data = getRestTestHelper().getJsonAsSingletonList("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("groupprovider/" + FILE_GROUP_MANAGER); + getRestTestHelper().assertNumberOfGroups(data, 4); + } + + public void testDeleteGroup() throws Exception + { + AbstractACLTestCase.writeACLFileUtil(this, "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("groupprovider/" + FILE_GROUP_MANAGER); + getRestTestHelper().assertNumberOfGroups(data, 3); + + getRestTestHelper().removeGroup(OTHER_GROUP, FILE_GROUP_MANAGER, HttpServletResponse.SC_FORBIDDEN); + + data = getRestTestHelper().getJsonAsSingletonList("groupprovider/" + FILE_GROUP_MANAGER); + getRestTestHelper().assertNumberOfGroups(data, 3); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + getRestTestHelper().removeGroup(OTHER_GROUP, FILE_GROUP_MANAGER); + + data = getRestTestHelper().getJsonAsSingletonList("groupprovider/" + FILE_GROUP_MANAGER); + getRestTestHelper().assertNumberOfGroups(data, 2); + } + + public void testUpdateGroupAddMember() throws Exception + { + AbstractACLTestCase.writeACLFileUtil(this, "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, "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("group/" + FILE_GROUP_MANAGER + "/" + groupName); + getRestTestHelper().assertNumberOfGroupMembers(group, expectedNumberOfMembers); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/LogViewerACLTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/LogViewerACLTest.java new file mode 100644 index 0000000000..1b14e3b10e --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/LogViewerACLTest.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.acl; + +import java.io.IOException; + +import org.apache.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.server.model.Plugin; +import org.apache.qpid.server.security.acl.AbstractACLTestCase; +import org.apache.qpid.systest.rest.QpidRestTestCase; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class LogViewerACLTest extends QpidRestTestCase +{ + private static final String ALLOWED_USER = "user1"; + private static final String DENIED_USER = "user2"; + + @Override + protected void customizeConfiguration() throws IOException + { + super.customizeConfiguration(); + getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); + + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + "ACL ALLOW-LOG " + ALLOWED_USER + " ACCESS_LOGS BROKER", + "ACL DENY-LOG " + DENIED_USER + " ACCESS_LOGS BROKER", + "ACL DENY-LOG ALL ALL"); + + getBrokerConfiguration().setObjectAttribute(Plugin.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, + HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED, true); + } + + public void testGetLogRecordsAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + int responseCode = getRestTestHelper().submitRequest("/service/logrecords", "GET"); + assertEquals("Access to log records should be allowed", 200, responseCode); + } + + public void testGetLogRecordsDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + int responseCode = getRestTestHelper().submitRequest("/service/logrecords", "GET"); + assertEquals("Access to log records should be denied", 403, responseCode); + } + + public void testGetLogFilesAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + int responseCode = getRestTestHelper().submitRequest("/service/logfilenames", "GET"); + assertEquals("Access to log files should be allowed", 200, responseCode); + } + + public void testGetLogFilesDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + int responseCode = getRestTestHelper().submitRequest("/service/logfilenames", "GET"); + assertEquals("Access to log files should be denied", 403, responseCode); + } + + public void testDownloadLogFileAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + int responseCode = getRestTestHelper().submitRequest("/service/logfile?l=appender%2fqpid.log", "GET"); + assertEquals("Access to log files should be allowed", 404, responseCode); + } + + public void testDownloadLogFileDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + int responseCode = getRestTestHelper().submitRequest("/service/logfile?l=appender%2fqpid.log", "GET"); + assertEquals("Access to log files should be denied", 403, responseCode); + } + +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/QueueRestACLTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/QueueRestACLTest.java new file mode 100644 index 0000000000..a123de2984 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/QueueRestACLTest.java @@ -0,0 +1,188 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.systest.rest.acl; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.server.model.Plugin; +import org.apache.qpid.server.model.Queue; +import org.apache.qpid.server.security.acl.AbstractACLTestCase; +import org.apache.qpid.systest.rest.QpidRestTestCase; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class QueueRestACLTest extends QpidRestTestCase +{ + private static final String ALLOWED_USER = "user1"; + private static final String DENIED_USER = "user2"; + private String _queueUrl; + private String _queueName; + + @Override + public void setUp() throws Exception + { + super.setUp(); + _queueName = getTestName(); + _queueUrl = "queue/test/test/" + _queueName; + } + + @Override + protected void customizeConfiguration() throws IOException + { + super.customizeConfiguration(); + getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); + + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + "ACL ALLOW-LOG " + ALLOWED_USER + " CREATE QUEUE", + "ACL DENY-LOG " + DENIED_USER + " CREATE QUEUE", + "ACL ALLOW-LOG " + ALLOWED_USER + " UPDATE QUEUE", + "ACL DENY-LOG " + DENIED_USER + " UPDATE QUEUE", + "ACL ALLOW-LOG " + ALLOWED_USER + " DELETE QUEUE", + "ACL DENY-LOG " + DENIED_USER + " DELETE QUEUE", + "ACL DENY-LOG ALL ALL"); + + getBrokerConfiguration().setObjectAttribute(Plugin.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, + HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED, true); + } + + public void testCreateQueueAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + int responseCode = createQueue(); + assertEquals("Queue creation should be allowed", 201, responseCode); + + assertQueueExists(); + } + + public void testCreateQueueDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + int responseCode = createQueue(); + assertEquals("Queue creation should be denied", 403, responseCode); + + assertQueueDoesNotExist(); + } + + public void testDeleteQueueAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + int responseCode = createQueue(); + assertEquals("Queue creation should be allowed", 201, responseCode); + + assertQueueExists(); + + responseCode = getRestTestHelper().submitRequest(_queueUrl, "DELETE"); + assertEquals("Queue deletion should be allowed", 200, responseCode); + + assertQueueDoesNotExist(); + } + + public void testDeleteQueueDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + int responseCode = createQueue(); + assertEquals("Queue creation should be allowed", 201, responseCode); + + assertQueueExists(); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + responseCode = getRestTestHelper().submitRequest(_queueUrl, "DELETE"); + assertEquals("Queue deletion should be denied", 403, responseCode); + + assertQueueExists(); + } + + + + public void testSetQueueAttributesAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + int responseCode = createQueue(); + + assertQueueExists(); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Queue.NAME, _queueName); + attributes.put(Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES, 100000); + attributes.put(Queue.QUEUE_FLOW_RESUME_SIZE_BYTES, 80000); + + responseCode = getRestTestHelper().submitRequest(_queueUrl, "PUT", attributes); + assertEquals("Setting of queue attribites should be allowed", 200, responseCode); + + Map<String, Object> queueData = getRestTestHelper().getJsonAsSingletonList(_queueUrl); + assertEquals("Unexpected " + Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES, 100000, queueData.get(Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES) ); + assertEquals("Unexpected " + Queue.QUEUE_FLOW_RESUME_SIZE_BYTES, 80000, queueData.get(Queue.QUEUE_FLOW_RESUME_SIZE_BYTES) ); + } + + public void testSetQueueAttributesDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + int responseCode = createQueue(); + assertQueueExists(); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Queue.NAME, _queueName); + attributes.put(Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES, 100000); + attributes.put(Queue.QUEUE_FLOW_RESUME_SIZE_BYTES, 80000); + + responseCode = getRestTestHelper().submitRequest(_queueUrl, "PUT", attributes); + assertEquals("Setting of queue attribites should be allowed", 403, responseCode); + + Map<String, Object> queueData = getRestTestHelper().getJsonAsSingletonList(_queueUrl); + assertEquals("Unexpected " + Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES, 0, queueData.get(Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES) ); + assertEquals("Unexpected " + Queue.QUEUE_FLOW_RESUME_SIZE_BYTES, 0, queueData.get(Queue.QUEUE_FLOW_RESUME_SIZE_BYTES) ); + } + + private int createQueue() throws Exception + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Queue.NAME, _queueName); + + return getRestTestHelper().submitRequest(_queueUrl, "PUT", attributes); + } + + private void assertQueueDoesNotExist() throws Exception + { + assertQueueExistence(false); + } + + private void assertQueueExists() throws Exception + { + assertQueueExistence(true); + } + + private void assertQueueExistence(boolean exists) throws Exception + { + List<Map<String, Object>> queues = getRestTestHelper().getJsonAsList(_queueUrl); + assertEquals("Unexpected result", exists, !queues.isEmpty()); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/UserPreferencesRestACLTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/UserPreferencesRestACLTest.java new file mode 100644 index 0000000000..b626b821c8 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/UserPreferencesRestACLTest.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.IOException; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.server.model.Plugin; +import org.apache.qpid.server.model.PreferencesProvider; +import org.apache.qpid.server.model.adapter.FileSystemPreferencesProvider; +import org.apache.qpid.server.security.acl.AbstractACLTestCase; +import org.apache.qpid.systest.rest.QpidRestTestCase; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.test.utils.TestFileUtils; + +public class UserPreferencesRestACLTest extends QpidRestTestCase +{ + + private static final String REST_USER_PREFERENCES_BASE_URL = "/service/userpreferences"; + private static final String ALLOWED_USER = "webadmin"; + private static final String DENIED_USER = "admin"; + private static final String TEST_USER_PREFERENCES_GET_URL = REST_USER_PREFERENCES_BASE_URL + "/" + + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/%s"; + + private File _preferencesProviderFile; + + public void setUp() throws Exception + { + _preferencesProviderFile = TestFileUtils.createTempFile(this, ".prefs.json", + "{\"webadmin\":{\"language\": \"en\", \"saveTabs\":true}," + " \"admin\":{\"language\": \"fr\", \"saveTabs\":false}" + + "}"); + super.setUp(); + } + + public void tearDown() throws Exception + { + try + { + super.tearDown(); + } + finally + { + if (_preferencesProviderFile != null) + { + _preferencesProviderFile.delete(); + } + } + } + + @Override + protected void customizeConfiguration() throws IOException + { + super.customizeConfiguration(); + getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); + + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_USER + + " UPDATE USER", "ACL DENY-LOG " + DENIED_USER + " UPDATE USER", "ACL DENY-LOG ALL ALL"); + + TestBrokerConfiguration brokerConfiguration = getBrokerConfiguration(); + brokerConfiguration.setObjectAttribute(Plugin.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, + HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED, true); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(PreferencesProvider.NAME, "test"); + attributes.put(PreferencesProvider.TYPE, FileSystemPreferencesProvider.PROVIDER_TYPE); + attributes.put(FileSystemPreferencesProvider.PATH, _preferencesProviderFile.getAbsolutePath()); + brokerConfiguration + .addPreferencesProviderConfiguration(TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER, attributes); + } + + public void testListUsersWithPreferencesAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + List<Map<String, Object>> users = getRestTestHelper().getJsonAsList(REST_USER_PREFERENCES_BASE_URL); + assertUsers(users); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + users = getRestTestHelper().getJsonAsList(REST_USER_PREFERENCES_BASE_URL); + assertUsers(users); + } + + public void testViewOtherUserPreferencesAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String userPreferencesUrl = String.format(TEST_USER_PREFERENCES_GET_URL, DENIED_USER); + Map<String, Object> preferences = getRestTestHelper().getJsonAsMap(userPreferencesUrl); + assertEquals("Unexpected number of preferences", 2, preferences.size()); + assertEquals("Unexpected language preference", "fr", preferences.get("language")); + assertEquals("Unexpected saveTabs preference", false, preferences.get("saveTabs")); + } + + public void testViewOtherUserPreferencesDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + int responseCode = getRestTestHelper().submitRequest( + "/service/userpreferences?user=" + + URLEncoder.encode(TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/" + ALLOWED_USER, "UTF-8"), + "DELETE"); + assertEquals("Preferences deletion should be denied", 403, responseCode); + } + + public void testDeleteOtherUserPreferencesAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String userPreferencesUrl = String.format(TEST_USER_PREFERENCES_GET_URL, DENIED_USER); + Map<String, Object> preferences = getRestTestHelper().getJsonAsMap(userPreferencesUrl); + assertEquals("Unexpected number of preferences", 2, preferences.size()); + assertEquals("Unexpected language preference", "fr", preferences.get("language")); + assertEquals("Unexpected saveTabs preference", false, preferences.get("saveTabs")); + + int responseCode = getRestTestHelper().submitRequest( + "/service/userpreferences?user=" + + URLEncoder.encode(TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/" + DENIED_USER, "UTF-8"), + "DELETE"); + assertEquals("Preferences deletion should be allowed", 200, responseCode); + + preferences = getRestTestHelper().getJsonAsMap(userPreferencesUrl); + assertEquals("Unexpected number of preferences after deletion", 0, preferences.size()); + } + + public void testDeleteOtherUserPreferencesDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String userPreferencesUrl = String.format(TEST_USER_PREFERENCES_GET_URL, ALLOWED_USER); + Map<String, Object> preferences = getRestTestHelper().getJsonAsMap(userPreferencesUrl); + assertEquals("Unexpected number of preferences", 2, preferences.size()); + assertEquals("Unexpected language preference", "en", preferences.get("language")); + assertEquals("Unexpected saveTabs preference", true, preferences.get("saveTabs")); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + int responseCode = getRestTestHelper().submitRequest( + "/service/userpreferences?user=" + + URLEncoder.encode(TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/" + ALLOWED_USER, "UTF-8"), + "DELETE"); + assertEquals("Preferences deletion should be denied", 403, responseCode); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + preferences = getRestTestHelper().getJsonAsMap(userPreferencesUrl); + assertEquals("Unexpected number of preferences after deletion", 2, preferences.size()); + } + + + private void assertUsers(List<Map<String, Object>> users) + { + assertEquals("Unexpected number of users", 2, users.size()); + Map<String, Object> webadmin = findUser("webadmin", users); + assertEquals("Unexpected name", "webadmin", webadmin.get("name")); + assertEquals("Unexpected authentication provider", TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER, webadmin.get("authenticationProvider")); + Map<String, Object> admin = findUser("admin", users); + assertEquals("Unexpected name", "admin", admin.get("name")); + assertEquals("Unexpected authentication provider", TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER, admin.get("authenticationProvider")); + } + + private Map<String, Object> findUser(String name, List<Map<String, Object>> users) + { + for (Map<String, Object> user : users) + { + if (name.equals(user.get("name"))) + { + return user; + } + } + return null; + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java new file mode 100644 index 0000000000..d80c8e14b2 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java @@ -0,0 +1,195 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.systest.rest.acl; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javax.servlet.http.HttpServletResponse; + +import org.codehaus.jackson.JsonParseException; +import org.codehaus.jackson.map.JsonMappingException; + +import org.apache.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.server.model.Plugin; +import org.apache.qpid.server.security.acl.AbstractACLTestCase; +import org.apache.qpid.systest.rest.QpidRestTestCase; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +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().addGroupFileConfiguration(_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 IOException + { + super.customizeConfiguration(); + getBrokerConfiguration().setObjectAttribute(Plugin.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED, 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, "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, "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, "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); + + int responseCode = getRestTestHelper().submitRequest("user/" + + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/", "GET", (byte[])null); + boolean passwordIsCorrect = responseCode == HttpServletResponse.SC_OK; + + assertEquals(passwordExpectedToBeCorrect, passwordIsCorrect); + } + + private void assertUserDoesNotExist(String newUser) throws JsonParseException, JsonMappingException, IOException + { + String path = "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 = "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")); + } +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/VirtualHostACLTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/VirtualHostACLTest.java new file mode 100644 index 0000000000..45123325e3 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/VirtualHostACLTest.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.qpid.systest.rest.acl; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.server.model.Plugin; +import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.model.VirtualHostNode; +import org.apache.qpid.server.security.acl.AbstractACLTestCase; +import org.apache.qpid.server.virtualhost.ProvidedStoreVirtualHostImpl; +import org.apache.qpid.server.virtualhostnode.JsonVirtualHostNode; +import org.apache.qpid.systest.rest.QpidRestTestCase; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class VirtualHostACLTest extends QpidRestTestCase +{ + private static final String VHN_WITHOUT_VH = "myVhnWithoutVh"; + + private static final String ALLOWED_USER = "user1"; + private static final String DENIED_USER = "user2"; + + @Override + protected void customizeConfiguration() throws IOException + { + super.customizeConfiguration(); + getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); + + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + "ACL ALLOW-LOG " + ALLOWED_USER + " ALL VIRTUALHOST", + "ACL DENY-LOG " + DENIED_USER + " ALL VIRTUALHOST", + "ACL DENY-LOG ALL ALL"); + + getBrokerConfiguration().setObjectAttribute(Plugin.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, + HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED, true); + + Map<String, Object> virtualHostNodeAttributes = new HashMap<>(); + virtualHostNodeAttributes.put(VirtualHostNode.NAME, VHN_WITHOUT_VH); + virtualHostNodeAttributes.put(VirtualHostNode.TYPE, getTestProfileVirtualHostNodeType()); + // TODO need better way to determine the VHN's optional attributes + virtualHostNodeAttributes.put(JsonVirtualHostNode.STORE_PATH, getStoreLocation(VHN_WITHOUT_VH)); + + getBrokerConfiguration().addObjectConfiguration(VirtualHostNode.class, virtualHostNodeAttributes); + } + + public void testCreateVirtualHostAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String hostName = getTestName(); + + int responseCode = createVirtualHost(VHN_WITHOUT_VH, hostName); + assertEquals("Virtual host creation should be allowed", HttpServletResponse.SC_CREATED, responseCode); + + assertVirtualHostExists(VHN_WITHOUT_VH, hostName); + } + + public void testCreateVirtualHostDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + String hostName = getTestName(); + + int responseCode = createVirtualHost(VHN_WITHOUT_VH, hostName); + assertEquals("Virtual host creation should be denied", HttpServletResponse.SC_FORBIDDEN, responseCode); + + assertVirtualHostDoesNotExist(VHN_WITHOUT_VH, hostName); + } + + public void testDeleteVirtualHostDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + getRestTestHelper().submitRequest("virtualhost/" + TEST2_VIRTUALHOST + "/" + TEST2_VIRTUALHOST, "DELETE", HttpServletResponse.SC_FORBIDDEN); + + assertVirtualHostExists(TEST2_VIRTUALHOST, TEST2_VIRTUALHOST); + } + + public void testUpdateVirtualHostDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + Map<String, Object> attributes = new HashMap<>(); + attributes.put(VirtualHost.NAME, TEST2_VIRTUALHOST); + attributes.put(VirtualHost.DESCRIPTION, "new description"); + + getRestTestHelper().submitRequest("virtualhost/" + TEST2_VIRTUALHOST + "/" + TEST2_VIRTUALHOST, "PUT", attributes, HttpServletResponse.SC_FORBIDDEN); + } + + /* === Utility Methods === */ + + private int createVirtualHost(final String testVirtualHostNode, String virtualHostName) throws Exception + { + Map<String, Object> data = new HashMap<>(); + data.put(VirtualHost.NAME, virtualHostName); + data.put(VirtualHost.TYPE, ProvidedStoreVirtualHostImpl.VIRTUAL_HOST_TYPE); + + return getRestTestHelper().submitRequest("virtualhost/" + testVirtualHostNode + "/" + virtualHostName, "PUT", data); + } + + private void assertVirtualHostDoesNotExist(final String virtualHostNodeName, String virtualHostName) throws Exception + { + assertVirtualHostExistence(virtualHostNodeName, virtualHostName, false); + } + + private void assertVirtualHostExists(final String virtualHostNodeName, String virtualHostName) throws Exception + { + assertVirtualHostExistence(virtualHostNodeName, virtualHostName, true); + } + + private void assertVirtualHostExistence(final String virtualHostNodeName, String virtualHostName, boolean exists) throws Exception + { + List<Map<String, Object>> hosts = getRestTestHelper().getJsonAsList("virtualhost/" + virtualHostNodeName + "/" + virtualHostName); + assertEquals("Node " + virtualHostName + (exists ? " does not exist" : " exists"), exists, !hosts.isEmpty()); + } + + private String getStoreLocation(String hostName) + { + return new File(TMP_FOLDER, "store-" + hostName + "-" + System.currentTimeMillis()).getAbsolutePath(); + } + +} diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/VirtualHostNodeACLTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/VirtualHostNodeACLTest.java new file mode 100644 index 0000000000..4809962f24 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/acl/VirtualHostNodeACLTest.java @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.systest.rest.acl; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.map.JsonMappingException; + +import org.apache.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.server.model.AccessControlProvider; +import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.ExternalFileBasedAuthenticationManager; +import org.apache.qpid.server.model.GroupProvider; +import org.apache.qpid.server.model.KeyStore; +import org.apache.qpid.server.model.Plugin; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.Protocol; +import org.apache.qpid.server.model.TrustStore; +import org.apache.qpid.server.model.VirtualHostNode; +import org.apache.qpid.server.model.adapter.FileBasedGroupProvider; +import org.apache.qpid.server.model.adapter.FileBasedGroupProviderImpl; +import org.apache.qpid.server.security.FileKeyStore; +import org.apache.qpid.server.security.FileTrustStore; +import org.apache.qpid.server.security.access.FileAccessControlProviderConstants; +import org.apache.qpid.server.security.acl.AbstractACLTestCase; +import org.apache.qpid.server.security.auth.manager.AnonymousAuthenticationManager; +import org.apache.qpid.server.security.auth.manager.PlainPasswordDatabaseAuthenticationManager; +import org.apache.qpid.server.virtualhost.memory.MemoryVirtualHost; +import org.apache.qpid.server.virtualhostnode.JsonVirtualHostNode; +import org.apache.qpid.systest.rest.QpidRestTestCase; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.test.utils.TestFileUtils; +import org.apache.qpid.test.utils.TestSSLConstants; + +public class VirtualHostNodeACLTest extends QpidRestTestCase +{ + private static final String TEST_VIRTUAL_HOST_NODE = "myTestVirtualHostNode"; + private static final String ALLOWED_USER = "user1"; + private static final String DENIED_USER = "user2"; + + @Override + protected void customizeConfiguration() throws IOException + { + super.customizeConfiguration(); + getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); + + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + "ACL ALLOW-LOG " + ALLOWED_USER + " ALL VIRTUALHOSTNODE", + "ACL DENY-LOG " + DENIED_USER + " ALL VIRTUALHOSTNODE", + "ACL DENY-LOG ALL ALL"); + + getBrokerConfiguration().setObjectAttribute(Plugin.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, + HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED, true); + + Map<String, Object> virtualHostNodeAttributes = new HashMap<>(); + virtualHostNodeAttributes.put(VirtualHostNode.NAME, TEST_VIRTUAL_HOST_NODE); + virtualHostNodeAttributes.put(VirtualHostNode.TYPE, getTestProfileVirtualHostNodeType()); + // TODO need better way to determine the VHN's optional attributes + virtualHostNodeAttributes.put(JsonVirtualHostNode.STORE_PATH, getStoreLocation(TEST_VIRTUAL_HOST_NODE)); + + + getBrokerConfiguration().addObjectConfiguration(VirtualHostNode.class, virtualHostNodeAttributes); + } + + public void testCreateVirtualHostNodeAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String hostName = getTestName(); + + int responseCode = createVirtualHostNode(hostName); + assertEquals("Virtual host node creation should be allowed", HttpServletResponse.SC_CREATED, responseCode); + + assertVirtualHostNodeExists(hostName); + } + + public void testCreateVirtualHostNodeDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + String hostName = getTestName(); + + int responseCode = createVirtualHostNode(hostName); + assertEquals("Virtual host node creation should be denied", HttpServletResponse.SC_FORBIDDEN, responseCode); + + assertVirtualHostNodeDoesNotExist(hostName); + } + + public void testDeleteVirtualHostNodeDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + getRestTestHelper().submitRequest("virtualhostnode/" + TEST_VIRTUAL_HOST_NODE, "DELETE", HttpServletResponse.SC_FORBIDDEN); + + assertVirtualHostNodeExists(TEST_VIRTUAL_HOST_NODE); + } + + /* === Utility Methods === */ + + private int createVirtualHostNode(String virtualHostNodeName) throws Exception + { + Map<String, Object> data = new HashMap<>(); + data.put(VirtualHostNode.NAME, virtualHostNodeName); + data.put(VirtualHostNode.TYPE, getTestProfileVirtualHostNodeType()); + data.put(JsonVirtualHostNode.STORE_PATH, getStoreLocation(virtualHostNodeName)); + + return getRestTestHelper().submitRequest("virtualhostnode/" + virtualHostNodeName, "PUT", data); + } + + private void assertVirtualHostNodeDoesNotExist(String name) throws Exception + { + assertVirtualHostNodeExistence(name, false); + } + + private void assertVirtualHostNodeExists(String name) throws Exception + { + assertVirtualHostNodeExistence(name, true); + } + + private void assertVirtualHostNodeExistence(String name, boolean exists) throws Exception + { + List<Map<String, Object>> hosts = getRestTestHelper().getJsonAsList("virtualhostnode/" + name); + assertEquals("Node " + name + (exists ? " does not exist" : " exists"), exists, !hosts.isEmpty()); + } + + private String getStoreLocation(String hostName) + { + return new File(TMP_FOLDER, "store-" + hostName + "-" + System.currentTimeMillis()).getAbsolutePath(); + } + +} |