ARTEMIS-4010 LegacyLDAPSecuritySettingPlugin missing data

In commit a9a85f98db I removed the code
which modified existing matches. However, I forgot that the matches read
from LDAP are often duplicated so instead of always adding a new match
this commit ensures that the *right* match is modified rather than a
potentially more generic wildcard match (which was the original
problem).
This commit is contained in:
Justin Bertram 2022-09-20 22:11:45 -05:00
parent 0113e38695
commit e47b7992ca
No known key found for this signature in database
GPG Key ID: F41830B875BB8633
8 changed files with 356 additions and 76 deletions

View File

@ -317,4 +317,17 @@ public class Role implements Serializable {
result = 31 * result + (browse ? 1 : 0);
return result;
}
public void merge(Role other) {
send = send || other.send;
consume = consume || other.consume;
createAddress = createAddress || other.createAddress;
deleteAddress = deleteAddress || other.deleteAddress;
createDurableQueue = createDurableQueue || other.createDurableQueue;
deleteDurableQueue = deleteDurableQueue || other.deleteDurableQueue;
createNonDurableQueue = createNonDurableQueue || other.createNonDurableQueue;
deleteNonDurableQueue = deleteNonDurableQueue || other.deleteNonDurableQueue;
manage = manage || other.manage;
browse = browse || other.browse;
}
}

View File

@ -420,7 +420,13 @@ public class LegacyLDAPSecuritySettingPlugin implements SecuritySettingPlugin {
boolean write = permissionType.equalsIgnoreCase(writePermissionValue);
boolean read = permissionType.equalsIgnoreCase(readPermissionValue);
boolean admin = permissionType.equalsIgnoreCase(adminPermissionValue);
Role role = new Role(roleName,
Role existingRole = null;
for (Role role : roles) {
if (role.getName().equals(roleName)) {
existingRole = role;
}
}
Role newRole = new Role(roleName,
write, // send
read, // consume
(allowQueueAdminOnRead && read) || admin, // createDurableQueue
@ -431,7 +437,11 @@ public class LegacyLDAPSecuritySettingPlugin implements SecuritySettingPlugin {
read, // browse
admin, // createAddress
admin); // deleteAddress
roles.add(role);
if (existingRole != null) {
existingRole.merge(newRole);
} else {
roles.add(newRole);
}
}
if (logger.isDebugEnabled()) {
@ -475,9 +485,35 @@ public class LegacyLDAPSecuritySettingPlugin implements SecuritySettingPlugin {
try {
processSearchResult(newRoles, (SearchResult) namingEvent.getNewBinding());
for (Map.Entry<String, Set<Role>> entry : newRoles.entrySet()) {
logger.debug("adding match " + entry.getKey() + ": " + entry.getValue());
securityRepository.addMatch(entry.getKey(), entry.getValue());
for (Map.Entry<String, Set<Role>> newRole : newRoles.entrySet()) {
Set<Role> existingRoles = securityRepository.getMatch(newRole.getKey());
if (securityRepository.containsExactWildcardMatch(newRole.getKey()) || securityRepository.containsExactMatch(newRole.getKey()) && existingRoles != securityRepository.getDefault()) {
Set<Role> merged = new HashSet<>();
for (Role existingRole : existingRoles) {
for (Role role : newRole.getValue()) {
if (existingRole.getName().equals(role.getName())) {
if (logger.isDebugEnabled()) {
logger.debug("merging role " + role + " with existing role " + existingRole + " at " + newRole.getKey());
}
existingRole.merge(role);
merged.add(role);
}
}
}
for (Role role : newRole.getValue()) {
if (!merged.contains(role)) {
if (logger.isDebugEnabled()) {
logger.debug("add new role " + role + " to existing roles " + existingRoles);
}
existingRoles.add(role);
}
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("adding new match " + newRole.getKey() + ": " + newRole.getValue());
}
securityRepository.addMatch(newRole.getKey(), newRole.getValue());
}
}
} catch (NamingException e) {
ActiveMQServerLogger.LOGGER.failedToProcessEvent(e);
@ -537,10 +573,10 @@ public class LegacyLDAPSecuritySettingPlugin implements SecuritySettingPlugin {
}
}
}
}
for (Role roleToRemove : rolesToRemove) {
roles.remove(roleToRemove);
}
if (roles.removeAll(rolesToRemove)) {
logger.debug("Removed roles: " + rolesToRemove + ". Remaining roles: " + roles);
}
}
} catch (NamingException e) {

View File

@ -109,4 +109,6 @@ public interface HierarchicalRepository<T> {
int getCacheSize();
boolean containsExactMatch(String match);
boolean containsExactWildcardMatch(String match);
}

View File

@ -249,6 +249,11 @@ public class HierarchicalObjectRepository<T> implements HierarchicalRepository<T
return exactMatches.containsKey(match);
}
@Override
public boolean containsExactWildcardMatch(String match) {
return wildcardMatches.containsKey(match);
}
/**
* merge all the possible matches, if the values implement Mergeable then a full merge is done
*

View File

@ -17,7 +17,7 @@
# Additional logger names to configure (root logger is always configured)
# Root logger option
loggers=org.jboss.logging,org.apache.activemq.artemis.core.server,org.apache.activemq.artemis.utils,org.apache.activemq.artemis.core.journal,org.apache.activemq.artemis.core.client,org.apache.activemq.artemis.jms,org.apache.activemq.artemis.ra,org.apache.activemq.artemis.tests.smoke,org.apache.activemq.artemis.tests.unit,org.apache.activemq.artemis.tests.integration,org.apache.activemq.artemis.jms.tests,org.apache.activemq.cli.test,org.apache.activemq.audit,org.apache.activemq.audit.message
loggers=org.jboss.logging,org.apache.activemq.artemis.core.server,org.apache.activemq.artemis.utils,org.apache.activemq.artemis.core.journal,org.apache.activemq.artemis.core.client,org.apache.activemq.artemis.jms,org.apache.activemq.artemis.ra,org.apache.activemq.artemis.tests.smoke,org.apache.activemq.artemis.tests.unit,org.apache.activemq.artemis.tests.integration,org.apache.activemq.artemis.jms.tests,org.apache.activemq.cli.test,org.apache.activemq.audit,org.apache.activemq.audit.message,org.apache.directory
# Root logger level
logger.level=INFO
@ -36,6 +36,8 @@ logger.org.apache.activemq.artemis.tests.unit.level=DEBUG
logger.org.apache.activemq.artemis.jms.tests.level=DEBUG
logger.org.apache.activemq.artemis.tests.smoke.level=DEBUG
logger.org.apache.directory.level=FATAL
# Root logger handlers
logger.handlers=CONSOLE,TEST

View File

@ -243,7 +243,7 @@ public class LegacyLDAPSecuritySettingPluginListenerTest extends AbstractLdapTes
try {
session.createConsumer(queue);
Assert.fail("Sending here should fail due to the modified security data.");
Assert.fail("Creating consumer here should fail due to the modified security data.");
} catch (ActiveMQException e) {
// ok
}

View File

@ -31,6 +31,7 @@ import java.util.Hashtable;
import java.util.Map;
import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.QueueConfiguration;
import org.apache.activemq.artemis.api.core.TransportConfiguration;
import org.apache.activemq.artemis.api.core.client.ActiveMQClient;
import org.apache.activemq.artemis.api.core.client.ClientProducer;
@ -167,75 +168,134 @@ public class LegacyLDAPSecuritySettingPluginListenerTest2 extends AbstractLdapTe
Assert.assertTrue(e.getMessage().contains("229032")); // authorization exception
}
{ // add new user - user3
DirContext ctx = getContext();
BasicAttributes basicAttributes = new BasicAttributes();
basicAttributes.put("userPassword", "secret");
Attribute objclass = new BasicAttribute("objectclass");
objclass.add("top");
objclass.add("person");
objclass.add("organizationalPerson");
objclass.add("inetOrgPerson");
basicAttributes.put(objclass);
Attribute cn = new BasicAttribute("cn");
cn.add("user3");
basicAttributes.put(cn);
Attribute sn = new BasicAttribute("sn");
sn.add("user3");
basicAttributes.put(sn);
ctx.bind("uid=user3,ou=users,dc=example,dc=com", null, basicAttributes);
}
addUser("user5");
addRole("team5", new String[] {"user5"});
addUser("userFoo");
addRole("roleFoo", new String[] {"userFoo"});
addMatch("project5.$");
addPermission(new String[] {"team5", "amq"}, "read", "project5.$");
addPermission(new String[] {"team5", "amq", "roleFoo"}, "write", "project5.$");
addPermission(new String[] {"team5", "amq"}, "admin", "project5.$");
{ // add new role - team3
DirContext ctx = getContext();
BasicAttributes basicAttributes = new BasicAttributes();
basicAttributes.put("uniqueMember", "uid=user3,ou=users,dc=example,dc=com");
Attribute objclass = new BasicAttribute("objectclass");
objclass.add("top");
objclass.add("groupOfUniqueNames");
basicAttributes.put(objclass);
ctx.bind("cn=team3,ou=roles,dc=example,dc=com", null, basicAttributes);
}
{ // add new security match - project3.$
DirContext ctx = getContext();
BasicAttributes basicAttributes = new BasicAttributes();
Attribute objclass = new BasicAttribute("objectclass");
objclass.add("top");
objclass.add("applicationProcess");
basicAttributes.put(objclass);
ctx.bind("cn=project3.$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com", null, basicAttributes);
}
{ // add write permission for match
DirContext ctx = getContext();
BasicAttributes basicAttributes = new BasicAttributes();
basicAttributes.put("uniquemember", "cn=team3,ou=roles,dc=example,dc=com");
Attribute objclass = new BasicAttribute("objectclass");
objclass.add("top");
objclass.add("groupOfUniqueNames");
basicAttributes.put(objclass);
ctx.bind("cn=write,cn=project3.$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com", null, basicAttributes);
// authz should succeed
try {
ClientSession session = cf.createSession("user5", "secret", false, true, true, false, 0);
ClientProducer producer = session.createProducer("project5.test");
producer.send(session.createMessage(true));
} catch (ActiveMQException e) {
Assert.fail("Should NOT fail");
}
// authz should succeed
try {
ClientSession session = cf.createSession("user3", "secret", false, true, true, false, 0);
ClientProducer producer = session.createProducer("project3.test");
ClientSession session = cf.createSession("user5", "secret", false, true, true, false, 0);
session.createQueue(new QueueConfiguration("project5.test"));
} catch (ActiveMQException e) {
Assert.fail("Should NOT fail");
}
// authz should succeed
try {
ClientSession session = cf.createSession("userFoo", "secret", false, true, true, false, 0);
ClientProducer producer = session.createProducer("project5.test");
producer.send(session.createMessage(true));
} catch (ActiveMQException e) {
e.printStackTrace();
Assert.fail("Should NOT fail");
}
// authz should fail
try {
ClientSession session = cf.createSession("user3", "secret", false, true, true, false, 0);
ClientProducer producer = session.createProducer("project7.test");
ClientSession session = cf.createSession("userFoo", "secret", false, true, true, false, 0);
session.createQueue(new QueueConfiguration("project5.foo"));
Assert.fail("Creating queue here should fail!");
} catch (ActiveMQException e) {
Assert.assertTrue(e.getMessage().contains("229213")); // authorization exception
}
// authz should fail
try {
ClientSession session = cf.createSession("userFoo", "secret", false, true, true, false, 0);
session.createConsumer("project5.test");
Assert.fail("Creating consumer here should fail!");
} catch (ActiveMQException e) {
Assert.assertTrue(e.getMessage().contains("229213")); // authorization exception
}
// authz should fail
try {
ClientSession session = cf.createSession("user5", "secret", false, true, true, false, 0);
ClientProducer producer = session.createProducer("project6.test");
producer.send(session.createMessage(true));
Assert.fail("Sending message here should fail!");
} catch (ActiveMQException e) {
Assert.assertTrue(e.getMessage().contains("229032")); // authorization exception
}
// authz should fail
try {
ClientSession session = cf.createSession("user5", "secret", false, true, true, false, 0);
ClientProducer producer = session.createProducer("project4.test");
producer.send(session.createMessage(true));
Assert.fail("Sending message here should fail!");
} catch (ActiveMQException e) {
Assert.assertTrue(e.getMessage().contains("229032")); // authorization exception
}
}
private void addPermission(String[] roles, String permission, String match) throws NamingException {
DirContext ctx = getContext();
BasicAttributes basicAttributes = new BasicAttributes();
Attribute uniqueMembers = new BasicAttribute("uniqueMember");
for (String role : roles) {
uniqueMembers.add("cn=" + role + ",ou=roles,dc=example,dc=com");
}
basicAttributes.put(uniqueMembers);
Attribute objclass = new BasicAttribute("objectclass");
objclass.add("top");
objclass.add("groupOfUniqueNames");
basicAttributes.put(objclass);
ctx.bind("cn=" + permission + ",cn=" + match + ",ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com", null, basicAttributes);
}
private void addMatch(String match) throws NamingException {
DirContext ctx = getContext();
BasicAttributes basicAttributes = new BasicAttributes();
Attribute objclass = new BasicAttribute("objectclass");
objclass.add("top");
objclass.add("applicationProcess");
basicAttributes.put(objclass);
ctx.bind("cn=" + match + ",ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com", null, basicAttributes);
}
private void addRole(String roleName, String[] usersInRole) throws NamingException {
DirContext ctx = getContext();
BasicAttributes basicAttributes = new BasicAttributes();
for (String user : usersInRole) {
basicAttributes.put("uniqueMember", "uid=" + user + ",ou=users,dc=example,dc=com");
}
Attribute objclass = new BasicAttribute("objectclass");
objclass.add("top");
objclass.add("groupOfUniqueNames");
basicAttributes.put(objclass);
ctx.bind("cn=" + roleName + ",ou=roles,dc=example,dc=com", null, basicAttributes);
}
private void addUser(String username) throws NamingException {
DirContext ctx = getContext();
BasicAttributes basicAttributes = new BasicAttributes();
basicAttributes.put("userPassword", "secret");
Attribute objclass = new BasicAttribute("objectclass");
objclass.add("top");
objclass.add("person");
objclass.add("organizationalPerson");
objclass.add("inetOrgPerson");
basicAttributes.put(objclass);
Attribute cn = new BasicAttribute("cn");
cn.add(username);
basicAttributes.put(cn);
Attribute sn = new BasicAttribute("sn");
sn.add(username);
basicAttributes.put(sn);
ctx.bind("uid=" + username + ",ou=users,dc=example,dc=com", null, basicAttributes);
}
}

View File

@ -20,6 +20,11 @@ dc: example
objectclass: domain
objectclass: top
dn: ou=ActiveMQ,dc=example,dc=com
objectClass: organizationalUnit
objectClass: top
ou: ActiveMQ
dn: ou=users,dc=example,dc=com
objectclass: organizationalUnit
objectclass: top
@ -47,6 +52,50 @@ givenName: user2
uid: user2
userPassword: secret
dn: uid=user3,ou=users,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: user3
sn: user3
givenName: user3
uid: user3
userPassword: secret
dn: uid=user4,ou=users,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: user4
sn: user4
givenName: user4
uid: user4
userPassword: secret
dn: uid=amq,ou=users,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: amq
sn: amq
givenName: amq
uid: amq
userPassword: secret
dn: uid=adminUser,ou=users,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: adminUser
sn: adminUser
givenName: adminUser
uid: adminUser
userPassword: secret
dn: ou=roles,dc=example,dc=com
objectclass: organizationalUnit
objectclass: top
@ -64,10 +113,29 @@ objectClass: top
cn: team2
uniqueMember: uid=user2,ou=users,dc=example,dc=com
dn: ou=ActiveMQ,dc=example,dc=com
objectClass: organizationalUnit
dn: cn=team3,ou=roles,dc=example,dc=com
objectClass: groupOfUniqueNames
objectClass: top
ou: ActiveMQ
cn: team3
uniqueMember: uid=user3,ou=users,dc=example,dc=com
dn: cn=team4,ou=roles,dc=example,dc=com
objectClass: groupOfUniqueNames
objectClass: top
cn: team4
uniqueMember: uid=user4,ou=users,dc=example,dc=com
dn: cn=amq,ou=roles,dc=example,dc=com
objectClass: groupOfUniqueNames
objectClass: top
cn: amq
uniqueMember: uid=amq,ou=users,dc=example,dc=com
dn: cn=admin,ou=roles,dc=example,dc=com
objectClass: groupOfUniqueNames
objectClass: top
cn: admin
uniqueMember: uid=adminUser,ou=users,dc=example,dc=com
dn: ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: organizationalUnit
@ -79,15 +147,53 @@ objectClass: organizationalUnit
objectClass: top
ou: queues
dn: cn=$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: applicationProcess
objectClass: top
cn: $
dn: cn=read,cn=$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: groupOfUniqueNames
objectClass: top
cn: read
uniqueMember: cn=admin,ou=roles,dc=example,dc=com
dn: cn=write,cn=$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: groupOfUniqueNames
objectClass: top
cn: write
uniqueMember: cn=admin,ou=roles,dc=example,dc=com
dn: cn=admin,cn=$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: groupOfUniqueNames
objectClass: top
cn: admin
uniqueMember: cn=admin,ou=roles,dc=example,dc=com
dn: cn=project1.$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: applicationProcess
objectClass: top
cn: project1.$
dn: cn=write,cn=project1.$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: top
dn: cn=read,cn=project1.$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: groupOfUniqueNames
objectClass: top
cn: read
uniqueMember: cn=amq,ou=roles,dc=example,dc=com
uniqueMember: cn=team1,ou=roles,dc=example,dc=com
dn: cn=write,cn=project1.$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: groupOfUniqueNames
objectClass: top
cn: write
uniqueMember: cn=amq,ou=roles,dc=example,dc=com
uniqueMember: cn=team1,ou=roles,dc=example,dc=com
dn: cn=admin,cn=project1.$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: groupOfUniqueNames
objectClass: top
cn: admin
uniqueMember: cn=amq,ou=roles,dc=example,dc=com
uniqueMember: cn=team1,ou=roles,dc=example,dc=com
dn: cn=project2.$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
@ -99,15 +205,71 @@ dn: cn=read,cn=project2.$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=co
objectClass: groupOfUniqueNames
objectClass: top
cn: read
uniqueMember: cn=amq,ou=roles,dc=example,dc=com
uniqueMember: cn=team2,ou=roles,dc=example,dc=com
dn: cn=$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: top
objectClass: applicationProcess
cn: $
dn: cn=write,cn=$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: top
dn: cn=write,cn=project2.$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: groupOfUniqueNames
objectClass: top
cn: write
uniqueMember: cn=some_role,ou=roles,dc=example,dc=com
uniqueMember: cn=amq,ou=roles,dc=example,dc=com
uniqueMember: cn=team2,ou=roles,dc=example,dc=com
dn: cn=admin,cn=project2.$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: groupOfUniqueNames
objectClass: top
cn: admin
uniqueMember: cn=amq,ou=roles,dc=example,dc=com
uniqueMember: cn=team2,ou=roles,dc=example,dc=com
dn: cn=project3.$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: applicationProcess
objectClass: top
cn: project3.$
dn: cn=read,cn=project3.$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: groupOfUniqueNames
objectClass: top
cn: read
uniqueMember: cn=amq,ou=roles,dc=example,dc=com
uniqueMember: cn=team3,ou=roles,dc=example,dc=com
dn: cn=write,cn=project3.$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: groupOfUniqueNames
objectClass: top
cn: write
uniqueMember: cn=amq,ou=roles,dc=example,dc=com
uniqueMember: cn=team3,ou=roles,dc=example,dc=com
dn: cn=admin,cn=project3.$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: groupOfUniqueNames
objectClass: top
cn: admin
uniqueMember: cn=amq,ou=roles,dc=example,dc=com
uniqueMember: cn=team3,ou=roles,dc=example,dc=com
dn: cn=project4.$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: applicationProcess
objectClass: top
cn: project4.$
dn: cn=read,cn=project4.$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: groupOfUniqueNames
objectClass: top
cn: read
uniqueMember: cn=amq,ou=roles,dc=example,dc=com
uniqueMember: cn=team4,ou=roles,dc=example,dc=com
dn: cn=write,cn=project4.$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: groupOfUniqueNames
objectClass: top
cn: write
uniqueMember: cn=amq,ou=roles,dc=example,dc=com
uniqueMember: cn=team4,ou=roles,dc=example,dc=com
dn: cn=admin,cn=project4.$,ou=queues,ou=destinations,ou=ActiveMQ,dc=example,dc=com
objectClass: groupOfUniqueNames
objectClass: top
cn: admin
uniqueMember: cn=amq,ou=roles,dc=example,dc=com
uniqueMember: cn=team4,ou=roles,dc=example,dc=com