Skip to content

Commit

Permalink
This closes #175 ARTEMIS-229 address on Security Interface
Browse files Browse the repository at this point in the history
  • Loading branch information
clebertsuconic committed Sep 25, 2015
2 parents 4cdcc61 + 6890f12 commit e85bb3c
Show file tree
Hide file tree
Showing 3 changed files with 271 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.apache.activemq.artemis.core.settings.HierarchicalRepository;
import org.apache.activemq.artemis.core.settings.HierarchicalRepositoryChangeListener;
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager;
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager2;
import org.apache.activemq.artemis.utils.ConcurrentHashSet;
import org.apache.activemq.artemis.utils.TypedProperties;

Expand Down Expand Up @@ -159,7 +160,16 @@ public void check(final SimpleString address,
return;
}

if (!securityManager.validateUserAndRole(user, session.getPassword(), roles, checkType)) {
final boolean validated;
if (securityManager instanceof ActiveMQSecurityManager2) {
final ActiveMQSecurityManager2 securityManager2 = (ActiveMQSecurityManager2) securityManager;
validated = securityManager2.validateUserAndRole(user, session.getPassword(), roles, checkType, saddress);
}
else {
validated = securityManager.validateUserAndRole(user, session.getPassword(), roles, checkType);
}

if (!validated) {
if (notificationService != null) {
TypedProperties props = new TypedProperties();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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.activemq.artemis.spi.core.security;

import java.util.Set;

import org.apache.activemq.artemis.core.security.CheckType;
import org.apache.activemq.artemis.core.security.Role;

/**
* Used to validate whether a user is authorized to connect to the
* server and perform certain functions on certain destinations.
*
* This is an evolution of {@link ActiveMQSecurityManager} that adds
* the ability to perform authorization taking the destination address
* into account.
*/
public interface ActiveMQSecurityManager2 extends ActiveMQSecurityManager {

/**
* Determine whether the given user is valid and whether they have
* the correct role for the given destination address.
*
* This method is called instead of
* {@link ActiveMQSecurityManager.validateUserAndRole}.
*
* @param user the user
* @param password the user's password
* @param roles the user's roles
* @param checkType which permission to validate
* @param address the address for which to perform authorization
* @return true if the user is valid and they have the correct roles for the given destination address
*/
boolean validateUserAndRole(String user, String password, Set<Role> roles, CheckType checkType, String address);
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,14 @@
import org.apache.activemq.artemis.tests.util.CreateMessage;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.security.CheckType;
import org.apache.activemq.artemis.core.security.Role;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.Queue;
import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl;
import org.apache.activemq.artemis.core.settings.HierarchicalRepository;
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager;
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager2;
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManagerImpl;
import org.junit.Assert;
import org.junit.Before;
Expand Down Expand Up @@ -1049,6 +1053,213 @@ public void _testComplexRoles2() throws Exception {

}

@Test
public void testCustomSecurityManager() throws Exception {
final Configuration configuration = createDefaultInVMConfig().setSecurityEnabled(true);
final ActiveMQSecurityManager customSecurityManager = new ActiveMQSecurityManager() {
public boolean validateUser(final String username, final String password) {
return (username.equals("foo") || username.equals("bar") || username.equals("all")) &&
password.equals("frobnicate");
}
public boolean validateUserAndRole(
final String username,
final String password,
final Set<Role> requiredRoles,
final CheckType checkType) {

if ((username.equals("foo") || username.equals("bar") || username.equals("all")) &&
password.equals("frobnicate")) {

if (username.equals("all")) {
return true;
}
else if (username.equals("foo")) {
return checkType == CheckType.CONSUME || checkType == CheckType.CREATE_NON_DURABLE_QUEUE;
}
else if (username.equals("bar")) {
return checkType == CheckType.SEND || checkType == CheckType.CREATE_NON_DURABLE_QUEUE;
}
else {
return false;
}
}
else {
return false;
}
}
};
final ActiveMQServer server = addServer(new ActiveMQServerImpl(configuration, customSecurityManager));
server.start();

final ServerLocator locator = createInVMNonHALocator();
locator.setBlockOnNonDurableSend(true).setBlockOnDurableSend(true);
final ClientSessionFactory factory = createSessionFactory(locator);
ClientSession adminSession = factory.createSession("all", "frobnicate", false, true, true, false, -1);

final String queueName = "test.queue";
adminSession.createQueue(queueName, queueName, false);

// Wrong user name
try {
factory.createSession("baz", "frobnicate", false, true, true, false, -1);
Assert.fail("should throw exception");
}
catch (ActiveMQSecurityException se) {
//ok
}
catch (ActiveMQException e) {
fail("Invalid Exception type:" + e.getType());
}

// Wrong password
try {
factory.createSession("foo", "xxx", false, true, true, false, -1);
Assert.fail("should throw exception");
}
catch (ActiveMQSecurityException se) {
//ok
}
catch (ActiveMQException e) {
fail("Invalid Exception type:" + e.getType());
}

// Correct user and password, allowed to send but not receive
{
final ClientSession session = factory.createSession("foo", "frobnicate", false, true, true, false, -1);
checkUserReceiveNoSend(queueName, session, adminSession);
}

// Correct user and password, allowed to receive but not send
{
final ClientSession session = factory.createSession("bar", "frobnicate", false, true, true, false, -1);
checkUserSendNoReceive(queueName, session);
}

}

@Test
public void testCustomSecurityManager2() throws Exception {
final Configuration configuration = createDefaultInVMConfig().setSecurityEnabled(true);
final ActiveMQSecurityManager customSecurityManager = new ActiveMQSecurityManager2() {
public boolean validateUser(final String username, final String password) {
return (username.equals("foo") || username.equals("bar") || username.equals("all")) &&
password.equals("frobnicate");
}
public boolean validateUserAndRole(
final String username,
final String password,
final Set<Role> requiredRoles,
final CheckType checkType) {

fail("Unexpected call to overridden method");
return false;
}

public boolean validateUserAndRole(
final String username,
final String password,
final Set<Role> requiredRoles,
final CheckType checkType,
final String address) {

if ((username.equals("foo") || username.equals("bar") || username.equals("all")) &&
password.equals("frobnicate")) {

if (username.equals("all")) {
return true;
}
else if (username.equals("foo")) {
return address.equals("test.queue") && checkType == CheckType.CONSUME;
}
else if (username.equals("bar")) {
return address.equals("test.queue") && checkType == CheckType.SEND;
}
else {
return false;
}
}
else {
return false;
}
}
};
final ActiveMQServer server = addServer(new ActiveMQServerImpl(configuration, customSecurityManager));
server.start();

final ServerLocator locator = createInVMNonHALocator();
locator.setBlockOnNonDurableSend(true).setBlockOnDurableSend(true);
final ClientSessionFactory factory = createSessionFactory(locator);
ClientSession adminSession = factory.createSession("all", "frobnicate", false, true, true, false, -1);

final String queueName = "test.queue";
adminSession.createQueue(queueName, queueName, false);

final String otherQueueName = "other.queue";
adminSession.createQueue(otherQueueName, otherQueueName, false);

// Wrong user name
try {
factory.createSession("baz", "frobnicate", false, true, true, false, -1);
Assert.fail("should throw exception");
}
catch (ActiveMQSecurityException se) {
//ok
}
catch (ActiveMQException e) {
fail("Invalid Exception type:" + e.getType());
}

// Wrong password
try {
factory.createSession("foo", "xxx", false, true, true, false, -1);
Assert.fail("should throw exception");
}
catch (ActiveMQSecurityException se) {
//ok
}
catch (ActiveMQException e) {
fail("Invalid Exception type:" + e.getType());
}

// Correct user and password, wrong queue for sending
try {
final ClientSession session = factory.createSession("foo", "frobnicate", false, true, true, false, -1);
checkUserReceiveNoSend(otherQueueName, session, adminSession);
Assert.fail("should throw exception");
}
catch (ActiveMQSecurityException se) {
//ok
}
catch (ActiveMQException e) {
fail("Invalid Exception type:" + e.getType());
}

// Correct user and password, wrong queue for receiving
try {
final ClientSession session = factory.createSession("foo", "frobnicate", false, true, true, false, -1);
checkUserReceiveNoSend(otherQueueName, session, adminSession);
Assert.fail("should throw exception");
}
catch (ActiveMQSecurityException se) {
//ok
}
catch (ActiveMQException e) {
fail("Invalid Exception type:" + e.getType());
}

// Correct user and password, allowed to send but not receive
{
final ClientSession session = factory.createSession("foo", "frobnicate", false, true, true, false, -1);
checkUserReceiveNoSend(queueName, session, adminSession);
}

// Correct user and password, allowed to receive but not send
{
final ClientSession session = factory.createSession("bar", "frobnicate", false, true, true, false, -1);
checkUserSendNoReceive(queueName, session);
}
}

// Check the user connection has both send and receive permissions on the queue
private void checkUserSendAndReceive(final String genericQueueName,
final ClientSession connection) throws Exception {
Expand Down

0 comments on commit e85bb3c

Please sign in to comment.