This closes #1206
This commit is contained in:
commit
e078666c03
|
@ -927,6 +927,10 @@ public interface Configuration {
|
|||
*/
|
||||
Map<String, Set<Role>> getSecurityRoles();
|
||||
|
||||
Configuration addSecurityRoleNameMapping(String internalRole, Set<String> externalRoles);
|
||||
|
||||
Map<String, Set<String>> getSecurityRoleNameMappings();
|
||||
|
||||
Configuration putSecurityRoles(String match, Set<Role> roles);
|
||||
|
||||
Configuration setConnectorServiceConfigurations(List<ConnectorServiceConfiguration> configs);
|
||||
|
|
|
@ -232,6 +232,8 @@ public class ConfigurationImpl implements Configuration, Serializable {
|
|||
|
||||
private List<SecuritySettingPlugin> securitySettingPlugins = new ArrayList<>();
|
||||
|
||||
private Map<String, Set<String>> securityRoleNameMappings = new HashMap<>();
|
||||
|
||||
protected List<ConnectorServiceConfiguration> connectorServiceConfigurations = new ArrayList<>();
|
||||
|
||||
private boolean maskPassword = ActiveMQDefaultConfiguration.isDefaultMaskPassword();
|
||||
|
@ -1293,6 +1295,21 @@ public class ConfigurationImpl implements Configuration, Serializable {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration addSecurityRoleNameMapping(String internalRole, Set<String> externalRoles) {
|
||||
if (securityRoleNameMappings.containsKey(internalRole)) {
|
||||
securityRoleNameMappings.get(internalRole).addAll(externalRoles);
|
||||
} else {
|
||||
securityRoleNameMappings.put(internalRole, externalRoles);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Set<String>> getSecurityRoleNameMappings() {
|
||||
return securityRoleNameMappings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ConnectorServiceConfiguration> getConnectorServiceConfigurations() {
|
||||
return this.connectorServiceConfigurations;
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.io.Reader;
|
|||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -94,6 +95,8 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
|
|||
|
||||
public static final String SECURITY_PLUGIN_ELEMENT_NAME = "security-setting-plugin";
|
||||
|
||||
public static final String SECURITY_ROLE_MAPPING_NAME = "role-mapping";
|
||||
|
||||
private static final String PERMISSION_ELEMENT_NAME = "permission";
|
||||
|
||||
private static final String SETTING_ELEMENT_NAME = "setting";
|
||||
|
@ -106,6 +109,10 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
|
|||
|
||||
private static final String VALUE_ATTR_NAME = "value";
|
||||
|
||||
private static final String ROLE_FROM_ATTR_NAME = "from";
|
||||
|
||||
private static final String ROLE_TO_ATTR_NAME = "to";
|
||||
|
||||
static final String CREATEDURABLEQUEUE_NAME = "createDurableQueue";
|
||||
|
||||
private static final String DELETEDURABLEQUEUE_NAME = "deleteDurableQueue";
|
||||
|
@ -618,12 +625,18 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
|
|||
*/
|
||||
private void parseSecurity(final Element e, final Configuration config) {
|
||||
NodeList elements = e.getElementsByTagName("security-settings");
|
||||
|
||||
if (elements.getLength() != 0) {
|
||||
Element node = (Element) elements.item(0);
|
||||
NodeList list = node.getElementsByTagName(SECURITY_ELEMENT_NAME);
|
||||
NodeList list = node.getElementsByTagName(SECURITY_ROLE_MAPPING_NAME);
|
||||
for (int i = 0; i < list.getLength(); i++) {
|
||||
Pair<String, Set<Role>> securityItem = parseSecurityRoles(list.item(i));
|
||||
Map<String, Set<String>> roleMappings = parseSecurityRoleMapping(list.item(i));
|
||||
for (Map.Entry<String, Set<String>> roleMapping : roleMappings.entrySet()) {
|
||||
config.addSecurityRoleNameMapping(roleMapping.getKey(), roleMapping.getValue());
|
||||
}
|
||||
}
|
||||
list = node.getElementsByTagName(SECURITY_ELEMENT_NAME);
|
||||
for (int i = 0; i < list.getLength(); i++) {
|
||||
Pair<String, Set<Role>> securityItem = parseSecurityRoles(list.item(i), config.getSecurityRoleNameMappings());
|
||||
config.putSecurityRoles(securityItem.getA(), securityItem.getB());
|
||||
}
|
||||
list = node.getElementsByTagName(SECURITY_PLUGIN_ELEMENT_NAME);
|
||||
|
@ -711,7 +724,7 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
|
|||
* @param node
|
||||
* @return
|
||||
*/
|
||||
protected Pair<String, Set<Role>> parseSecurityRoles(final Node node) {
|
||||
protected Pair<String, Set<Role>> parseSecurityRoles(final Node node, final Map<String, Set<String>> roleMappings) {
|
||||
final String match = node.getAttributes().getNamedItem("match").getNodeValue();
|
||||
|
||||
Set<Role> securityRoles = new HashSet<>();
|
||||
|
@ -737,7 +750,9 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
|
|||
final String type = getAttributeValue(child, TYPE_ATTR_NAME);
|
||||
final String roleString = getAttributeValue(child, ROLES_ATTR_NAME);
|
||||
String[] roles = roleString.split(",");
|
||||
for (String role : roles) {
|
||||
String[] mappedRoles = getMappedRoleNames(roles, roleMappings);
|
||||
|
||||
for (String role : mappedRoles) {
|
||||
if (SEND_NAME.equals(type)) {
|
||||
send.add(role.trim());
|
||||
} else if (CONSUME_NAME.equals(type)) {
|
||||
|
@ -770,7 +785,6 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (String role : allRoles) {
|
||||
|
@ -780,6 +794,23 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
|
|||
return securityMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate and expand a set of role names to a set of mapped role names, also includes the original role names
|
||||
* @param roles the original set of role names
|
||||
* @param roleMappings a one-to-many mapping of original role names to mapped role names
|
||||
* @return the final set of mapped role names
|
||||
*/
|
||||
private String[] getMappedRoleNames(String[] roles, Map<String, Set<String>> roleMappings) {
|
||||
Set<String> mappedRoles = new HashSet<>();
|
||||
for (String role : roles) {
|
||||
if (roleMappings.containsKey(role)) {
|
||||
mappedRoles.addAll(roleMappings.get(role));
|
||||
}
|
||||
mappedRoles.add(role);
|
||||
}
|
||||
return mappedRoles.toArray(new String[mappedRoles.size()]);
|
||||
}
|
||||
|
||||
private Pair<SecuritySettingPlugin, Map<String, String>> parseSecuritySettingPlugins(Node item) {
|
||||
final String clazz = item.getAttributes().getNamedItem("class-name").getNodeValue();
|
||||
final Map<String, String> settings = new HashMap<>();
|
||||
|
@ -804,6 +835,38 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
|
|||
return new Pair<>(securitySettingPlugin, settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the map of internal ActiveMQ role names to sets of external (e.g. LDAP) role names. For example, given a role
|
||||
* "myrole" with a DN of "cn=myrole,dc=local,dc=com":
|
||||
* from="cn=myrole,dc=local,dc=com", to="amq,admin,guest"
|
||||
* from="cn=myOtherRole,dc=local,dc=com", to="amq"
|
||||
* The resulting map will consist of:
|
||||
* amq => {"cn=myrole,dc=local,dc=com","cn=myOtherRole",dc=local,dc=com"}
|
||||
* admin => {"cn=myrole,dc=local,dc=com"}
|
||||
* guest => {"cn=myrole,dc=local,dc=com"}
|
||||
* @param item the role-mapping node
|
||||
* @return the map of local ActiveMQ role names to the set of mapped role names
|
||||
*/
|
||||
private Map<String, Set<String>> parseSecurityRoleMapping(Node item) {
|
||||
Map<String, Set<String>> mappedRoleNames = new HashMap<>();
|
||||
String externalRoleName = getAttributeValue(item, ROLE_FROM_ATTR_NAME).trim();
|
||||
Set<String> internalRoleNames = new HashSet<>();
|
||||
Collections.addAll(internalRoleNames, getAttributeValue(item, ROLE_TO_ATTR_NAME).split(","));
|
||||
for (String internalRoleName : internalRoleNames) {
|
||||
internalRoleName = internalRoleName.trim();
|
||||
if (mappedRoleNames.containsKey(internalRoleName)) {
|
||||
mappedRoleNames.get(internalRoleName).add(externalRoleName);
|
||||
} else {
|
||||
Set<String> externalRoleNames = new HashSet<>();
|
||||
externalRoleNames.add(externalRoleName);
|
||||
if ((internalRoleName.length() > 0) && (externalRoleName.length() > 0)) {
|
||||
mappedRoleNames.put(internalRoleName, externalRoleNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
return mappedRoleNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param node
|
||||
* @return
|
||||
|
|
|
@ -754,81 +754,102 @@
|
|||
a list of security settings
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:complexType>
|
||||
<xsd:choice>
|
||||
<xsd:element name="security-setting" maxOccurs="unbounded" minOccurs="0">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
a permission to add to the matched addresses
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="permission" maxOccurs="unbounded" minOccurs="0">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="type" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
the type of permission
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="roles" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
a comma-separated list of roles to apply the permission to
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="match" type="xsd:string" use="required">
|
||||
<xsd:sequence>
|
||||
<xsd:choice>
|
||||
<xsd:element name="security-setting" maxOccurs="unbounded" minOccurs="0">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
regular expression for matching security roles against addresses
|
||||
a permission to add to the matched addresses
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="permission" maxOccurs="unbounded" minOccurs="0">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="type" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
the type of permission
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="roles" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
a comma-separated list of roles to apply the permission to
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="match" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
regular expression for matching security roles against addresses
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="security-setting-plugin" maxOccurs="1" minOccurs="0">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
a plugin
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="setting" maxOccurs="unbounded" minOccurs="0">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
the name of the setting
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="value" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
the value for the setting
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="class-name" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
the name of the plugin class to instantiate
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
<xsd:element name="role-mapping" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="from" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
the name of the external role
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="to" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
the comma delimited name of the internal role(s)
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="security-setting-plugin" maxOccurs="1" minOccurs="0">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
a plugin
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="setting" maxOccurs="unbounded" minOccurs="0">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
the name of the setting
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="value" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
the value for the setting
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="class-name" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
the name of the plugin class to instantiate
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
|
|
|
@ -476,6 +476,90 @@ public class FileConfigurationTest extends ConfigurationImplTest {
|
|||
assertEquals(legacyLDAPSecuritySettingPlugin.isEnableListener(), false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecurityRoleMapping() throws Exception {
|
||||
FileConfiguration fc = new FileConfiguration();
|
||||
FileDeploymentManager deploymentManager = new FileDeploymentManager("securityRoleMappings.xml");
|
||||
deploymentManager.addDeployable(fc);
|
||||
deploymentManager.readConfiguration();
|
||||
|
||||
Map<String, Set<Role>> securityRoles = fc.getSecurityRoles();
|
||||
Set<Role> roles = securityRoles.get("#");
|
||||
|
||||
//N.B. - FileConfigurationParser uses the constructor without createAddress and deleteAddress
|
||||
//cn=mygroup,dc=local,dc=com = amq1
|
||||
Role testRole1 = new Role("cn=mygroup,dc=local,dc=com",false, false, false,
|
||||
false, true, false, false,
|
||||
false);
|
||||
|
||||
//myrole1 = amq1 + amq2
|
||||
Role testRole2 = new Role("myrole1",false, false, false,
|
||||
false, true, true, false,
|
||||
false);
|
||||
|
||||
//myrole3 = amq3 + amq4
|
||||
Role testRole3 = new Role("myrole3",false, false, true,
|
||||
true, false, false, false,
|
||||
false);
|
||||
|
||||
//myrole4 = amq5 + amq!@#$%^&*() + amq6
|
||||
Role testRole4 = new Role("myrole4",true, true, false,
|
||||
false, false, false, false,
|
||||
true);
|
||||
|
||||
//myrole5 = amq4 = amq3 + amq4
|
||||
Role testRole5 = new Role("myrole5",false, false, true,
|
||||
true, false, false, false,
|
||||
false);
|
||||
|
||||
Role testRole6 = new Role("amq1",false, false, false,
|
||||
false, true, false, false,
|
||||
false);
|
||||
|
||||
Role testRole7 = new Role("amq2",false, false, false,
|
||||
false, false, true, false,
|
||||
false);
|
||||
|
||||
Role testRole8 = new Role("amq3",false, false, true,
|
||||
false, false, false, false,
|
||||
false);
|
||||
|
||||
Role testRole9 = new Role("amq4",false, false, true,
|
||||
true, false, false, false,
|
||||
false);
|
||||
|
||||
Role testRole10 = new Role("amq5",false, false, false,
|
||||
false, false, false, false,
|
||||
false);
|
||||
|
||||
Role testRole11 = new Role("amq6",false, true, false,
|
||||
false, false, false, false,
|
||||
true);
|
||||
|
||||
Role testRole12 = new Role("amq7",false, false, false,
|
||||
false, false, false, true,
|
||||
false);
|
||||
|
||||
Role testRole13 = new Role("amq!@#$%^&*()",true, false, false,
|
||||
false, false, false, false,
|
||||
false);
|
||||
|
||||
assertEquals(13, roles.size());
|
||||
assertTrue(roles.contains(testRole1));
|
||||
assertTrue(roles.contains(testRole2));
|
||||
assertTrue(roles.contains(testRole3));
|
||||
assertTrue(roles.contains(testRole4));
|
||||
assertTrue(roles.contains(testRole5));
|
||||
assertTrue(roles.contains(testRole6));
|
||||
assertTrue(roles.contains(testRole7));
|
||||
assertTrue(roles.contains(testRole8));
|
||||
assertTrue(roles.contains(testRole9));
|
||||
assertTrue(roles.contains(testRole10));
|
||||
assertTrue(roles.contains(testRole11));
|
||||
assertTrue(roles.contains(testRole12));
|
||||
assertTrue(roles.contains(testRole13));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContextClassLoaderUsage() throws Exception {
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<!--
|
||||
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.
|
||||
-->
|
||||
<configuration
|
||||
xmlns="urn:activemq"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="urn:activemq ../../../../activemq-server/src/main/resources/schema/artemis-server.xsd">
|
||||
<core xmlns="urn:activemq:core">
|
||||
<security-settings>
|
||||
<security-setting match="#">
|
||||
<permission type="createNonDurableQueue" roles="amq1"/>
|
||||
<permission type="deleteNonDurableQueue" roles="amq2"/>
|
||||
<permission type="createDurableQueue" roles="amq3,amq4"/>
|
||||
<permission type="deleteDurableQueue" roles="amq4"/>
|
||||
<permission type="createAddress" roles="amq5"/>
|
||||
<permission type="deleteAddress" roles="amq5"/>
|
||||
<permission type="consume" roles="amq6"/>
|
||||
<permission type="browse" roles="amq6"/>
|
||||
<permission type="send" roles="amq!@#$%^&*()"/>
|
||||
<!-- we need this otherwise ./artemis data imp wouldn't work -->
|
||||
<permission type="manage" roles="amq7"/>
|
||||
</security-setting>
|
||||
<role-mapping from="cn=mygroup,dc=local,dc=com" to="amq1" />
|
||||
<role-mapping from="myrole1" to="amq1,amq2" />
|
||||
<role-mapping from="myrole2" to="" />
|
||||
<role-mapping from="myrole3" to="amq3" />
|
||||
<role-mapping from="myrole3" to="amq4" />
|
||||
<role-mapping from="myrole4" to="amq5,amq!@#$%^&*(),amq6" />
|
||||
<role-mapping from="myrole5" to="amq4" />
|
||||
</security-settings>
|
||||
</core>
|
||||
</configuration>
|
Loading…
Reference in New Issue