You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by gt...@apache.org on 2022/06/23 08:41:40 UTC
[activemq-artemis] branch main updated: ARTEMIS-3863 - allow Role configuration via properties
This is an automated email from the ASF dual-hosted git repository.
gtully pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/activemq-artemis.git
The following commit(s) were added to refs/heads/main by this push:
new 6d926719f4 ARTEMIS-3863 - allow Role configuration via properties
6d926719f4 is described below
commit 6d926719f48201bb4c89691a71b7891d26114537
Author: Gary Tully <ga...@gmail.com>
AuthorDate: Fri Jun 17 11:22:10 2022 +0100
ARTEMIS-3863 - allow Role configuration via properties
---
.../activemq/artemis/core/security/Role.java | 70 ++++++++++++++++---
.../core/config/impl/ConfigurationImpl.java | 22 ++++--
.../activemq/artemis/core/config/impl/RoleSet.java | 51 ++++++++++++++
.../core/config/impl/ConfigurationImplTest.java | 78 ++++++++++++++++++++++
4 files changed, 206 insertions(+), 15 deletions(-)
diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/security/Role.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/security/Role.java
index fcaa97450c..8977416e07 100644
--- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/security/Role.java
+++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/security/Role.java
@@ -28,32 +28,36 @@ public class Role implements Serializable {
private static final long serialVersionUID = 3560097227776448872L;
- private final String name;
+ private String name;
- private final boolean send;
+ private boolean send;
- private final boolean consume;
+ private boolean consume;
- private final boolean createAddress;
+ private boolean createAddress;
- private final boolean deleteAddress;
+ private boolean deleteAddress;
- private final boolean createDurableQueue;
+ private boolean createDurableQueue;
- private final boolean deleteDurableQueue;
+ private boolean deleteDurableQueue;
- private final boolean createNonDurableQueue;
+ private boolean createNonDurableQueue;
- private final boolean deleteNonDurableQueue;
+ private boolean deleteNonDurableQueue;
- private final boolean manage;
+ private boolean manage;
- private final boolean browse;
+ private boolean browse;
public JsonObject toJson() {
return JsonLoader.createObjectBuilder().add("name", name).add("send", send).add("consume", consume).add("createDurableQueue", createDurableQueue).add("deleteDurableQueue", deleteDurableQueue).add("createNonDurableQueue", createNonDurableQueue).add("deleteNonDurableQueue", deleteNonDurableQueue).add("manage", manage).add("browse", browse).add("createAddress", createAddress).add("deleteAddress", deleteAddress).build();
}
+ public Role() {
+ // for properties config
+ }
+
/**
* @param name
* @param send
@@ -165,6 +169,50 @@ public class Role implements Serializable {
return browse;
}
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setSend(boolean send) {
+ this.send = send;
+ }
+
+ public void setConsume(boolean consume) {
+ this.consume = consume;
+ }
+
+ public void setCreateAddress(boolean createAddress) {
+ this.createAddress = createAddress;
+ }
+
+ public void setDeleteAddress(boolean deleteAddress) {
+ this.deleteAddress = deleteAddress;
+ }
+
+ public void setCreateDurableQueue(boolean createDurableQueue) {
+ this.createDurableQueue = createDurableQueue;
+ }
+
+ public void setDeleteDurableQueue(boolean deleteDurableQueue) {
+ this.deleteDurableQueue = deleteDurableQueue;
+ }
+
+ public void setCreateNonDurableQueue(boolean createNonDurableQueue) {
+ this.createNonDurableQueue = createNonDurableQueue;
+ }
+
+ public void setDeleteNonDurableQueue(boolean deleteNonDurableQueue) {
+ this.deleteNonDurableQueue = deleteNonDurableQueue;
+ }
+
+ public void setManage(boolean manage) {
+ this.manage = manage;
+ }
+
+ public void setBrowse(boolean browse) {
+ this.browse = browse;
+ }
+
@Override
public String toString() {
StringBuffer stringReturn = new StringBuffer("Role {name=" + name + "; allows=[");
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java
index 070d230094..58ddeb18d3 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java
@@ -1726,6 +1726,12 @@ public class ConfigurationImpl implements Configuration, Serializable {
@Override
public ConfigurationImpl putSecurityRoles(String match, Set<Role> roles) {
+ securitySettings.put(match, new RoleSet(match, roles));
+ return this;
+ }
+
+ // to provide type information to creation from properties
+ public ConfigurationImpl addSecurityRole(String match, RoleSet roles) {
securitySettings.put(match, roles);
return this;
}
@@ -2813,7 +2819,8 @@ public class ConfigurationImpl implements Configuration, Serializable {
if (!map.containsKey(key)) {
map.put(key, newNamedInstanceForCollection(collectionInfo.getA(), collectionInfo.getB(), key));
}
- return map.get(key);
+ Object value = map.get(key);
+ return trackCollectionOrMap(null, value, value);
} else { // collection
Object value = findByNameProperty(key, (Collection)bean);
if (value == null) {
@@ -2821,12 +2828,16 @@ public class ConfigurationImpl implements Configuration, Serializable {
value = newNamedInstanceForCollection(collectionInfo.getA(), collectionInfo.getB(), key);
((Collection) bean).add(value);
}
- return value;
+ return trackCollectionOrMap(null, value, value);
}
}
Object resolved = getNestedProperty(bean, name);
+ return trackCollectionOrMap(name, resolved, bean);
+ }
+
+ private Object trackCollectionOrMap(String name, Object resolved, Object bean) {
if (resolved instanceof Collection || resolved instanceof Map) {
collections.push(new Pair<String, Object>(name, bean));
}
@@ -2907,8 +2918,11 @@ public class ConfigurationImpl implements Configuration, Serializable {
private Object newNamedInstanceForCollection(String collectionPropertyName, Object hostingBean, String name) {
// find the add X and init an instance of the type with name=name
- // expect an add... without the plural
- String addPropertyName = "add" + Character.toUpperCase(collectionPropertyName.charAt(0)) + collectionPropertyName.substring(1, collectionPropertyName.length() - 1);
+ String addPropertyName = "add";
+ // expect an add... without the plural for named accessors
+ if (collectionPropertyName != null && collectionPropertyName.length() > 0) {
+ addPropertyName += Character.toUpperCase(collectionPropertyName.charAt(0)) + collectionPropertyName.substring(1, collectionPropertyName.length() - 1);
+ }
// we don't know the type, infer from add method add(X x) or add(String key, X x)
final Method[] methods = hostingBean.getClass().getMethods();
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/RoleSet.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/RoleSet.java
new file mode 100644
index 0000000000..95bfefd602
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/RoleSet.java
@@ -0,0 +1,51 @@
+/*
+ * 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.core.config.impl;
+
+import org.apache.activemq.artemis.core.security.Role;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class RoleSet extends HashSet<Role> {
+
+ private String name;
+
+ public RoleSet() {
+ super();
+ }
+
+ public RoleSet(String key, Set<Role> value) {
+ setName(key);
+ if (value != null) {
+ addAll(value);
+ }
+ }
+
+ // provide a helper add method with the type
+ public void add(String name, Role value) {
+ super.add(value);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImplTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImplTest.java
index 9811e1a8d3..2ff0d42e30 100644
--- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImplTest.java
+++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImplTest.java
@@ -16,9 +16,11 @@
*/
package org.apache.activemq.artemis.core.config.impl;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
@@ -28,6 +30,7 @@ import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.apache.activemq.artemis.ArtemisConstants;
@@ -39,6 +42,8 @@ import org.apache.activemq.artemis.core.config.amqpBrokerConnectivity.AMQPBroker
import org.apache.activemq.artemis.core.config.amqpBrokerConnectivity.AMQPBrokerConnectionAddressType;
import org.apache.activemq.artemis.core.config.amqpBrokerConnectivity.AMQPMirrorBrokerConnectionElement;
import org.apache.activemq.artemis.core.config.ha.LiveOnlyPolicyConfiguration;
+import org.apache.activemq.artemis.core.deployers.impl.FileConfigurationParser;
+import org.apache.activemq.artemis.core.security.Role;
import org.apache.activemq.artemis.core.server.JournalType;
import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType;
import org.apache.activemq.artemis.core.server.plugin.impl.LoggingActiveMQServerPlugin;
@@ -759,6 +764,79 @@ public class ConfigurationImplTest extends ActiveMQTestBase {
Assert.assertEquals(SimpleString.toSimpleString("moreImportant"), configuration.getAddressSettings().get("Name.With.Dots").getExpiryAddress());
}
+ @Test
+ public void testRoleSettingsViaProperties() throws Exception {
+ ConfigurationImpl configuration = new ConfigurationImpl();
+
+ Properties properties = new Properties();
+
+ properties.put("securityRoles.TEST.users.send", "true");
+ properties.put("securityRoles.TEST.users.consume", "true");
+
+ configuration.parsePrefixedProperties(properties, null);
+
+ Assert.assertEquals(1, configuration.getSecurityRoles().size());
+ Assert.assertEquals(1, configuration.getSecurityRoles().get("TEST").size());
+ Assert.assertTrue(configuration.getSecurityRoles().get("TEST").stream().findFirst().orElse(null).isConsume());
+ Assert.assertTrue(configuration.getSecurityRoles().get("TEST").stream().findFirst().orElse(null).isSend());
+ Assert.assertFalse(configuration.getSecurityRoles().get("TEST").stream().findFirst().orElse(null).isCreateAddress());
+ }
+
+ @Test
+ public void testRoleAugmentViaProperties() throws Exception {
+
+ final String xmlConfig = "<configuration xmlns=\"urn:activemq\"\n" +
+ "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
+ "xsi:schemaLocation=\"urn:activemq /schema/artemis-configuration.xsd\">\n" +
+ "<security-settings>" + "\n" +
+ "<security-setting match=\"#\">" + "\n" +
+ "<permission type=\"consume\" roles=\"guest\"/>" + "\n" +
+ "<permission type=\"send\" roles=\"guest\"/>" + "\n" +
+ "</security-setting>" + "\n" +
+ "</security-settings>" + "\n" +
+ "</configuration>";
+
+ FileConfigurationParser parser = new FileConfigurationParser();
+ ByteArrayInputStream input = new ByteArrayInputStream(xmlConfig.getBytes(StandardCharsets.UTF_8));
+
+ ConfigurationImpl configuration = (ConfigurationImpl) parser.parseMainConfig(input);
+ Properties properties = new Properties();
+
+ // new entry
+ properties.put("securityRoles.TEST.users.send", "true");
+ properties.put("securityRoles.TEST.users.consume", "false");
+
+ // modify existing role
+ properties.put("securityRoles.#.guest.consume", "false");
+
+ // modify with new role
+ properties.put("securityRoles.#.users.send", "true");
+
+ configuration.parsePrefixedProperties(properties, null);
+
+ // verify new addition
+ Assert.assertEquals(2, configuration.getSecurityRoles().size());
+ Assert.assertEquals(1, configuration.getSecurityRoles().get("TEST").size());
+ Assert.assertFalse(configuration.getSecurityRoles().get("TEST").stream().findFirst().orElse(null).isConsume());
+ Assert.assertTrue(configuration.getSecurityRoles().get("TEST").stream().findFirst().orElse(null).isSend());
+
+ // verify augmentation
+ Assert.assertEquals(2, configuration.getSecurityRoles().get("#").size());
+ Set roles = configuration.getSecurityRoles().get("#");
+ class RolePredicate implements Predicate<Role> {
+ final String roleName;
+ RolePredicate(String name) {
+ this.roleName = name;
+ }
+ @Override
+ public boolean test(Role role) {
+ return roleName.equals(role.getName()) && !role.isConsume() && role.isSend() && !role.isCreateAddress();
+ }
+ }
+ Assert.assertEquals(1L, roles.stream().filter(new RolePredicate("guest")).count());
+ Assert.assertEquals(1L, roles.stream().filter(new RolePredicate("users")).count());
+ }
+
@Test
public void testValuePostFixModifier() throws Throwable {
ConfigurationImpl configuration = new ConfigurationImpl();