You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by cl...@apache.org on 2019/10/23 19:28:22 UTC
[activemq-artemis] branch master updated: ARTEMIS-2503 Improve
wildcards for the authorisation key attributes
This is an automated email from the ASF dual-hosted git repository.
clebertsuconic pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/activemq-artemis.git
The following commit(s) were added to refs/heads/master by this push:
new 0ac6057 ARTEMIS-2503 Improve wildcards for the authorisation key attributes
new 4f3df1f This closes #2851
0ac6057 is described below
commit 0ac605740eda4a11dfa29150da343557435d5db7
Author: brusdev <br...@gmail.com>
AuthorDate: Thu Oct 17 10:20:58 2019 +0200
ARTEMIS-2503 Improve wildcards for the authorisation key attributes
Improve wildcard support for the key attribute in the roles access
match element and whitelist entry element, allowing prefix match for
the mBean properties.
---
.../server/management/JMXAccessControlList.java | 171 +++++++++++++--------
.../management/JMXAccessControlListTest.java | 10 ++
docs/user-manual/en/management.md | 13 ++
3 files changed, 126 insertions(+), 68 deletions(-)
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/JMXAccessControlList.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/JMXAccessControlList.java
index 5430157..f09e91a 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/JMXAccessControlList.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/JMXAccessControlList.java
@@ -18,79 +18,95 @@ package org.apache.activemq.artemis.core.server.management;
import javax.management.ObjectName;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Pattern;
public class JMXAccessControlList {
-
- private Access defaultAccess = new Access("*");
- private Map<String, Access> domainAccess = new HashMap<>();
- private ConcurrentHashMap<String, List<String>> whitelist = new ConcurrentHashMap<>();
-
-
- public void addToWhiteList(String domain, String match) {
- List<String> list = new ArrayList<>();
- list = whitelist.putIfAbsent(domain, list);
- if (list == null) {
- list = whitelist.get(domain);
+ private static final String WILDCARD = "*";
+
+ private Access defaultAccess = new Access(WILDCARD);
+ private ConcurrentHashMap<String, TreeMap<String, Access>> domainAccess = new ConcurrentHashMap<>();
+ private ConcurrentHashMap<String, TreeMap<String, Access>> whitelist = new ConcurrentHashMap<>();
+ private Comparator<String> keyComparator = (key1, key2) -> {
+ boolean key1ContainsWildCard = key1.contains(WILDCARD);
+ boolean key2ContainsWildcard = key2.contains(WILDCARD);
+ if (key1ContainsWildCard && !key2ContainsWildcard) {
+ return +1;
+ } else if (!key1ContainsWildCard && key2ContainsWildcard) {
+ return -1;
+ } else if (key1ContainsWildCard && key2ContainsWildcard) {
+ return key2.length() - key1.length();
+ }
+ return key1.length() - key2.length();
+ };
+
+ public void addToWhiteList(String domain, String key) {
+ TreeMap<String, Access> domainMap = new TreeMap<>(keyComparator);
+ domainMap = whitelist.putIfAbsent(domain, domainMap);
+ if (domainMap == null) {
+ domainMap = whitelist.get(domain);
}
- list.add(match != null ? match : "*");
+ Access access = new Access(domain, normalizeKey(key));
+ domainMap.putIfAbsent(access.getKey(), access);
}
public List<String> getRolesForObject(ObjectName objectName, String methodName) {
- Hashtable<String, String> keyPropertyList = objectName.getKeyPropertyList();
- for (Map.Entry<String, String> keyEntry : keyPropertyList.entrySet()) {
- String key = keyEntry.getKey() + "=" + keyEntry.getValue();
- Access access = domainAccess.get(getObjectID(objectName.getDomain(), key));
- if (access != null) {
- return access.getMatchingRolesForMethod(methodName);
+ TreeMap<String, Access> domainMap = domainAccess.get(objectName.getDomain());
+ if (domainMap != null) {
+ Hashtable<String, String> keyPropertyList = objectName.getKeyPropertyList();
+ for (Map.Entry<String, String> keyEntry : keyPropertyList.entrySet()) {
+ String key = normalizeKey(keyEntry.getKey() + "=" + keyEntry.getValue());
+ for (Access accessEntry : domainMap.values()) {
+ if (accessEntry.getKeyPattern().matcher(key).matches()) {
+ return accessEntry.getMatchingRolesForMethod(methodName);
+ }
+ }
}
- }
- for (Map.Entry<String, String> keyEntry : keyPropertyList.entrySet()) {
- String key = keyEntry.getKey() + "=*";
- Access access = domainAccess.get(getObjectID(objectName.getDomain(), key));
+
+ Access access = domainMap.get("");
if (access != null) {
return access.getMatchingRolesForMethod(methodName);
}
}
- Access access = domainAccess.get(objectName.getDomain());
- if (access == null) {
- access = defaultAccess;
- }
- return access.getMatchingRolesForMethod(methodName);
+
+ return defaultAccess.getMatchingRolesForMethod(methodName);
}
public boolean isInWhiteList(ObjectName objectName) {
- List<String> matches = whitelist.get(objectName.getDomain());
- if (matches != null) {
- for (String match : matches) {
- if (match.equals("*")) {
- return true;
- } else {
- String[] split = match.split("=");
- String key = split[0];
- String val = split[1];
- String propVal = objectName.getKeyProperty(key);
- if (propVal != null && (val.equals("*") || propVal.equals(val))) {
+ TreeMap<String, Access> domainMap = whitelist.get(objectName.getDomain());
+ if (domainMap != null) {
+ if (domainMap.containsKey("")) {
+ return true;
+ }
+
+ Hashtable<String, String> keyPropertyList = objectName.getKeyPropertyList();
+ for (Map.Entry<String, String> keyEntry : keyPropertyList.entrySet()) {
+ String key = normalizeKey(keyEntry.getKey() + "=" + keyEntry.getValue());
+ for (Access accessEntry : domainMap.values()) {
+ if (accessEntry.getKeyPattern().matcher(key).matches()) {
return true;
}
}
}
}
+
return false;
}
public void addToDefaultAccess(String method, String... roles) {
if (roles != null) {
- if ( method.equals("*")) {
+ if ( method.equals(WILDCARD)) {
defaultAccess.addCatchAll(roles);
- } else if (method.endsWith("*")) {
- String prefix = method.replace("*", "");
+ } else if (method.endsWith(WILDCARD)) {
+ String prefix = method.replace(WILDCARD, "");
defaultAccess.addMethodsPrefixes(prefix, roles);
} else {
defaultAccess.addMethods(method, roles);
@@ -99,44 +115,54 @@ public class JMXAccessControlList {
}
public void addToRoleAccess(String domain,String key, String method, String... roles) {
- String id = getObjectID(domain, key);
- Access access = domainAccess.get(id);
+ TreeMap<String, Access> domainMap = new TreeMap<>(keyComparator);
+ domainMap = domainAccess.putIfAbsent(domain, domainMap);
+ if (domainMap == null) {
+ domainMap = domainAccess.get(domain);
+ }
+
+ String accessKey = normalizeKey(key);
+ Access access = domainMap.get(accessKey);
if (access == null) {
- access = new Access(domain);
- domainAccess.put(id, access);
+ access = new Access(domain, accessKey);
+ domainMap.put(accessKey, access);
}
- if (method.equals("*")) {
+ if (method.equals(WILDCARD)) {
access.addCatchAll(roles);
- } else if (method.endsWith("*")) {
- String prefix = method.replace("*", "");
+ } else if (method.endsWith(WILDCARD)) {
+ String prefix = method.replace(WILDCARD, "");
access.addMethodsPrefixes(prefix, roles);
} else {
access.addMethods(method, roles);
}
}
- private String getObjectID(String domain, String key) {
- String id = domain;
-
- if (key != null) {
- String actualKey = key;
- if (key.endsWith("\"")) {
- actualKey = actualKey.replace("\"", "");
- }
- id += ":" + actualKey;
+ private String normalizeKey(final String key) {
+ if (key == null) {
+ return "";
+ } else if (key.endsWith("\"")) {
+ return key.replace("\"", "");
}
- return id;
+ return key;
}
static class Access {
- private final String domain;
+ private final String id;
+ private final String key;
+ private final Pattern keyPattern;
List<String> catchAllRoles = new ArrayList<>();
Map<String, List<String>> methodRoles = new HashMap<>();
Map<String, List<String>> methodPrefixRoles = new LinkedHashMap<>();
- Access(String domain) {
- this.domain = domain;
+ Access(String id) {
+ this(id, "");
+ }
+
+ Access(String id, String key) {
+ this.id = id;
+ this.key = key;
+ this.keyPattern = Pattern.compile(key.replace(WILDCARD, ".*"));
}
public synchronized void addMethods(String prefix, String... roles) {
@@ -167,8 +193,16 @@ public class JMXAccessControlList {
}
}
- public String getDomain() {
- return domain;
+ public String getId() {
+ return id;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public Pattern getKeyPattern() {
+ return keyPattern;
}
public List<String> getMatchingRolesForMethod(String methodName) {
@@ -184,22 +218,23 @@ public class JMXAccessControlList {
return catchAllRoles;
}
}
+
public static JMXAccessControlList createDefaultList() {
JMXAccessControlList accessControlList = new JMXAccessControlList();
accessControlList.addToWhiteList("hawtio", "type=*");
accessControlList.addToRoleAccess("org.apache.activemq.artemis", null, "list*", "view", "update", "amq");
- accessControlList.addToRoleAccess("org.apache.activemq.artemis", null,"get*", "view", "update", "amq");
- accessControlList.addToRoleAccess("org.apache.activemq.artemis", null,"is*", "view", "update", "amq");
- accessControlList.addToRoleAccess("org.apache.activemq.artemis", null,"set*","update", "amq");
- accessControlList.addToRoleAccess("org.apache.activemq.artemis", null,"*", "amq");
+ accessControlList.addToRoleAccess("org.apache.activemq.artemis", null, "get*", "view", "update", "amq");
+ accessControlList.addToRoleAccess("org.apache.activemq.artemis", null, "is*", "view", "update", "amq");
+ accessControlList.addToRoleAccess("org.apache.activemq.artemis", null, "set*", "update", "amq");
+ accessControlList.addToRoleAccess("org.apache.activemq.artemis", null, WILDCARD, "amq");
accessControlList.addToDefaultAccess("list*", "view", "update", "amq");
accessControlList.addToDefaultAccess("get*", "view", "update", "amq");
accessControlList.addToDefaultAccess("is*", "view", "update", "amq");
accessControlList.addToDefaultAccess("set*", "update", "amq");
- accessControlList.addToDefaultAccess("*", "amq");
+ accessControlList.addToDefaultAccess(WILDCARD, "amq");
return accessControlList;
}
diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/JMXAccessControlListTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/JMXAccessControlListTest.java
index 9d91f83..e0b0233 100644
--- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/JMXAccessControlListTest.java
+++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/JMXAccessControlListTest.java
@@ -96,6 +96,16 @@ public class JMXAccessControlListTest {
}
@Test
+ public void testBasicRoleWithWildcardInKey() throws MalformedObjectNameException {
+ JMXAccessControlList controlList = new JMXAccessControlList();
+ controlList.addToRoleAccess("org.myDomain", "type=foo*","listSomething", "update");
+ controlList.addToRoleAccess("org.myDomain", "type=foo.bar*","listSomething", "admin");
+ controlList.addToRoleAccess("org.myDomain", null,"listSomething", "view");
+ List<String> roles = controlList.getRolesForObject(new ObjectName("org.myDomain:type=foo.bar.test"), "listSomething");
+ Assert.assertArrayEquals(roles.toArray(), new String[]{"admin"});
+ }
+
+ @Test
public void testMutipleBasicRoles() throws MalformedObjectNameException {
JMXAccessControlList controlList = new JMXAccessControlList();
controlList.addToRoleAccess("org.myDomain", null, "listSomething", "admin", "view", "update");
diff --git a/docs/user-manual/en/management.md b/docs/user-manual/en/management.md
index 7f33da3..f3d7370 100644
--- a/docs/user-manual/en/management.md
+++ b/docs/user-manual/en/management.md
@@ -388,6 +388,19 @@ by configuring:
</match>
```
+You can also use wildcards for the mBean properties so the following would
+also match, allowing prefix match for the mBean properties.
+
+```xml
+<match domain="org.apache.activemq.artemis" key="queue=example*">
+ <access method="list*" roles="view,update,amq"/>
+ <access method="get*" roles="view,update,amq"/>
+ <access method="is*" roles="view,update,amq"/>
+ <access method="set*" roles="update,amq"/>
+ <access method="*" roles="amq"/>
+</match>
+```
+
Access to JMX mBean attributes are converted to method calls so these are
controlled via the `set*`, `get*` and `is*`. The `*` access is the catch all
for everything other method that isn't specifically matched.