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 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(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 { + + private String name; + + public RoleSet() { + super(); + } + + public RoleSet(String key, Set 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 = "\n" + + "" + "\n" + + "" + "\n" + + "" + "\n" + + "" + "\n" + + "" + "\n" + + "" + "\n" + + ""; + + 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 { + 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();