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 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) {
|
||||
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) {
|
||||
|
|
|
@ -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.ActiveMQSecurityManager3;
|
||||
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.TypedProperties;
|
||||
import org.jboss.logging.Logger;
|
||||
|
@ -201,17 +202,28 @@ public class SecurityStoreImpl implements SecurityStore, HierarchicalRepositoryC
|
|||
}
|
||||
|
||||
String user = session.getUsername();
|
||||
if (checkCached(address, user, checkType)) {
|
||||
// OK
|
||||
// bypass permission checks for management cluster user
|
||||
if (managementClusterUser.equals(user) && session.getPassword().equals(managementClusterPassword)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String saddress = address.toString();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -257,8 +269,7 @@ public class SecurityStoreImpl implements SecurityStore, HierarchicalRepositoryC
|
|||
if (act != null) {
|
||||
set = act;
|
||||
}
|
||||
set.add(address);
|
||||
|
||||
set.add(isFullyQualified ? fqqn : address);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2195,13 +2195,8 @@ public class ActiveMQServerImpl implements ActiveMQServer {
|
|||
}
|
||||
|
||||
if (session != null) {
|
||||
|
||||
if (queue.isDurable()) {
|
||||
// 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);
|
||||
}
|
||||
// make sure the user has privileges to delete this queue
|
||||
securityStore.check(address, queueName, queue.isDurable() ? CheckType.DELETE_DURABLE_QUEUE : CheckType.DELETE_NON_DURABLE_QUEUE, session);
|
||||
}
|
||||
|
||||
// This check is only valid if checkConsumerCount == true
|
||||
|
|
|
@ -538,18 +538,11 @@ public class ServerSessionImpl implements ServerSession, FailureListener {
|
|||
}
|
||||
|
||||
SimpleString address = removePrefix(binding.getAddress());
|
||||
if (browseOnly) {
|
||||
try {
|
||||
securityCheck(address, queueName, CheckType.BROWSE, this);
|
||||
} catch (Exception e) {
|
||||
securityCheck(address.concat(".").concat(unPrefixedQueueName), queueName, CheckType.BROWSE, this);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
securityCheck(address, queueName, CheckType.CONSUME, this);
|
||||
} catch (Exception e) {
|
||||
securityCheck(address.concat(".").concat(unPrefixedQueueName), queueName, CheckType.CONSUME, this);
|
||||
}
|
||||
try {
|
||||
securityCheck(address, unPrefixedQueueName, browseOnly ? CheckType.BROWSE : CheckType.CONSUME, this);
|
||||
} catch (Exception e) {
|
||||
// this is here for backwards compatibility with the pre-FQQN syntax from ARTEMIS-592
|
||||
securityCheck(address.concat(".").concat(unPrefixedQueueName), queueName, browseOnly ? CheckType.BROWSE : CheckType.CONSUME, this);
|
||||
}
|
||||
|
||||
Filter filter = FilterImpl.createFilter(filterString);
|
||||
|
@ -723,12 +716,8 @@ public class ServerSessionImpl implements ServerSession, FailureListener {
|
|||
.setAddress(removePrefix(queueConfiguration.getAddress()))
|
||||
.setName(removePrefix(queueConfiguration.getName()));
|
||||
|
||||
if (queueConfiguration.isDurable()) {
|
||||
// make sure the user has privileges to create this queue
|
||||
securityCheck(queueConfiguration.getAddress(), queueConfiguration.getName(), CheckType.CREATE_DURABLE_QUEUE, this);
|
||||
} else {
|
||||
securityCheck(queueConfiguration.getAddress(), queueConfiguration.getName(), CheckType.CREATE_NON_DURABLE_QUEUE, this);
|
||||
}
|
||||
// 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);
|
||||
|
||||
AddressSettings as = server.getAddressSettingsRepository().getMatch(queueConfiguration.getAddress().toString());
|
||||
|
||||
|
@ -1043,7 +1032,7 @@ public class ServerSessionImpl implements ServerSession, FailureListener {
|
|||
}
|
||||
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());
|
||||
|
||||
|
@ -2140,10 +2129,9 @@ public class ServerSessionImpl implements ServerSession, FailureListener {
|
|||
|
||||
AddressInfo art = getAddressAndRoutingType(new AddressInfo(msg.getAddressSimpleString(), routingType));
|
||||
|
||||
// Consumer
|
||||
// check the user has write access to this address.
|
||||
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) {
|
||||
if (!autoCommitSends && tx != null) {
|
||||
tx.markAsRollbackOnly(e);
|
||||
|
|
|
@ -107,4 +107,6 @@ public interface HierarchicalRepository<T> {
|
|||
void clearCache();
|
||||
|
||||
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
|
||||
*
|
||||
|
|
|
@ -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
|
||||
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
|
||||
|
||||
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.tests.util.ActiveMQTestBase;
|
||||
import org.apache.activemq.artemis.tests.util.CreateMessage;
|
||||
import org.apache.activemq.artemis.utils.CompositeAddress;
|
||||
import org.apache.activemq.command.ActiveMQQueue;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
|
@ -509,7 +510,17 @@ public class SecurityTest extends ActiveMQTestBase {
|
|||
}
|
||||
|
||||
@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 QUEUE_A = new SimpleString("a");
|
||||
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));
|
||||
Set<Role> aRoles = new HashSet<>();
|
||||
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<>();
|
||||
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.addAddressInfo(new AddressInfo(ADDRESS, RoutingType.ANYCAST));
|
||||
server.createQueue(new QueueConfiguration(QUEUE_A).setAddress(ADDRESS).setRoutingType(RoutingType.ANYCAST));
|
||||
|
|
Loading…
Reference in New Issue