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();