ARTEMIS-2872 support FQQN syntax for security-settings
This commit is contained in:
parent
57b8c22a62
commit
d86067a65b
|
@ -23,11 +23,22 @@ public class CompositeAddress {
|
||||||
public static String SEPARATOR = "::";
|
public static String SEPARATOR = "::";
|
||||||
|
|
||||||
public static String toFullyQualified(String address, String qName) {
|
public static String toFullyQualified(String address, String qName) {
|
||||||
return new StringBuilder().append(address).append(SEPARATOR).append(qName).toString();
|
return toFullyQualified(SimpleString.toSimpleString(address), SimpleString.toSimpleString(qName)).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SimpleString toFullyQualified(SimpleString address, SimpleString qName) {
|
public static SimpleString toFullyQualified(SimpleString address, SimpleString qName) {
|
||||||
return address.concat(SEPARATOR).concat(qName);
|
SimpleString result;
|
||||||
|
if (address == null && qName == null) {
|
||||||
|
result = null;
|
||||||
|
} else if (address != null && qName == null) {
|
||||||
|
result = address;
|
||||||
|
} else if (address == null && qName != null) {
|
||||||
|
result = qName;
|
||||||
|
} else {
|
||||||
|
result = address.concat(SEPARATOR).concat(qName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isFullyQualified(String address) {
|
public static boolean isFullyQualified(String address) {
|
||||||
|
|
|
@ -41,6 +41,7 @@ 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.ActiveMQSecurityManager2;
|
||||||
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager3;
|
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager3;
|
||||||
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager4;
|
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager4;
|
||||||
|
import org.apache.activemq.artemis.utils.CompositeAddress;
|
||||||
import org.apache.activemq.artemis.utils.collections.ConcurrentHashSet;
|
import org.apache.activemq.artemis.utils.collections.ConcurrentHashSet;
|
||||||
import org.apache.activemq.artemis.utils.collections.TypedProperties;
|
import org.apache.activemq.artemis.utils.collections.TypedProperties;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
@ -201,17 +202,28 @@ public class SecurityStoreImpl implements SecurityStore, HierarchicalRepositoryC
|
||||||
}
|
}
|
||||||
|
|
||||||
String user = session.getUsername();
|
String user = session.getUsername();
|
||||||
if (checkCached(address, user, checkType)) {
|
// bypass permission checks for management cluster user
|
||||||
// OK
|
if (managementClusterUser.equals(user) && session.getPassword().equals(managementClusterPassword)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String saddress = address.toString();
|
String saddress = address.toString();
|
||||||
|
|
||||||
Set<Role> roles = securityRepository.getMatch(saddress);
|
Set<Role> roles = securityRepository.getMatch(saddress);
|
||||||
|
|
||||||
// bypass permission checks for management cluster user
|
/*
|
||||||
if (managementClusterUser.equals(user) && session.getPassword().equals(managementClusterPassword)) {
|
* If a valid queue is passed in and there's an exact match for the FQQN then use the FQQN instead of the address
|
||||||
|
*/
|
||||||
|
boolean isFullyQualified = false;
|
||||||
|
SimpleString fqqn = null;
|
||||||
|
if (queue != null) {
|
||||||
|
fqqn = CompositeAddress.toFullyQualified(address, queue);
|
||||||
|
if (securityRepository.containsExactMatch(fqqn.toString())) {
|
||||||
|
roles = securityRepository.getMatch(fqqn.toString());
|
||||||
|
isFullyQualified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkCached(isFullyQualified ? fqqn : address, user, checkType)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,8 +269,7 @@ public class SecurityStoreImpl implements SecurityStore, HierarchicalRepositoryC
|
||||||
if (act != null) {
|
if (act != null) {
|
||||||
set = act;
|
set = act;
|
||||||
}
|
}
|
||||||
set.add(address);
|
set.add(isFullyQualified ? fqqn : address);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2195,13 +2195,8 @@ public class ActiveMQServerImpl implements ActiveMQServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
|
// make sure the user has privileges to delete this queue
|
||||||
if (queue.isDurable()) {
|
securityStore.check(address, queueName, queue.isDurable() ? CheckType.DELETE_DURABLE_QUEUE : CheckType.DELETE_NON_DURABLE_QUEUE, session);
|
||||||
// make sure the user has privileges to delete this queue
|
|
||||||
securityStore.check(address, queueName, CheckType.DELETE_DURABLE_QUEUE, session);
|
|
||||||
} else {
|
|
||||||
securityStore.check(address, queueName, CheckType.DELETE_NON_DURABLE_QUEUE, session);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This check is only valid if checkConsumerCount == true
|
// This check is only valid if checkConsumerCount == true
|
||||||
|
|
|
@ -538,18 +538,11 @@ public class ServerSessionImpl implements ServerSession, FailureListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
SimpleString address = removePrefix(binding.getAddress());
|
SimpleString address = removePrefix(binding.getAddress());
|
||||||
if (browseOnly) {
|
try {
|
||||||
try {
|
securityCheck(address, unPrefixedQueueName, browseOnly ? CheckType.BROWSE : CheckType.CONSUME, this);
|
||||||
securityCheck(address, queueName, CheckType.BROWSE, this);
|
} catch (Exception e) {
|
||||||
} catch (Exception e) {
|
// this is here for backwards compatibility with the pre-FQQN syntax from ARTEMIS-592
|
||||||
securityCheck(address.concat(".").concat(unPrefixedQueueName), queueName, CheckType.BROWSE, this);
|
securityCheck(address.concat(".").concat(unPrefixedQueueName), queueName, browseOnly ? CheckType.BROWSE : CheckType.CONSUME, this);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
securityCheck(address, queueName, CheckType.CONSUME, this);
|
|
||||||
} catch (Exception e) {
|
|
||||||
securityCheck(address.concat(".").concat(unPrefixedQueueName), queueName, CheckType.CONSUME, this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Filter filter = FilterImpl.createFilter(filterString);
|
Filter filter = FilterImpl.createFilter(filterString);
|
||||||
|
@ -723,12 +716,8 @@ public class ServerSessionImpl implements ServerSession, FailureListener {
|
||||||
.setAddress(removePrefix(queueConfiguration.getAddress()))
|
.setAddress(removePrefix(queueConfiguration.getAddress()))
|
||||||
.setName(removePrefix(queueConfiguration.getName()));
|
.setName(removePrefix(queueConfiguration.getName()));
|
||||||
|
|
||||||
if (queueConfiguration.isDurable()) {
|
// make sure the user has privileges to create this queue
|
||||||
// make sure the user has privileges to create this queue
|
securityCheck(queueConfiguration.getAddress(), queueConfiguration.getName(), queueConfiguration.isDurable() ? CheckType.CREATE_DURABLE_QUEUE : CheckType.CREATE_NON_DURABLE_QUEUE, this);
|
||||||
securityCheck(queueConfiguration.getAddress(), queueConfiguration.getName(), CheckType.CREATE_DURABLE_QUEUE, this);
|
|
||||||
} else {
|
|
||||||
securityCheck(queueConfiguration.getAddress(), queueConfiguration.getName(), CheckType.CREATE_NON_DURABLE_QUEUE, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
AddressSettings as = server.getAddressSettingsRepository().getMatch(queueConfiguration.getAddress().toString());
|
AddressSettings as = server.getAddressSettingsRepository().getMatch(queueConfiguration.getAddress().toString());
|
||||||
|
|
||||||
|
@ -1043,7 +1032,7 @@ public class ServerSessionImpl implements ServerSession, FailureListener {
|
||||||
}
|
}
|
||||||
queueConfiguration.setAddress(removePrefix(queueConfiguration.getAddress()));
|
queueConfiguration.setAddress(removePrefix(queueConfiguration.getAddress()));
|
||||||
|
|
||||||
securityCheck(queueConfiguration.getAddress(), queueConfiguration.getName(), queueConfiguration.isDurable() == null || queueConfiguration.isDurable() ? CheckType.CREATE_DURABLE_QUEUE : CheckType.CREATE_NON_DURABLE_QUEUE, this);
|
securityCheck(queueConfiguration.getAddress(), queueConfiguration.getName(), queueConfiguration.isDurable() ? CheckType.CREATE_DURABLE_QUEUE : CheckType.CREATE_NON_DURABLE_QUEUE, this);
|
||||||
|
|
||||||
server.checkQueueCreationLimit(getUsername());
|
server.checkQueueCreationLimit(getUsername());
|
||||||
|
|
||||||
|
@ -2140,10 +2129,9 @@ public class ServerSessionImpl implements ServerSession, FailureListener {
|
||||||
|
|
||||||
AddressInfo art = getAddressAndRoutingType(new AddressInfo(msg.getAddressSimpleString(), routingType));
|
AddressInfo art = getAddressAndRoutingType(new AddressInfo(msg.getAddressSimpleString(), routingType));
|
||||||
|
|
||||||
// Consumer
|
|
||||||
// check the user has write access to this address.
|
// check the user has write access to this address.
|
||||||
try {
|
try {
|
||||||
securityCheck(art.getName(), CheckType.SEND, this);
|
securityCheck(CompositeAddress.extractAddressName(art.getName()), CompositeAddress.isFullyQualified(art.getName()) ? CompositeAddress.extractQueueName(art.getName()) : null, CheckType.SEND, this);
|
||||||
} catch (ActiveMQException e) {
|
} catch (ActiveMQException e) {
|
||||||
if (!autoCommitSends && tx != null) {
|
if (!autoCommitSends && tx != null) {
|
||||||
tx.markAsRollbackOnly(e);
|
tx.markAsRollbackOnly(e);
|
||||||
|
|
|
@ -107,4 +107,6 @@ public interface HierarchicalRepository<T> {
|
||||||
void clearCache();
|
void clearCache();
|
||||||
|
|
||||||
int getCacheSize();
|
int getCacheSize();
|
||||||
|
|
||||||
|
boolean containsExactMatch(String match);
|
||||||
}
|
}
|
||||||
|
|
|
@ -244,6 +244,11 @@ public class HierarchicalObjectRepository<T> implements HierarchicalRepository<T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsExactMatch(String match) {
|
||||||
|
return exactMatches.containsKey(match);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* merge all the possible matches, if the values implement Mergeable then a full merge is done
|
* merge all the possible matches, if the values implement Mergeable then a full merge is done
|
||||||
*
|
*
|
||||||
|
|
|
@ -139,6 +139,36 @@ By not inheriting permissions, it allows you to effectively deny permissions in
|
||||||
more specific security-setting blocks by simply not specifying them. Otherwise
|
more specific security-setting blocks by simply not specifying them. Otherwise
|
||||||
it would not be possible to deny permissions in sub-groups of addresses.
|
it would not be possible to deny permissions in sub-groups of addresses.
|
||||||
|
|
||||||
|
### Fine-grained security using fully qualified queue name
|
||||||
|
|
||||||
|
In certain situations it may be necessary to configure security that is more
|
||||||
|
fine-grained that simply across an entire address. For example, consider an
|
||||||
|
address with multiple queues:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<addresses>
|
||||||
|
<address name="foo">
|
||||||
|
<anycast>
|
||||||
|
<queue name="q1" />
|
||||||
|
<queue name="q2" />
|
||||||
|
</anycast>
|
||||||
|
</address>
|
||||||
|
</addresses>
|
||||||
|
```
|
||||||
|
|
||||||
|
You may want to limit consumption from `q1` to one role and consumption from
|
||||||
|
`q2` to another role. You can do this using the fully qualified queue name (i.e.
|
||||||
|
fqqn") in the `match` of the `security-setting`, e.g.:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<security-setting match="foo::q1">
|
||||||
|
<permission type="consume" roles="q1Role"/>
|
||||||
|
</security-setting>
|
||||||
|
<security-setting match="foo::q2">
|
||||||
|
<permission type="consume" roles="q2Role"/>
|
||||||
|
</security-setting>
|
||||||
|
```
|
||||||
|
|
||||||
## Security Setting Plugin
|
## Security Setting Plugin
|
||||||
|
|
||||||
Aside from configuring sets of permissions via XML these permissions can
|
Aside from configuring sets of permissions via XML these permissions can
|
||||||
|
|
|
@ -65,6 +65,7 @@ import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager3;
|
||||||
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager4;
|
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager4;
|
||||||
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
|
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
|
||||||
import org.apache.activemq.artemis.tests.util.CreateMessage;
|
import org.apache.activemq.artemis.tests.util.CreateMessage;
|
||||||
|
import org.apache.activemq.artemis.utils.CompositeAddress;
|
||||||
import org.apache.activemq.command.ActiveMQQueue;
|
import org.apache.activemq.command.ActiveMQQueue;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -509,7 +510,17 @@ public class SecurityTest extends ActiveMQTestBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testJAASSecurityManagerAuthorizationSameAddressDifferentQueues() throws Exception {
|
// this is for backwards compatibility with the pre-FQQN syntax from ARTEMIS-592
|
||||||
|
public void testJAASSecurityManagerAuthorizationSameAddressDifferentQueuesDotSyntax() throws Exception {
|
||||||
|
internalJAASSecurityManagerAuthorizationSameAddressDifferentQueues(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testJAASSecurityManagerAuthorizationSameAddressDifferentQueuesFqqnSyntax() throws Exception {
|
||||||
|
internalJAASSecurityManagerAuthorizationSameAddressDifferentQueues(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void internalJAASSecurityManagerAuthorizationSameAddressDifferentQueues(boolean fqqnSyntax) throws Exception {
|
||||||
final SimpleString ADDRESS = new SimpleString("address");
|
final SimpleString ADDRESS = new SimpleString("address");
|
||||||
final SimpleString QUEUE_A = new SimpleString("a");
|
final SimpleString QUEUE_A = new SimpleString("a");
|
||||||
final SimpleString QUEUE_B = new SimpleString("b");
|
final SimpleString QUEUE_B = new SimpleString("b");
|
||||||
|
@ -518,10 +529,18 @@ public class SecurityTest extends ActiveMQTestBase {
|
||||||
ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig().setSecurityEnabled(true), ManagementFactory.getPlatformMBeanServer(), securityManager, false));
|
ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig().setSecurityEnabled(true), ManagementFactory.getPlatformMBeanServer(), securityManager, false));
|
||||||
Set<Role> aRoles = new HashSet<>();
|
Set<Role> aRoles = new HashSet<>();
|
||||||
aRoles.add(new Role(QUEUE_A.toString(), false, true, false, false, false, false, false, false, false, false));
|
aRoles.add(new Role(QUEUE_A.toString(), false, true, false, false, false, false, false, false, false, false));
|
||||||
server.getConfiguration().putSecurityRoles(ADDRESS.concat(".").concat(QUEUE_A).toString(), aRoles);
|
if (fqqnSyntax) {
|
||||||
|
server.getConfiguration().putSecurityRoles(CompositeAddress.toFullyQualified(ADDRESS, QUEUE_A).toString(), aRoles);
|
||||||
|
} else {
|
||||||
|
server.getConfiguration().putSecurityRoles(ADDRESS.concat(".").concat(QUEUE_A).toString(), aRoles);
|
||||||
|
}
|
||||||
Set<Role> bRoles = new HashSet<>();
|
Set<Role> bRoles = new HashSet<>();
|
||||||
bRoles.add(new Role(QUEUE_B.toString(), false, true, false, false, false, false, false, false, false, false));
|
bRoles.add(new Role(QUEUE_B.toString(), false, true, false, false, false, false, false, false, false, false));
|
||||||
server.getConfiguration().putSecurityRoles(ADDRESS.concat(".").concat(QUEUE_B).toString(), bRoles);
|
if (fqqnSyntax) {
|
||||||
|
server.getConfiguration().putSecurityRoles(CompositeAddress.toFullyQualified(ADDRESS, QUEUE_B).toString(), bRoles);
|
||||||
|
} else {
|
||||||
|
server.getConfiguration().putSecurityRoles(ADDRESS.concat(".").concat(QUEUE_B).toString(), bRoles);
|
||||||
|
}
|
||||||
server.start();
|
server.start();
|
||||||
server.addAddressInfo(new AddressInfo(ADDRESS, RoutingType.ANYCAST));
|
server.addAddressInfo(new AddressInfo(ADDRESS, RoutingType.ANYCAST));
|
||||||
server.createQueue(new QueueConfiguration(QUEUE_A).setAddress(ADDRESS).setRoutingType(RoutingType.ANYCAST));
|
server.createQueue(new QueueConfiguration(QUEUE_A).setAddress(ADDRESS).setRoutingType(RoutingType.ANYCAST));
|
||||||
|
|
Loading…
Reference in New Issue