ARTEMIS-3863 - allow Role configuration via properties

This commit is contained in:
Gary Tully 2022-06-17 11:22:10 +01:00
parent 56e9d9525d
commit 6d926719f4
4 changed files with 206 additions and 15 deletions

View File

@ -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=[");

View File

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

View File

@ -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;
}
}

View File

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