// Copyright (c) 2007-Present Pivotal Software, Inc. All rights reserved.
//
// This software, the RabbitMQ Java client library, is triple-licensed under the
// Mozilla Public License 1.1 ("MPL"), the GNU General Public License version 2
// ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see
// LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL,
// please see LICENSE-APACHE2.
//
// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
// either express or implied. See the LICENSE file for specific language governing
// rights and limitations of this software.
//
// If you have any questions regarding licensing, please contact us at
// info@rabbitmq.com.
package com.rabbitmq.client.test.server;
import com.rabbitmq.client.*;
import com.rabbitmq.client.impl.AMQChannel;
import com.rabbitmq.client.impl.recovery.AutorecoveringChannel;
import com.rabbitmq.client.test.BrokerTestCase;
import com.rabbitmq.client.test.TestUtils;
import com.rabbitmq.tools.Host;
import org.junit.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import static org.junit.Assert.*;
public class Permissions extends BrokerTestCase
{
private Channel adminCh;
public Permissions()
{
ConnectionFactory factory = TestUtils.connectionFactory();
factory.setUsername("test");
factory.setPassword("test");
factory.setVirtualHost("/test");
connectionFactory = factory;
}
public void setUp()
throws IOException, TimeoutException {
deleteRestrictedAccount();
addRestrictedAccount();
super.setUp();
}
public void tearDown()
throws IOException, TimeoutException {
super.tearDown();
deleteRestrictedAccount();
}
protected void addRestrictedAccount()
throws IOException
{
Host.rabbitmqctl("add_user test test");
Host.rabbitmqctl("add_user testadmin test");
Host.rabbitmqctl("add_vhost /test");
Host.rabbitmqctl("set_permissions -p /test test configure write read");
Host.rabbitmqctl("set_permissions -p /test testadmin \".*\" \".*\" \".*\"");
}
protected void deleteRestrictedAccount()
throws IOException
{
Host.rabbitmqctlIgnoreErrors("clear_permissions -p /test testadmin");
Host.rabbitmqctlIgnoreErrors("clear_permissions -p /test test");
Host.rabbitmqctlIgnoreErrors("delete_vhost /test");
Host.rabbitmqctlIgnoreErrors("delete_user testadmin");
Host.rabbitmqctlIgnoreErrors("delete_user test");
}
protected void createResources()
throws IOException, TimeoutException {
ConnectionFactory factory = TestUtils.connectionFactory();
factory.setUsername("testadmin");
factory.setPassword("test");
factory.setVirtualHost("/test");
Connection connection = factory.newConnection();
adminCh = connection.createChannel();
withNames(new WithName() {
public void with(String name) throws IOException {
adminCh.exchangeDeclare(name, "direct");
adminCh.queueDeclare(name, false, false, false, null);
}});
}
protected void releaseResources()
throws IOException
{
withNames(new WithName() {
public void with(String name) throws IOException {
adminCh.queueDelete(name);
adminCh.exchangeDelete(name);
}});
adminCh.getConnection().abort();
}
protected void withNames(WithName action)
throws IOException
{
action.with("configure");
action.with("write");
action.with("read");
action.with("none");
}
@Test public void auth() throws TimeoutException {
ConnectionFactory unAuthFactory = TestUtils.connectionFactory();
unAuthFactory.setUsername("test");
unAuthFactory.setPassword("tset");
try {
unAuthFactory.newConnection();
fail("Exception expected if password is wrong");
} catch (IOException e) {
assertTrue(e instanceof AuthenticationFailureException);
String msg = e.getMessage();
assertTrue("Exception message should contain 'auth'",
msg.toLowerCase().contains("auth"));
}
}
@Test public void exchangeConfiguration()
throws IOException
{
runConfigureTest(new WithName() {
public void with(String name) throws IOException {
channel.exchangeDeclare(name, "direct");
}});
runConfigureTest(new WithName() {
public void with(String name) throws IOException {
channel.exchangeDelete(name);
}});
}
@Test public void queueConfiguration()
throws IOException
{
runConfigureTest(new WithName() {
public void with(String name) throws IOException {
channel.queueDeclare(name, false, false, false, null);
}});
runConfigureTest(new WithName() {
public void with(String name) throws IOException {
channel.queueDelete(name);
}});
}
@Test public void passiveDeclaration() throws IOException {
runTest(true, true, true, true, new WithName() {
public void with(String name) throws IOException {
channel.exchangeDeclarePassive(name);
}});
runTest(true, true, true, true, new WithName() {
public void with(String name) throws IOException {
channel.queueDeclarePassive(name);
}});
}
@Test public void binding()
throws IOException
{
runTest(false, true, false, false, new WithName() {
public void with(String name) throws IOException {
channel.queueBind(name, "read", "");
}});
runTest(false, false, true, false, new WithName() {
public void with(String name) throws IOException {
channel.queueBind("write", name, "");
}});
}
@Test public void publish()
throws IOException
{
runTest(false, true, false, false, new WithName() {
public void with(String name) throws IOException {
channel.basicPublish(name, "", null, "foo".getBytes());
//followed by a dummy synchronous command in order
//to catch any errors
channel.basicQos(0);
}});
}
@Test public void get()
throws IOException
{
runTest(false, false, true, false, new WithName() {
public void with(String name) throws IOException {
channel.basicGet(name, true);
}});
}
@Test public void consume()
throws IOException
{
runTest(false, false, true, false, new WithName() {
public void with(String name) throws IOException {
channel.basicConsume(name, new QueueingConsumer(channel));
}});
}
@Test public void purge()
throws IOException
{
runTest(false, false, true, false, new WithName() {
public void with(String name) throws IOException {
AMQChannel channelDelegate = (AMQChannel) ((AutorecoveringChannel)channel).getDelegate();
channelDelegate.exnWrappingRpc(new AMQP.Queue.Purge.Builder()
.queue(name)
.build());
}});
}
@Test public void altExchConfiguration()
throws IOException
{
runTest(false, false, false, false,
createAltExchConfigTest("configure-me"));
runTest(false, false, false, false,
createAltExchConfigTest("configure-and-write-me"));
runTest(false, true, false, false,
createAltExchConfigTest("configure-and-read-me"));
}
@Test public void dLXConfiguration()
throws IOException
{
runTest(false, false, false, false,
createDLXConfigTest("configure-me"));
runTest(false, false, false, false,
createDLXConfigTest("configure-and-write-me"));
runTest(false, true, false, false,
createDLXConfigTest("configure-and-read-me"));
}
@Test public void noAccess()
throws IOException, InterruptedException
{
Host.rabbitmqctl("set_permissions -p /test test \"\" \"\" \"\"");
Thread.sleep(2000);
assertAccessRefused(new WithName() {
public void with(String _e) throws IOException {
channel.queueDeclare();
}
}
);
assertAccessRefused(new WithName() {
public void with(String _e) throws IOException {
channel.queueDeclare("justaqueue", false, false, true, null);
}
}
);
assertAccessRefused(new WithName() {
public void with(String _e) throws IOException {
channel.queueDelete("configure");
}
}
);
assertAccessRefused(new WithName() {
public void with(String _e) throws IOException {
channel.queueBind("write", "write", "write");
}
}
);
assertAccessRefused(new WithName() {
public void with(String _e) throws IOException {
channel.queuePurge("read");
}
}
);
assertAccessRefused(new WithName() {
public void with(String _e) throws IOException {
channel.exchangeDeclare("justanexchange", "direct");
}
}
);
assertAccessRefused(new WithName() {
public void with(String _e) throws IOException {
channel.exchangeDeclare("configure", "direct");
}
}
);
assertAccessRefused(new WithName() {
public void with(String _e) throws IOException {
channel.basicPublish("write", "", null, "foo".getBytes());
channel.basicQos(0);
}
}
);
assertAccessRefused(new WithName() {
public void with(String _e) throws IOException {
channel.basicGet("read", false);
}
}
);
assertAccessRefused(new WithName() {
public void with(String _e) throws IOException {
channel.basicConsume("read", null);
}
}
);
}
protected WithName createAltExchConfigTest(final String exchange)
throws IOException
{
return new WithName() {
public void with(String ae) throws IOException {
Map<String, Object> args = new HashMap<String, Object>();
args.put("alternate-exchange", ae);
channel.exchangeDeclare(exchange, "direct", false, false, args);
channel.exchangeDelete(exchange);
}};
}
protected WithName createDLXConfigTest(final String queue)
throws IOException
{
return new WithName() {
public void with(String dlx) throws IOException {
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-dead-letter-exchange", dlx);
channel.queueDeclare(queue, false, false, false, args);
channel.queueDelete(queue);
}};
}
protected void runConfigureTest(WithName test)
throws IOException
{
runTest(true, "configure-me", test);
runTest(false, "write-me", test);
runTest(false, "read-me", test);
runTest(false, "none", test);
}
protected void runTest(boolean expC, boolean expW, boolean expR, boolean expN,
WithName test)
throws IOException
{
runTest(expC, "configure", test);
runTest(expW, "write", test);
runTest(expR, "read", test);
runTest(expN, "none", test);
}
protected void assertAccessRefused(WithName test) throws IOException {
runTest(false, "", test);
}
protected void runTest(boolean exp, String name, WithName test)
throws IOException
{
String msg = "'" + name + "' -> " + exp;
try {
test.with(name);
assertTrue(msg, exp);
} catch (IOException e) {
assertFalse(msg, exp);
checkShutdownSignal(AMQP.ACCESS_REFUSED, e);
openChannel();
} catch (AlreadyClosedException e) {
assertFalse(msg, exp);
checkShutdownSignal(AMQP.ACCESS_REFUSED, e);
openChannel();
}
}
private interface WithName {
public void with(String name) throws IOException;
}
}