This closes #175 ARTEMIS-229 address on Security Interface

This commit is contained in:
Clebert Suconic 2015-09-25 11:22:30 -04:00
commit e85bb3ca4a
3 changed files with 271 additions and 1 deletions

View File

@ -34,6 +34,7 @@ import org.apache.activemq.artemis.core.server.management.NotificationService;
import org.apache.activemq.artemis.core.settings.HierarchicalRepository; import org.apache.activemq.artemis.core.settings.HierarchicalRepository;
import org.apache.activemq.artemis.core.settings.HierarchicalRepositoryChangeListener; 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.ActiveMQSecurityManager;
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager2;
import org.apache.activemq.artemis.utils.ConcurrentHashSet; import org.apache.activemq.artemis.utils.ConcurrentHashSet;
import org.apache.activemq.artemis.utils.TypedProperties; import org.apache.activemq.artemis.utils.TypedProperties;
@ -159,7 +160,16 @@ public class SecurityStoreImpl implements SecurityStore, HierarchicalRepositoryC
return; 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) { if (notificationService != null) {
TypedProperties props = new TypedProperties(); TypedProperties props = new TypedProperties();

View File

@ -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);
}

View File

@ -33,10 +33,14 @@ import org.apache.activemq.artemis.api.core.client.ServerLocator;
import org.apache.activemq.artemis.tests.util.CreateMessage; import org.apache.activemq.artemis.tests.util.CreateMessage;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.apache.activemq.artemis.core.config.Configuration; 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.security.Role;
import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.Queue; 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.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.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManagerImpl;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
@ -1049,6 +1053,213 @@ public class SecurityTest extends ActiveMQTestBase {
} }
@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 // Check the user connection has both send and receive permissions on the queue
private void checkUserSendAndReceive(final String genericQueueName, private void checkUserSendAndReceive(final String genericQueueName,
final ClientSession connection) throws Exception { final ClientSession connection) throws Exception {