ARTEMIS-2889 better support for JMS topics with legacy LDAP plugin

This commit is contained in:
Justin Bertram 2020-08-28 21:41:53 -05:00 committed by Clebert Suconic
parent 400623afcb
commit 9a90248f49
4 changed files with 101 additions and 11 deletions

View File

@ -66,6 +66,7 @@ public class LegacyLDAPSecuritySettingPlugin implements SecuritySettingPlugin {
public static final String WRITE_PERMISSION_VALUE = "writePermissionValue";
public static final String ENABLE_LISTENER = "enableListener";
public static final String MAP_ADMIN_TO_MANAGE = "mapAdminToManage";
public static final String ALLOW_QUEUE_ADMIN_ON_READ = "allowQueueAdminOnRead";
private String initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
private String connectionURL = "ldap://localhost:1024";
@ -81,6 +82,7 @@ public class LegacyLDAPSecuritySettingPlugin implements SecuritySettingPlugin {
private String writePermissionValue = "write";
private boolean enableListener = true;
private boolean mapAdminToManage = false;
private boolean allowQueueAdminOnRead = false;
private DirContext context;
private EventDirContext eventContext;
@ -104,6 +106,7 @@ public class LegacyLDAPSecuritySettingPlugin implements SecuritySettingPlugin {
writePermissionValue = getOption(options, WRITE_PERMISSION_VALUE, writePermissionValue);
enableListener = getOption(options, ENABLE_LISTENER, Boolean.TRUE.toString()).equalsIgnoreCase(Boolean.TRUE.toString());
mapAdminToManage = getOption(options, MAP_ADMIN_TO_MANAGE, Boolean.FALSE.toString()).equalsIgnoreCase(Boolean.TRUE.toString());
allowQueueAdminOnRead = getOption(options, ALLOW_QUEUE_ADMIN_ON_READ, Boolean.FALSE.toString()).equalsIgnoreCase(Boolean.TRUE.toString());
}
return this;
@ -244,6 +247,15 @@ public class LegacyLDAPSecuritySettingPlugin implements SecuritySettingPlugin {
return this;
}
public boolean isAllowQueueAdminOnRead() {
return allowQueueAdminOnRead;
}
public LegacyLDAPSecuritySettingPlugin setAllowQueueAdminOnRead(boolean allowQueueAdminOnRead) {
this.allowQueueAdminOnRead = allowQueueAdminOnRead;
return this;
}
protected boolean isContextAlive() {
boolean alive = false;
if (context != null) {
@ -405,18 +417,20 @@ public class LegacyLDAPSecuritySettingPlugin implements SecuritySettingPlugin {
if (logger.isDebugEnabled()) {
logMessage.append("\n\tRole name: ").append(roleName);
}
boolean write = permissionType.equalsIgnoreCase(writePermissionValue);
boolean read = permissionType.equalsIgnoreCase(readPermissionValue);
boolean admin = permissionType.equalsIgnoreCase(adminPermissionValue);
Role role = new Role(roleName,
permissionType.equalsIgnoreCase(writePermissionValue), // send
permissionType.equalsIgnoreCase(readPermissionValue), // consume
permissionType.equalsIgnoreCase(adminPermissionValue), // createDurableQueue
permissionType.equalsIgnoreCase(adminPermissionValue), // deleteDurableQueue
permissionType.equalsIgnoreCase(adminPermissionValue), // createNonDurableQueue
permissionType.equalsIgnoreCase(adminPermissionValue), // deleteNonDurableQueue
mapAdminToManage ? permissionType.equalsIgnoreCase(adminPermissionValue) : false, // manage - map to admin based on configuration
permissionType.equalsIgnoreCase(readPermissionValue), // browse
permissionType.equalsIgnoreCase(adminPermissionValue), // createAddress
permissionType.equalsIgnoreCase(adminPermissionValue) // deleteAddress
);
write, // send
read, // consume
(allowQueueAdminOnRead && read) || admin, // createDurableQueue
(allowQueueAdminOnRead && read) || admin, // deleteDurableQueue
(allowQueueAdminOnRead && read) || admin, // createNonDurableQueue
admin, // deleteNonDurableQueue
mapAdminToManage ? admin : false, // manage - map to admin based on configuration
read, // browse
admin, // createAddress
admin); // deleteAddress
roles.add(role);
}

View File

@ -300,6 +300,12 @@ and Security Layer (SASL) authentication is currently not supported.
`manage` permission. See details of the mapping semantics below. The default
value is `false`.
- `allowQueueAdminOnRead`. Whether or not to map the legacy `read` permission to
the `createDurableQueue`, `createNonDurableQueue`, and `deleteDurableQueue`
permissions so that JMS clients can create durable and non-durable subscriptions
without needing the `admin` permission. This was allowed in ActiveMQ 5.x. The
default value is `false`.
The name of the queue or topic defined in LDAP will serve as the "match" for
the security-setting, the permission value will be mapped from the ActiveMQ 5.x
type to the Artemis type, and the role will be mapped as-is.

View File

@ -16,6 +16,11 @@
*/
package org.apache.activemq.artemis.tests.integration.security;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.Topic;
import javax.naming.Context;
import javax.naming.NameClassPair;
import javax.naming.NamingEnumeration;
@ -44,7 +49,9 @@ import org.apache.activemq.artemis.core.remoting.impl.invm.InVMConnectorFactory;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.ActiveMQServers;
import org.apache.activemq.artemis.api.core.RoutingType;
import org.apache.activemq.artemis.core.server.impl.AddressInfo;
import org.apache.activemq.artemis.core.server.impl.LegacyLDAPSecuritySettingPlugin;
import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.apache.directory.server.annotations.CreateLdapServer;
@ -320,4 +327,38 @@ public class LegacyLDAPSecuritySettingPluginTest extends AbstractLdapTestUnit {
session.close();
cf.close();
}
@Test
public void testJmsTopicSubscriberReadPermissionOnly() throws Exception {
internalJmsTopicSubscriberReadPermissionOnly(false);
}
@Test
public void testJmsTopicDurableSubscriberReadPermissionOnly() throws Exception {
internalJmsTopicSubscriberReadPermissionOnly(true);
}
private void internalJmsTopicSubscriberReadPermissionOnly(boolean durable) throws Exception {
((LegacyLDAPSecuritySettingPlugin)server.getConfiguration().getSecuritySettingPlugins().get(0)).setAllowQueueAdminOnRead(true);
server.start();
// The address needs to exist already otherwise the "admin" permission is required to create it
server.addAddressInfo(new AddressInfo(SimpleString.toSimpleString("topic1"), RoutingType.MULTICAST));
ConnectionFactory cf = new ActiveMQConnectionFactory("vm://0");
try (Connection connection = cf.createConnection("third", "secret")) {
Session session = connection.createSession();
Topic topic = session.createTopic("topic1");
if (durable) {
MessageConsumer consumer = session.createSharedDurableConsumer(topic, "foo");
consumer.close();
session.unsubscribe("foo");
} else {
session.createConsumer(topic);
}
} catch (Exception e) {
e.printStackTrace();
Assert.fail("should not throw exception here");
}
}
}

View File

@ -39,6 +39,13 @@ objectClass: account
objectClass: simpleSecurityObject
objectClass: top
dn: uid=third,ou=system
uid: third
userPassword: secret
objectClass: account
objectClass: simpleSecurityObject
objectClass: top
dn: cn=role1,ou=system
cn: role1
member: uid=first,ou=system
@ -51,6 +58,12 @@ member: uid=second,ou=system
objectClass: groupOfNames
objectClass: top
dn: cn=role3,ou=system
cn: role3
member: uid=third,ou=system
objectClass: groupOfNames
objectClass: top
dn: ou=destinations,o=ActiveMQ,ou=system
objectclass: organizationalUnit
objectclass: top
@ -61,6 +74,11 @@ objectclass: organizationalUnit
objectclass: top
ou: queues
dn: ou=topics,ou=destinations,o=ActiveMQ,ou=system
objectclass: organizationalUnit
objectclass: top
ou: topics
dn: cn=queue1,ou=queues,ou=destinations,o=ActiveMQ,ou=system
objectclass: applicationProcess
objectclass: top
@ -71,6 +89,11 @@ objectclass: applicationProcess
objectclass: top
cn: queue2
dn: cn=topic1,ou=topics,ou=destinations,o=ActiveMQ,ou=system
objectclass: applicationProcess
objectclass: top
cn: topic1
dn: cn=activemq.management,ou=queues,ou=destinations,o=ActiveMQ,ou=system
objectclass: applicationProcess
objectclass: top
@ -112,6 +135,12 @@ objectclass: top
cn: admin
uniquemember: cn=role1
dn: cn=read,cn=topic1,ou=topics,ou=destinations,o=ActiveMQ,ou=system
objectclass: groupOfUniqueNames
objectclass: top
cn: read
uniquemember: cn=role3
## group with member identified just by DN from SASL external tls certificate subject DN
dn: cn=widgets,ou=system
cn: widgets