This is an automated email from the ASF dual-hosted git repository.
vavrtom pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/qpid-broker-j.git
The following commit(s) were added to refs/heads/main by this push:
new 0b91ded QPID-8488: [Broker-J] Enhance ACL rule with multi-value
properties
0b91ded is described below
commit 0b91ded6e2c4587ff4ce2324636e2e5f22645295
Author: Marek Laca <[email protected]>
AuthorDate: Wed Feb 23 07:58:41 2022 +0100
QPID-8488: [Broker-J] Enhance ACL rule with multi-value properties
This closes #116
---
.../security/access/config/AclFileParser.java | 46 +-
.../security/access/config/AclRulePredicates.java | 34 +-
.../access/config/AclRulePredicatesBuilder.java | 112 ++-
.../qpid/server/security/access/config/Rule.java | 11 +-
.../access/config/predicates/MultiValue.java | 54 ++
.../config/predicates/RulePredicateBuilder.java | 9 +-
...stractCommonRuleBasedAccessControlProvider.java | 26 +-
.../server/security/access/plugins/AclRule.java | 6 +-
.../security/access/util/AbstractTreeBranch.java | 98 +++
.../qpid/server/security/access/util/Any.java | 59 ++
.../qpid/server/security/access/util/Empty.java | 57 ++
.../server/security/access/util/FinalBranch.java | 70 ++
.../server/security/access/util/PrefixTree.java | 111 +++
.../server/security/access/util/PrefixTreeSet.java | 33 +
.../server/security/access/util/TreeBranch.java | 236 ++++++
.../qpid/server/security/access/util/TreeRoot.java | 76 ++
.../security/access/util/WildCardBranch.java | 102 +++
.../server/security/access/util/WildCardSet.java | 48 ++
.../access/config/AclRulePredicatesTest.java | 43 +-
.../predicates/RulePredicateBuilderTest.java | 67 ++
.../config/predicates/RulePredicateTest.java | 57 ++
.../security/access/util/PrefixTreeTest.java | 942 +++++++++++++++++++++
.../management/accesscontrolprovider/RuleBased.js | 18 +-
...Java-Broker-Security-AccessControlProviders.xml | 17 +-
24 files changed, 2251 insertions(+), 81 deletions(-)
diff --git
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclFileParser.java
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclFileParser.java
index a964af7..bb1c54c 100644
---
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclFileParser.java
+++
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclFileParser.java
@@ -32,26 +32,30 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayDeque;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Queue;
+import java.util.Set;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.logging.EventLoggerProvider;
import org.apache.qpid.server.security.Result;
import org.apache.qpid.server.security.access.config.Rule.Builder;
import org.apache.qpid.server.security.access.plugins.RuleOutcome;
+import com.google.common.collect.Iterables;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
public final class AclFileParser
{
private static final Logger LOGGER =
LoggerFactory.getLogger(AclFileParser.class);
@@ -81,6 +85,8 @@ public final class AclFileParser
static final String PROPERTY_NO_VALUE_MSG = "Incomplete property (no
value) at line %d";
static final String GROUP_NOT_SUPPORTED = "GROUP keyword not supported at
line %d." +
" Groups should defined via a Group Provider, not in the ACL
file.";
+ static final String PROPERTY_NO_CLOSE_BRACKET_MSG = "Incomplete property
(no close bracket) at line %d";
+
private static final String INVALID_ENUM = "Not a valid %s: %s";
private static final String INVALID_URL = "Cannot convert %s to a readable
resource";
@@ -322,7 +328,8 @@ public final class AclFileParser
while (i.hasNext())
{
final String key = i.next().toLowerCase(Locale.ENGLISH);
- final Boolean value = Boolean.valueOf(readValue(i, line));
+ final Set<String> values = readValue(i, line);
+ final Boolean value =
Boolean.valueOf(Iterables.getOnlyElement(values));
if (Boolean.TRUE.equals(value))
{
@@ -343,7 +350,7 @@ public final class AclFileParser
}
}
- private static String readValue(Iterator<String> tokenIterator, int line)
+ private static Set<String> readValue(Iterator<String> tokenIterator, int
line)
{
if (!tokenIterator.hasNext())
{
@@ -357,7 +364,27 @@ public final class AclFileParser
{
throw new
IllegalConfigurationException(String.format(PROPERTY_NO_VALUE_MSG, line));
}
- return tokenIterator.next();
+ final String value = tokenIterator.next();
+ if ("[".equals(value))
+ {
+ final Set<String> values = new HashSet<>();
+ String next;
+ while (tokenIterator.hasNext())
+ {
+ next = tokenIterator.next();
+ if (AclRulePredicates.SEPARATOR.equals(next))
+ {
+ continue;
+ }
+ if ("]".equals(next))
+ {
+ return values;
+ }
+ values.add(next);
+ }
+ throw new
IllegalConfigurationException(String.format(PROPERTY_NO_CLOSE_BRACKET_MSG,
line));
+ }
+ return Collections.singleton(value);
}
private RuleOutcome parsePermission(final String text, final int line)
@@ -381,11 +408,8 @@ public final class AclFileParser
final String typeDescription)
{
return Optional.ofNullable(map.get(text.toUpperCase(Locale.ENGLISH)))
- .orElseThrow(() -> new
IllegalConfigurationException(String.format(PARSE_TOKEN_FAILED_MSG, line),
-
new IllegalArgumentException(String.format(
-
INVALID_ENUM,
-
typeDescription,
-
text))));
+ .orElseThrow(() -> new
IllegalConfigurationException(String.format(PARSE_TOKEN_FAILED_MSG, line),
+ new
IllegalArgumentException(String.format(INVALID_ENUM, typeDescription, text))));
}
private Reader getReaderFromURLString(String urlString)
diff --git
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclRulePredicates.java
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclRulePredicates.java
index de222fd..d1ad844 100644
---
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclRulePredicates.java
+++
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclRulePredicates.java
@@ -22,6 +22,7 @@ import java.util.AbstractMap;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
+import java.util.EnumSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -37,7 +38,10 @@ import com.google.common.collect.Iterables;
public final class AclRulePredicates extends AbstractMap<Property, Set<Object>>
implements RulePredicate
{
- static final String SEPARATOR = ",";
+ public static final String SEPARATOR = ",";
+
+ private static final Set<Property> JOIN_PROPERTIES =
+ EnumSet.of(Property.ATTRIBUTES, Property.FROM_HOSTNAME,
Property.FROM_NETWORK);
private final Map<Property, Set<Object>> _properties;
@@ -64,9 +68,9 @@ public final class AclRulePredicates extends
AbstractMap<Property, Set<Object>>
_rulePredicate =
Objects.requireNonNull(builder.newRulePredicate(factory));
}
- public Map<Property, String> getParsedProperties()
+ public Map<Property, Object> getParsedProperties()
{
- final Map<Property, String> parsed = new EnumMap<>(Property.class);
+ final Map<Property, Object> parsed = new EnumMap<>(Property.class);
for (final Map.Entry<Property, Set<Object>> entry :
_properties.entrySet())
{
final Set<Object> values = entry.getValue();
@@ -76,16 +80,30 @@ public final class AclRulePredicates extends
AbstractMap<Property, Set<Object>>
}
else
{
- parsed.put(entry.getKey(),
- values.stream()
- .map(Object::toString)
- .sorted()
- .collect(Collectors.joining(SEPARATOR)));
+ parsed.put(entry.getKey(), collect(entry.getKey(), values));
}
}
return parsed;
}
+ private Object collect(Property property, Set<Object> values)
+ {
+ if (JOIN_PROPERTIES.contains(property))
+ {
+ return values.stream()
+ .map(Object::toString)
+ .sorted()
+ .collect(Collectors.joining(SEPARATOR));
+ }
+ else
+ {
+ return values.stream()
+ .map(Object::toString)
+ .sorted()
+ .collect(Collectors.toList());
+ }
+ }
+
@Override
public boolean matches(LegacyOperation operation, ObjectProperties
objectProperties, Subject subject)
{
diff --git
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclRulePredicatesBuilder.java
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclRulePredicatesBuilder.java
index bd9d57a..7a6054d 100644
---
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclRulePredicatesBuilder.java
+++
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclRulePredicatesBuilder.java
@@ -19,6 +19,7 @@
package org.apache.qpid.server.security.access.config;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashSet;
@@ -29,6 +30,8 @@ import java.util.Set;
import
org.apache.qpid.server.security.access.config.predicates.RulePredicateBuilder;
import org.apache.qpid.server.security.access.firewall.FirewallRuleFactory;
+import org.apache.qpid.server.security.access.util.PrefixTreeSet;
+import org.apache.qpid.server.security.access.util.WildCardSet;
import com.google.common.collect.ImmutableSet;
import org.slf4j.Logger;
@@ -63,7 +66,7 @@ public final class AclRulePredicatesBuilder
{
for (final Map.Entry<Property, ?> entry : values.entrySet())
{
- addPropertyValue(entry.getKey(),
Objects.toString(entry.getValue(), null));
+ addPropertyValues(entry.getKey(), entry.getValue());
}
}
}
@@ -71,16 +74,32 @@ public final class AclRulePredicatesBuilder
public AclRulePredicatesBuilder()
{
super();
- for (final Property property : Property.values())
- {
- _parsedProperties.put(property, Collections.emptySet());
- }
_hostNames = new HashSet<>();
- _parsedProperties.put(Property.FROM_HOSTNAME, _hostNames);
_networks = new HashSet<>();
- _parsedProperties.put(Property.FROM_NETWORK, _networks);
_attributeNames = new HashSet<>();
- _parsedProperties.put(Property.ATTRIBUTES, _attributeNames);
+ for (final Property property : Property.values())
+ {
+ if (property == Property.FROM_HOSTNAME)
+ {
+ _parsedProperties.put(property, _hostNames);
+ }
+ else if (property == Property.FROM_NETWORK)
+ {
+ _parsedProperties.put(property, _networks);
+ }
+ else if (property == Property.ATTRIBUTES)
+ {
+ _parsedProperties.put(property, _attributeNames);
+ }
+ else if (Property.isBooleanType(property))
+ {
+ _parsedProperties.put(property, new HashSet<>());
+ }
+ else
+ {
+ _parsedProperties.put(property, new PrefixTreeSet());
+ }
+ }
}
public AclRulePredicates build()
@@ -103,22 +122,50 @@ public final class AclRulePredicatesBuilder
return this;
}
+ public AclRulePredicatesBuilder parse(String key, Set<String> values)
+ {
+ final Property property = Property.parse(key);
+ for (final String value : values)
+ {
+ if (addPropertyValue(property, value))
+ {
+ LOGGER.debug("Parsed {} with value {}", property, value);
+ }
+ }
+ return this;
+ }
+
public AclRulePredicatesBuilder put(Property property, String value)
{
addPropertyValue(property, value);
return this;
}
- private boolean addPropertyValue(final Property property, final String
value)
+ private void addPropertyValues(Property property, Object value)
+ {
+ if (value instanceof Collection)
+ {
+ for (final Object v : (Collection<?>) value)
+ {
+ addPropertyValues(property, v);
+ }
+ }
+ else
+ {
+ addPropertyValue(property, Objects.toString(value, null));
+ }
+ }
+
+ private boolean addPropertyValue(Property property, String value)
{
if (property == Property.FROM_HOSTNAME)
{
- checkFirewallRuleNotAlreadyDefined(property, value,
Property.FROM_NETWORK);
+ checkFirewallRule(property, value, Property.FROM_NETWORK);
_hostNames.addAll(splitToSet(value));
}
else if (property == Property.FROM_NETWORK)
{
- checkFirewallRuleNotAlreadyDefined(property, value,
Property.FROM_HOSTNAME);
+ checkFirewallRule(property, value, Property.FROM_HOSTNAME);
_networks.addAll(splitToSet(value));
}
else if (property == Property.ATTRIBUTES)
@@ -137,27 +184,40 @@ public final class AclRulePredicatesBuilder
}
else
{
- _parsedProperties.put(property,
Collections.singleton(sanitiseValue(property, value)));
+ addPropertyValueImpl(property, value);
}
return true;
}
- private Object sanitiseValue(Property property, String value)
+ private void addPropertyValueImpl(Property property, String value)
{
if (value == null)
{
- return WILD_CARD;
+ _parsedProperties.put(property, WildCardSet.newSet());
+ return;
}
value = value.trim();
if (value.isEmpty() || WILD_CARD.equals(value))
{
- return WILD_CARD;
+ _parsedProperties.put(property, WildCardSet.newSet());
+ return;
}
+ final Set<?> values = _parsedProperties.get(property);
if (Property.isBooleanType(property))
{
- return parseBoolean(value);
+ ((Set<Object>) values).add(parseBoolean(value));
+ }
+ else
+ {
+ if (values instanceof PrefixTreeSet)
+ {
+ ((PrefixTreeSet) values).add(value);
+ }
+ else
+ {
+ ((Set<Object>) values).add(value);
+ }
}
- return value;
}
private Boolean parseBoolean(String value)
@@ -178,14 +238,17 @@ public final class AclRulePredicatesBuilder
return Boolean.parseBoolean(value);
}
- private HashSet<String> splitToSet(String value)
+ private Set<String> splitToSet(String value)
{
+ if (value == null)
+ {
+ return Collections.emptySet();
+ }
return new
HashSet<>(Arrays.asList(value.split(AclRulePredicates.SEPARATOR)));
}
- private void checkFirewallRuleNotAlreadyDefined(Property property, String
value, Property exclusiveProperty)
+ private void checkFirewallRule(Property property, String value, Property
exclusiveProperty)
{
- checkPropertyAlreadyDefined(property);
if (!_parsedProperties.get(exclusiveProperty).isEmpty())
{
throw new IllegalStateException(
@@ -195,15 +258,6 @@ public final class AclRulePredicatesBuilder
}
}
- private void checkPropertyAlreadyDefined(Property property)
- {
- if (!_parsedProperties.get(property).isEmpty())
- {
- throw new IllegalStateException(String.format("Property '%s' has
already been defined",
- property.toString().toLowerCase(Locale.ENGLISH)));
- }
- }
-
Map<Property, Set<Object>> newProperties()
{
final Map<Property, Set<Object>> properties = new
EnumMap<>(Property.class);
diff --git
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Rule.java
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Rule.java
index 13c9874..d7d3b66 100644
---
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Rule.java
+++
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Rule.java
@@ -22,6 +22,7 @@ package org.apache.qpid.server.security.access.config;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import javax.security.auth.Subject;
@@ -138,7 +139,7 @@ public class Rule
return _object;
}
- public Map<Property, String> getAttributes()
+ public Map<Property, Object> getAttributes()
{
return _predicates.getParsedProperties();
}
@@ -213,7 +214,7 @@ public class Rule
}
@Override
- public Map<Property, String> getAttributes()
+ public Map<Property, Object> getAttributes()
{
return _rule.getAttributes();
}
@@ -276,6 +277,12 @@ public class Rule
return this;
}
+ public Builder withPredicate(String key, Set<String> values)
+ {
+ _aclRulePredicatesBuilder.parse(key, values);
+ return this;
+ }
+
public Builder withOwner()
{
_identity = OWNER;
diff --git
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/predicates/MultiValue.java
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/predicates/MultiValue.java
new file mode 100644
index 0000000..fa766ba
--- /dev/null
+++
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/predicates/MultiValue.java
@@ -0,0 +1,54 @@
+/*
+ * 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.qpid.server.security.access.config.predicates;
+
+import java.util.Objects;
+
+import javax.security.auth.Subject;
+
+import org.apache.qpid.server.security.access.config.LegacyOperation;
+import org.apache.qpid.server.security.access.config.ObjectProperties;
+import org.apache.qpid.server.security.access.config.Property;
+import org.apache.qpid.server.security.access.config.RulePredicate;
+import org.apache.qpid.server.security.access.util.PrefixTree;
+
+class MultiValue implements RulePredicate
+{
+ private final Property _property;
+
+ private final PrefixTree _tree;
+
+ static RulePredicate newInstance(Property property, PrefixTree tree)
+ {
+ return tree == null ? RulePredicate.any() : new MultiValue(property,
tree);
+ }
+
+ private MultiValue(Property property, PrefixTree tree)
+ {
+ _property = Objects.requireNonNull(property);
+ _tree = Objects.requireNonNull(tree);
+ }
+
+ @Override
+ public boolean matches(LegacyOperation operation, ObjectProperties
objectProperties, Subject subject)
+ {
+ final Object value = objectProperties.get(_property);
+ return (value instanceof String) && _tree.match((String) value);
+ }
+}
diff --git
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/predicates/RulePredicateBuilder.java
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/predicates/RulePredicateBuilder.java
index b7c5332..410066d 100644
---
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/predicates/RulePredicateBuilder.java
+++
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/predicates/RulePredicateBuilder.java
@@ -28,6 +28,7 @@ import
org.apache.qpid.server.security.access.config.AclRulePredicatesBuilder;
import org.apache.qpid.server.security.access.config.Property;
import org.apache.qpid.server.security.access.config.RulePredicate;
import org.apache.qpid.server.security.access.firewall.FirewallRuleFactory;
+import org.apache.qpid.server.security.access.util.PrefixTreeSet;
public final class RulePredicateBuilder
{
@@ -74,13 +75,17 @@ public final class RulePredicateBuilder
}
}
- private Set<String> toSet(Collection<?> hostnames)
+ private Set<String> toSet(Collection<?> values)
{
- return
hostnames.stream().map(Object::toString).collect(Collectors.toSet());
+ return
values.stream().map(Object::toString).collect(Collectors.toSet());
}
private RulePredicate buildGenericPredicate(Property property,
Collection<?> values)
{
+ if (values instanceof PrefixTreeSet && values.size() > 2)
+ {
+ return MultiValue.newInstance(property, ((PrefixTreeSet)
values).toPrefixTree());
+ }
RulePredicate predicate = RulePredicate.none();
for (final Object value : values)
{
diff --git
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AbstractCommonRuleBasedAccessControlProvider.java
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AbstractCommonRuleBasedAccessControlProvider.java
index f5a888b..a831907 100644
---
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AbstractCommonRuleBasedAccessControlProvider.java
+++
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AbstractCommonRuleBasedAccessControlProvider.java
@@ -29,10 +29,12 @@ import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
import org.apache.qpid.server.logging.EventLoggerProvider;
import org.apache.qpid.server.model.CommonAccessControlProvider;
@@ -139,13 +141,25 @@ abstract class
AbstractCommonRuleBasedAccessControlProvider<X extends AbstractCo
.append(rule.getOperation().name())
.append(' ')
.append(rule.getObjectType().name());
- for (final Map.Entry<Property, String> entry :
rule.getAttributes().entrySet())
+ for (final Map.Entry<Property, Object> entry :
rule.getAttributes().entrySet())
{
- sb.append(' ')
- .append(entry.getKey().getCanonicalName())
- .append("=\"")
- .append(entry.getValue())
- .append("\"");
+ if (entry.getValue() == null)
+ {
+ continue;
+ }
+ sb.append(' ').append(entry.getKey().getCanonicalName());
+
+ if (entry.getValue() instanceof Collection)
+ {
+ final Collection<?> values = (Collection<?>) entry.getValue();
+ sb.append("=[\"")
+
.append(values.stream().map(Object::toString).collect(Collectors.joining("\",\"")))
+ .append("\"]");
+ }
+ else
+ {
+ sb.append("=\"").append(entry.getValue()).append("\"");
+ }
}
}
diff --git
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AclRule.java
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AclRule.java
index bb1bf1e..7451c01 100644
---
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AclRule.java
+++
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AclRule.java
@@ -32,8 +32,12 @@ import
org.apache.qpid.server.security.access.config.Property;
public interface AclRule extends ManagedAttributeValue
{
String getIdentity();
+
ObjectType getObjectType();
+
LegacyOperation getOperation();
- Map<Property,String> getAttributes();
+
+ Map<Property, Object> getAttributes();
+
RuleOutcome getOutcome();
}
diff --git
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/AbstractTreeBranch.java
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/AbstractTreeBranch.java
new file mode 100644
index 0000000..7b489d1
--- /dev/null
+++
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/AbstractTreeBranch.java
@@ -0,0 +1,98 @@
+/*
+ * 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.qpid.server.security.access.util;
+
+import java.util.Objects;
+
+abstract class AbstractTreeBranch implements PrefixTree
+{
+ private static final String NO_PREFIX = "Tree root does not have a prefix";
+ private static final String EMPTY_INPUT_PREFIX = "The input prefix can not
be empty";
+ private static final String EMPTY_INPUT_STRING = "The input string can not
be empty";
+
+ final String _prefix;
+
+ final int _length;
+
+ abstract AbstractTreeBranch mergeString(String str);
+
+ abstract AbstractTreeBranch mergeWildCard(String prefix);
+
+ abstract boolean contains(String str);
+
+ AbstractTreeBranch(String prefix)
+ {
+ super();
+ _prefix = Objects.requireNonNull(prefix, "The prefix can not be
null!");
+ _length = _prefix.length();
+ }
+
+ AbstractTreeBranch()
+ {
+ super();
+ _prefix = "";
+ _length = 0;
+ }
+
+ @Override
+ public String prefix()
+ {
+ return _prefix;
+ }
+
+ @Override
+ public char firstPrefixCharacter()
+ {
+ if (_prefix.isEmpty())
+ {
+ throw new UnsupportedOperationException(NO_PREFIX);
+ }
+ return _prefix.charAt(0);
+ }
+
+ @Override
+ public PrefixTree mergeWithFinalValue(String str)
+ {
+ if (str == null || str.isEmpty())
+ {
+ throw new IllegalArgumentException(EMPTY_INPUT_STRING);
+ }
+ return mergeString(str);
+ }
+
+ @Override
+ public PrefixTree mergeWithPrefix(String prefix)
+ {
+ if (prefix == null || prefix.isEmpty())
+ {
+ throw new IllegalArgumentException(EMPTY_INPUT_PREFIX);
+ }
+ return mergeWildCard(prefix);
+ }
+
+ @Override
+ public boolean match(String str)
+ {
+ if (str == null || str.length() == 0)
+ {
+ return false;
+ }
+ return contains(str);
+ }
+}
diff --git
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/Any.java
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/Any.java
new file mode 100644
index 0000000..9b60ca2
--- /dev/null
+++
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/Any.java
@@ -0,0 +1,59 @@
+package org.apache.qpid.server.security.access.util;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+import com.google.common.collect.Iterators;
+
+final class Any extends AbstractTreeBranch
+{
+ static Any INSTANCE = new Any();
+
+ private Any()
+ {
+ super();
+ }
+
+ @Override
+ AbstractTreeBranch mergeString(String str)
+ {
+ return this;
+ }
+
+ @Override
+ AbstractTreeBranch mergeWildCard(String prefix)
+ {
+ return this;
+ }
+
+ @Override
+ public boolean match(String str)
+ {
+ return str != null;
+ }
+
+ @Override
+ boolean contains(String str)
+ {
+ return true;
+ }
+
+ @Override
+ public Map<Character, PrefixTree> branches()
+ {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public int size()
+ {
+ return 1;
+ }
+
+ @Override
+ public Iterator<String> iterator()
+ {
+ return Iterators.singletonIterator("*");
+ }
+}
diff --git
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/Empty.java
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/Empty.java
new file mode 100644
index 0000000..cc0fc2c
--- /dev/null
+++
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/Empty.java
@@ -0,0 +1,57 @@
+package org.apache.qpid.server.security.access.util;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+final class Empty extends AbstractTreeBranch
+{
+ static Empty INSTANCE = new Empty();
+
+ private Empty()
+ {
+ super();
+ }
+
+ @Override
+ public Map<Character, PrefixTree> branches()
+ {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public int size()
+ {
+ return 0;
+ }
+
+ @Override
+ public boolean match(String str)
+ {
+ return false;
+ }
+
+ @Override
+ AbstractTreeBranch mergeString(String str)
+ {
+ return new FinalBranch(str);
+ }
+
+ @Override
+ AbstractTreeBranch mergeWildCard(String prefix)
+ {
+ return new WildCardBranch(prefix);
+ }
+
+ @Override
+ boolean contains(String str)
+ {
+ return false;
+ }
+
+ @Override
+ public Iterator<String> iterator()
+ {
+ return Collections.emptyIterator();
+ }
+}
diff --git
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/FinalBranch.java
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/FinalBranch.java
new file mode 100644
index 0000000..913d107
--- /dev/null
+++
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/FinalBranch.java
@@ -0,0 +1,70 @@
+/*
+ * 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.qpid.server.security.access.util;
+
+import java.util.Iterator;
+import java.util.Map;
+
+final class FinalBranch extends TreeBranch
+{
+ FinalBranch(String prefix)
+ {
+ super(prefix);
+ }
+
+ FinalBranch(String prefix, Map<Character, AbstractTreeBranch> branches)
+ {
+ super(prefix, branches);
+ }
+
+ FinalBranch(String prefix, AbstractTreeBranch first)
+ {
+ super(prefix, first);
+ }
+
+ @Override
+ public int size()
+ {
+ return 1 + super.size();
+ }
+
+ @Override
+ boolean contains(String str)
+ {
+ return _prefix.equals(str) || super.contains(str);
+ }
+
+ @Override
+ public Iterator<String> iterator()
+ {
+ return new IteratorImpl(this, "");
+ }
+
+ @Override
+ FinalBranch newFinalBranch()
+ {
+ return this;
+ }
+
+ @Override
+ TreeBranch newTree(String substring, Map<Character, AbstractTreeBranch>
branches)
+ {
+ return new FinalBranch(substring, branches);
+ }
+}
diff --git
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/PrefixTree.java
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/PrefixTree.java
new file mode 100644
index 0000000..331467f
--- /dev/null
+++
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/PrefixTree.java
@@ -0,0 +1,111 @@
+/*
+ * 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.qpid.server.security.access.util;
+
+import java.util.Collection;
+import java.util.Map;
+
+public interface PrefixTree extends Iterable<String>
+{
+ String prefix();
+
+ char firstPrefixCharacter();
+
+ Map<Character, PrefixTree> branches();
+
+ int size();
+
+ boolean match(String str);
+
+ default PrefixTree mergeWith(String str)
+ {
+ if (str == null || str.isEmpty())
+ {
+ throw new IllegalArgumentException("Prefix tree can not be merged
with an empty value");
+ }
+ if ("*".equals(str))
+ {
+ return Any.INSTANCE;
+ }
+ if (str.endsWith("*"))
+ {
+ return mergeWithPrefix(str.substring(0, str.length() - 1));
+ }
+ return mergeWithFinalValue(str);
+ }
+
+ default PrefixTree mergeWith(Collection<String> collection)
+ {
+ PrefixTree tree = this;
+ for (final String str : collection)
+ {
+ tree = tree.mergeWith(str);
+ }
+ return tree;
+ }
+
+ PrefixTree mergeWithFinalValue(String str);
+
+ PrefixTree mergeWithPrefix(String prefix);
+
+ static PrefixTree empty()
+ {
+ return Empty.INSTANCE;
+ }
+
+ static PrefixTree from(String str)
+ {
+ if (str == null || str.isEmpty())
+ {
+ throw new IllegalArgumentException("A non null string is
required");
+ }
+ if ("*".equals(str))
+ {
+ return Any.INSTANCE;
+ }
+ if (str.endsWith("*"))
+ {
+ return fromPrefixWithWildCard(str.substring(0, str.length() - 1));
+ }
+ return fromFinalValue(str);
+ }
+
+ static PrefixTree from(Collection<String> collection)
+ {
+ return empty().mergeWith(collection);
+ }
+
+ static PrefixTree fromFinalValue(String value)
+ {
+ if (value == null || value.isEmpty())
+ {
+ throw new IllegalArgumentException("A non empty value is
required");
+ }
+ return new FinalBranch(value);
+ }
+
+ static PrefixTree fromPrefixWithWildCard(String prefix)
+ {
+ if (prefix == null || prefix.isEmpty())
+ {
+ throw new IllegalArgumentException("A non empty prefix is
required");
+ }
+ return new WildCardBranch(prefix);
+ }
+}
diff --git
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/PrefixTreeSet.java
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/PrefixTreeSet.java
new file mode 100644
index 0000000..1a5fafa
--- /dev/null
+++
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/PrefixTreeSet.java
@@ -0,0 +1,33 @@
+package org.apache.qpid.server.security.access.util;
+
+import java.util.AbstractSet;
+import java.util.Iterator;
+
+public class PrefixTreeSet extends AbstractSet<String>
+{
+ private PrefixTree _tree = PrefixTree.empty();
+
+ @Override
+ public boolean add(String s)
+ {
+ _tree = _tree.mergeWith(s);
+ return true;
+ }
+
+ @Override
+ public Iterator<String> iterator()
+ {
+ return _tree.iterator();
+ }
+
+ @Override
+ public int size()
+ {
+ return _tree.size();
+ }
+
+ public PrefixTree toPrefixTree()
+ {
+ return _tree;
+ }
+}
diff --git
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/TreeBranch.java
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/TreeBranch.java
new file mode 100644
index 0000000..529daa2
--- /dev/null
+++
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/TreeBranch.java
@@ -0,0 +1,236 @@
+/*
+ * 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.qpid.server.security.access.util;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Iterators;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+class TreeBranch extends AbstractTreeBranch
+{
+ final Map<Character, AbstractTreeBranch> _branches;
+
+ TreeBranch(String prefix)
+ {
+ super(prefix);
+ _branches = Collections.emptyMap();
+ }
+
+ TreeBranch(Map<Character, AbstractTreeBranch> branches)
+ {
+ super();
+ _branches = new HashMap<>(branches);
+ }
+
+ TreeBranch(String prefix, Map<Character, AbstractTreeBranch> branches)
+ {
+ super(prefix);
+ _branches = new HashMap<>(branches);
+ }
+
+ TreeBranch(String prefix, AbstractTreeBranch first)
+ {
+ super(prefix);
+ _branches = Collections.singletonMap(first.firstPrefixCharacter(),
first);
+ }
+
+ TreeBranch(String prefix, AbstractTreeBranch first, AbstractTreeBranch
second)
+ {
+ super(prefix);
+ _branches = new HashMap<>(2);
+ _branches.put(first.firstPrefixCharacter(), first);
+ _branches.put(second.firstPrefixCharacter(), second);
+ }
+
+ TreeBranch(AbstractTreeBranch first, AbstractTreeBranch second)
+ {
+ super();
+ _branches = new HashMap<>(2);
+ _branches.put(first.firstPrefixCharacter(), first);
+ _branches.put(second.firstPrefixCharacter(), second);
+ }
+
+ @Override
+ public Map<Character, PrefixTree> branches()
+ {
+ return Collections.unmodifiableMap(_branches);
+ }
+
+ @Override
+ public int size()
+ {
+ return _branches.values().stream().mapToInt(PrefixTree::size).sum();
+ }
+
+ @Override
+ boolean contains(String str)
+ {
+ final int length = str.length();
+ if (length > _length && str.startsWith(_prefix))
+ {
+ final String subString = str.substring(_length, length);
+ final PrefixTree subTree = _branches.get(subString.charAt(0));
+ if (subTree != null)
+ {
+ return subTree.match(subString);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Iterator<String> iterator()
+ {
+ return new IteratorImpl(this);
+ }
+
+ @Override
+ AbstractTreeBranch mergeString(String str)
+ {
+ final String common = Strings.commonPrefix(str, _prefix);
+ if (common.isEmpty())
+ {
+ return new TreeRoot(this, new FinalBranch(str));
+ }
+ final int commonLength = common.length();
+ if (commonLength == str.length())
+ {
+ if (commonLength == _length)
+ {
+ return newFinalBranch();
+ }
+ return new FinalBranch(str,
newTree(_prefix.substring(commonLength), _branches));
+ }
+ if (commonLength == _length)
+ {
+ final Map<Character, AbstractTreeBranch> branches = new
HashMap<>(_branches);
+ final String subString = str.substring(commonLength);
+ final char key = subString.charAt(0);
+ final AbstractTreeBranch branch = branches.get(key);
+ if (branch != null)
+ {
+ branches.put(key, branch.mergeString(subString));
+ }
+ else
+ {
+ branches.put(key, new FinalBranch(subString));
+ }
+ return newTree(common, branches);
+ }
+
+ return new TreeBranch(common,
+ newTree(_prefix.substring(commonLength), _branches),
+ new FinalBranch(str.substring(commonLength)));
+ }
+
+ @Override
+ AbstractTreeBranch mergeWildCard(String prefix)
+ {
+ final String common = Strings.commonPrefix(prefix, _prefix);
+ if (common.isEmpty())
+ {
+ return new TreeRoot(this, new WildCardBranch(prefix));
+ }
+ final int commonLength = common.length();
+ if (commonLength == prefix.length())
+ {
+ return new WildCardBranch(prefix);
+ }
+ if (commonLength == _length)
+ {
+ final Map<Character, AbstractTreeBranch> branches = new
HashMap<>(_branches);
+ final String subString = prefix.substring(commonLength);
+ final char key = subString.charAt(0);
+ final AbstractTreeBranch branch = branches.get(key);
+ if (branch != null)
+ {
+ branches.put(key, branch.mergeWildCard(subString));
+ }
+ else
+ {
+ branches.put(key, new WildCardBranch(subString));
+ }
+ return newTree(_prefix, branches);
+ }
+
+ return new TreeBranch(common,
+ newTree(_prefix.substring(commonLength), _branches),
+ new WildCardBranch(prefix.substring(commonLength)));
+ }
+
+ FinalBranch newFinalBranch()
+ {
+ return new FinalBranch(_prefix, _branches);
+ }
+
+ TreeBranch newTree(String prefix, Map<Character, AbstractTreeBranch>
branches)
+ {
+ return new TreeBranch(prefix, branches);
+ }
+
+ static final class IteratorImpl implements Iterator<String>
+ {
+ private final String _prefix;
+
+ private final Iterator<AbstractTreeBranch> _tree;
+
+ private Iterator<String> _branch;
+
+ IteratorImpl(TreeBranch root)
+ {
+ _prefix = root.prefix();
+ _tree = new TreeMap<>(root._branches).values().iterator();
+ _branch = Collections.emptyIterator();
+ }
+
+ IteratorImpl(TreeBranch root, String firstValue)
+ {
+ _prefix = root.prefix();
+ _tree = new TreeMap<>(root._branches).values().iterator();
+ _branch = Iterators.singletonIterator(firstValue);
+ }
+
+ @Override
+ public boolean hasNext()
+ {
+ boolean hasBranch = _branch.hasNext();
+ while (!hasBranch && _tree.hasNext())
+ {
+ _branch = _tree.next().iterator();
+ hasBranch = _branch.hasNext();
+ }
+ return hasBranch;
+ }
+
+ @Override
+ public String next()
+ {
+ while (!_branch.hasNext() && _tree.hasNext())
+ {
+ _branch = _tree.next().iterator();
+ }
+ return _prefix + _branch.next();
+ }
+ }
+}
diff --git
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/TreeRoot.java
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/TreeRoot.java
new file mode 100644
index 0000000..19235a8
--- /dev/null
+++
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/TreeRoot.java
@@ -0,0 +1,76 @@
+/*
+ * 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.qpid.server.security.access.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+final class TreeRoot extends TreeBranch
+{
+ TreeRoot(AbstractTreeBranch first, AbstractTreeBranch second)
+ {
+ super(first, second);
+ }
+
+ TreeRoot(Map<Character, AbstractTreeBranch> map)
+ {
+ super(map);
+ }
+
+ @Override
+ boolean contains(String str)
+ {
+ final PrefixTree subTree = _branches.get(str.charAt(0));
+ return subTree != null && subTree.match(str);
+ }
+
+ @Override
+ AbstractTreeBranch mergeString(String str)
+ {
+ final Map<Character, AbstractTreeBranch> branches = new
HashMap<>(_branches);
+ final char key = str.charAt(0);
+ final AbstractTreeBranch branch = _branches.get(key);
+ if (branch == null)
+ {
+ branches.put(key, new FinalBranch(str));
+ }
+ else
+ {
+ branches.put(key, branch.mergeString(str));
+ }
+ return new TreeRoot(branches);
+ }
+
+ @Override
+ AbstractTreeBranch mergeWildCard(String prefix)
+ {
+ final Map<Character, AbstractTreeBranch> branches = new
HashMap<>(_branches);
+ final char key = prefix.charAt(0);
+ final AbstractTreeBranch branch = _branches.get(key);
+ if (branch == null)
+ {
+ branches.put(key, new WildCardBranch(prefix));
+ }
+ else
+ {
+ branches.put(key, branch.mergeWildCard(prefix));
+ }
+ return new TreeRoot(branches);
+ }
+}
diff --git
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/WildCardBranch.java
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/WildCardBranch.java
new file mode 100644
index 0000000..0854cb6
--- /dev/null
+++
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/WildCardBranch.java
@@ -0,0 +1,102 @@
+/*
+ * 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.qpid.server.security.access.util;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Iterators;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+final class WildCardBranch extends AbstractTreeBranch
+{
+ WildCardBranch(String prefix)
+ {
+ super(prefix);
+ }
+
+ @Override
+ public int size()
+ {
+ return 1;
+ }
+
+ @Override
+ public Map<Character, PrefixTree> branches()
+ {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ boolean contains(String str)
+ {
+ return str.startsWith(_prefix);
+ }
+
+ @Override
+ public Iterator<String> iterator()
+ {
+ return Iterators.singletonIterator(_prefix + "*");
+ }
+
+ @Override
+ AbstractTreeBranch mergeString(String str)
+ {
+ final String common = Strings.commonPrefix(str, _prefix);
+ if (common.isEmpty())
+ {
+ return new TreeRoot(this, new FinalBranch(str));
+ }
+ final int commonLength = common.length();
+ if (commonLength == _length)
+ {
+ return this;
+ }
+ if (commonLength == str.length())
+ {
+ return new FinalBranch(common, new
WildCardBranch(_prefix.substring(commonLength)));
+ }
+ return new TreeBranch(common,
+ new WildCardBranch(_prefix.substring(commonLength)),
+ new FinalBranch(str.substring(commonLength)));
+ }
+
+ @Override
+ AbstractTreeBranch mergeWildCard(String prefix)
+ {
+ final String common = Strings.commonPrefix(prefix, _prefix);
+ if (common.isEmpty())
+ {
+ return new TreeRoot(this, new WildCardBranch(prefix));
+ }
+ final int commonLength = common.length();
+ if (commonLength == _length)
+ {
+ return this;
+ }
+ if (commonLength == prefix.length())
+ {
+ return new WildCardBranch(prefix);
+ }
+ return new TreeBranch(common,
+ new WildCardBranch(_prefix.substring(commonLength)),
+ new WildCardBranch(prefix.substring(commonLength)));
+ }
+}
diff --git
a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/WildCardSet.java
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/WildCardSet.java
new file mode 100644
index 0000000..cf76d0f
--- /dev/null
+++
b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/util/WildCardSet.java
@@ -0,0 +1,48 @@
+package org.apache.qpid.server.security.access.util;
+
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.qpid.server.security.access.config.AclRulePredicatesBuilder;
+
+import com.google.common.collect.Iterators;
+
+public class WildCardSet extends AbstractSet<Object>
+{
+ private static final WildCardSet INSTANCE = new WildCardSet();
+
+ public static WildCardSet newSet()
+ {
+ return INSTANCE;
+ }
+
+ private WildCardSet()
+ {
+ super();
+ }
+
+ @Override
+ public boolean add(Object o)
+ {
+ return false;
+ }
+
+ @Override
+ public boolean addAll(Collection<?> c)
+ {
+ return false;
+ }
+
+ @Override
+ public Iterator<Object> iterator()
+ {
+ return Iterators.singletonIterator(AclRulePredicatesBuilder.WILD_CARD);
+ }
+
+ @Override
+ public int size()
+ {
+ return 1;
+ }
+}
diff --git
a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/AclRulePredicatesTest.java
b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/AclRulePredicatesTest.java
index 48b2a8e..76dbac5 100644
---
a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/AclRulePredicatesTest.java
+++
b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/AclRulePredicatesTest.java
@@ -18,8 +18,10 @@
*/
package org.apache.qpid.server.security.access.config;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -116,21 +118,6 @@ public class AclRulePredicatesTest extends UnitTestBase
}
@Test
- public void testParseThrowsExceptionIfHostnameSpecified2Times()
- {
- _builder.parse(FROM_NETWORK.name(), "network");
- try
- {
- _builder.parse(FROM_NETWORK.name(), "network2");
- fail("Exception not thrown");
- }
- catch (IllegalStateException e)
- {
- // pass
- }
- }
-
- @Test
public void testParseAttributesRule()
{
final String attributes = "attribute1,attribute2";
@@ -163,7 +150,7 @@ public class AclRulePredicatesTest extends UnitTestBase
.parse(FROM_NETWORK.name(), "network")
.build(_firewallRuleFactory);
- final Map<Property, String> properties =
predicates.getParsedProperties();
+ final Map<Property, Object> properties =
predicates.getParsedProperties();
assertEquals(3, properties.size());
assertEquals(properties.get(NAME), "name");
@@ -171,7 +158,6 @@ public class AclRulePredicatesTest extends UnitTestBase
assertEquals(properties.get(FROM_NETWORK), "network");
}
-
@Test
public void testAttributeNames()
{
@@ -263,6 +249,29 @@ public class AclRulePredicatesTest extends UnitTestBase
}
@Test
+ public void testParse_MultiValue()
+ {
+ final String nameA = "name.A";
+ AclRulePredicates predicates = _builder.parse(NAME.name(),
nameA).build(_firewallRuleFactory);
+ assertEquals(Collections.singleton(nameA), predicates.get(NAME));
+
+ final String nameB = "name.B";
+ predicates = _builder.parse(NAME.name(),
nameB).build(_firewallRuleFactory);
+ assertEquals(new HashSet<>(Arrays.asList(nameA, nameB)),
predicates.get(NAME));
+
+ final String nameX = "name.*";
+ predicates = _builder.parse(NAME.name(),
nameX).build(_firewallRuleFactory);
+ assertEquals(Collections.singleton(nameX), predicates.get(NAME));
+
+ final String nameC = "name.C";
+ predicates = _builder.parse(NAME.name(),
nameC).build(_firewallRuleFactory);
+ assertEquals(Collections.singleton(nameX), predicates.get(NAME));
+
+ predicates = _builder.parse(NAME.name(),
"*").build(_firewallRuleFactory);
+ assertEquals(Collections.singleton("*"), predicates.get(NAME));
+ }
+
+ @Test
public void testEqualsHashCode()
{
_builder.parse(NAME.name(), "name");
diff --git
a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/predicates/RulePredicateBuilderTest.java
b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/predicates/RulePredicateBuilderTest.java
index bfbebe4..0418299 100644
---
a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/predicates/RulePredicateBuilderTest.java
+++
b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/predicates/RulePredicateBuilderTest.java
@@ -31,6 +31,8 @@ import
org.apache.qpid.server.security.access.config.ObjectProperties;
import org.apache.qpid.server.security.access.config.Property;
import org.apache.qpid.server.security.access.config.RulePredicate;
import org.apache.qpid.server.security.access.firewall.FirewallRuleFactory;
+import org.apache.qpid.server.security.access.util.PrefixTreeSet;
+import org.apache.qpid.server.security.access.util.WildCardSet;
import org.apache.qpid.server.security.auth.TestPrincipalUtils;
import org.apache.qpid.test.utils.UnitTestBase;
@@ -305,4 +307,69 @@ public class RulePredicateBuilderTest extends UnitTestBase
assertFalse(predicate.matches(LegacyOperation.PUBLISH, new
ObjectProperties(), _subject));
}
+
+ @Test
+ public void testMatch_PrefixTree()
+ {
+ final PrefixTreeSet tree = new PrefixTreeSet();
+ tree.addAll(Arrays.asList("Exchange.public.*", "Exchange.private.A",
"Exchange.private.B"));
+ final RulePredicate predicate =
_builder.build(Collections.singletonMap(NAME, tree));
+
+ ObjectProperties op = new ObjectProperties(NAME, "Exchange.private.A");
+ assertTrue(predicate.matches(LegacyOperation.PUBLISH, op, _subject));
+
+ op = new ObjectProperties(NAME, "Exchange.private.B");
+ assertTrue(predicate.matches(LegacyOperation.PUBLISH, op, _subject));
+
+ op = new ObjectProperties(NAME, "Exchange.public.ABC");
+ assertTrue(predicate.matches(LegacyOperation.PUBLISH, op, _subject));
+ }
+
+ @Test
+ public void testDoesNotMatch_PrefixTree()
+ {
+ final PrefixTreeSet tree = new PrefixTreeSet();
+ tree.addAll(Arrays.asList("Exchange.public.*", "Exchange.private.A",
"Exchange.private.B"));
+ final RulePredicate predicate =
_builder.build(Collections.singletonMap(NAME, tree));
+
+ final ObjectProperties op = new ObjectProperties(NAME,
"Exchange.private.xyz");
+ assertFalse(predicate.matches(LegacyOperation.PUBLISH, op, _subject));
+ }
+
+ @Test
+ public void testMatch_PrefixTree_single()
+ {
+ final PrefixTreeSet tree = new PrefixTreeSet();
+ tree.add("Exchange.public.*");
+ final RulePredicate predicate =
_builder.build(Collections.singletonMap(NAME, tree));
+
+ final ObjectProperties op = new ObjectProperties(NAME,
"Exchange.public.A");
+ assertTrue(predicate.matches(LegacyOperation.PUBLISH, op, _subject));
+ }
+
+ @Test
+ public void testDoesNotMatch_PrefixTree_single()
+ {
+ final PrefixTreeSet tree = new PrefixTreeSet();
+ tree.add("Exchange.public.*");
+ final RulePredicate predicate =
_builder.build(Collections.singletonMap(NAME, tree));
+
+ final ObjectProperties op = new ObjectProperties(NAME,
"Exchange.private.xyz");
+ assertFalse(predicate.matches(LegacyOperation.PUBLISH, op, _subject));
+ }
+
+ @Test
+ public void testMatch_WildcardSet()
+ {
+ final RulePredicate predicate =
_builder.build(Collections.singletonMap(NAME, WildCardSet.newSet()));
+
+ ObjectProperties op = new ObjectProperties(NAME, "Exchange.private.A");
+ assertTrue(predicate.matches(LegacyOperation.PUBLISH, op, _subject));
+
+ op = new ObjectProperties(NAME, "Exchange.private.B");
+ assertTrue(predicate.matches(LegacyOperation.PUBLISH, op, _subject));
+
+ op = new ObjectProperties(NAME, "Exchange.public.ABC");
+ assertTrue(predicate.matches(LegacyOperation.PUBLISH, op, _subject));
+ }
}
diff --git
a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/predicates/RulePredicateTest.java
b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/predicates/RulePredicateTest.java
index a9baaea..1f8478e 100644
---
a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/predicates/RulePredicateTest.java
+++
b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/predicates/RulePredicateTest.java
@@ -416,4 +416,61 @@ public class RulePredicateTest extends UnitTestBase
assertFalse(rule.matches(LegacyOperation.PUBLISH, ObjectType.EXCHANGE,
new ObjectProperties(), _subject));
assertFalse(rule.anyPropertiesMatch());
}
+
+ @Test
+ public void testMatch_Properties_MultiValue()
+ {
+ final Rule rule = _builder
+ .withPredicate(Property.ROUTING_KEY, "broadcast.public")
+ .withPredicate(Property.NAME, "broadcast.A")
+ .withPredicate(Property.NAME, "broadcast.B")
+ .withPredicate(Property.NAME, "broadcast.X.*")
+ .withOperation(LegacyOperation.PUBLISH)
+ .withObject(ObjectType.EXCHANGE)
+ .withOutcome(RuleOutcome.ALLOW)
+ .build(_firewallRuleFactory);
+
+ ObjectProperties action = new ObjectProperties();
+ action.put(Property.ROUTING_KEY, "broadcast.public");
+ action.put(Property.NAME, "broadcast.A");
+ action.put(Property.METHOD_NAME, "publish");
+
+ assertTrue(rule.matches(LegacyOperation.PUBLISH, ObjectType.EXCHANGE,
action, _subject));
+
+ action = new ObjectProperties();
+ action.put(Property.ROUTING_KEY, "broadcast.public");
+ action.put(Property.NAME, "broadcast.B");
+ action.put(Property.METHOD_NAME, "publish");
+
+ assertTrue(rule.matches(LegacyOperation.PUBLISH, ObjectType.EXCHANGE,
action, _subject));
+
+ action = new ObjectProperties();
+ action.put(Property.ROUTING_KEY, "broadcast.public");
+ action.put(Property.NAME, "broadcast.X.new");
+ action.put(Property.METHOD_NAME, "publish");
+
+ assertTrue(rule.matches(LegacyOperation.PUBLISH, ObjectType.EXCHANGE,
action, _subject));
+ }
+
+ @Test
+ public void testDoesNotMatch_Properties_MultiValue()
+ {
+ final Rule rule = _builder
+ .withPredicate(Property.ROUTING_KEY, "generic.public")
+ .withPredicate(Property.NAME, "broadcast.A")
+ .withPredicate(Property.NAME, "broadcast.B")
+ .withPredicate(Property.NAME, "broadcast.X.*")
+ .withOperation(LegacyOperation.PUBLISH)
+ .withObject(ObjectType.EXCHANGE)
+ .withOutcome(RuleOutcome.ALLOW)
+ .build(_firewallRuleFactory);
+
+ final ObjectProperties action = new ObjectProperties();
+ action.put(Property.ROUTING_KEY, "broadcast.public");
+ action.setName("broadcast");
+ action.put(Property.METHOD_NAME, "publish");
+
+ assertFalse(rule.matches(LegacyOperation.PUBLISH, ObjectType.EXCHANGE,
action, _subject));
+ assertFalse(rule.matches(LegacyOperation.PUBLISH, ObjectType.EXCHANGE,
new ObjectProperties(), _subject));
+ }
}
\ No newline at end of file
diff --git
a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/util/PrefixTreeTest.java
b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/util/PrefixTreeTest.java
new file mode 100644
index 0000000..41473c4
--- /dev/null
+++
b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/util/PrefixTreeTest.java
@@ -0,0 +1,942 @@
+/*
+ * 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.qpid.server.security.access.util;
+
+import org.apache.qpid.test.utils.UnitTestBase;
+
+import com.google.common.collect.Streams;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.stream.Collectors;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class PrefixTreeTest extends UnitTestBase
+{
+ @Test
+ public void testPrefixWithWildcard_Single()
+ {
+
testPrefixWithWildcard_Single(PrefixTree.fromPrefixWithWildCard("abcd"));
+
testPrefixWithWildcard_Single(PrefixTree.fromPrefixWithWildCard("abcd").mergeWithPrefix("abcd"));
+
testPrefixWithWildcard_Single(PrefixTree.fromPrefixWithWildCard("abcdXYZ").mergeWithPrefix("abcd"));
+
testPrefixWithWildcard_Single(PrefixTree.fromPrefixWithWildCard("abcd").mergeWithPrefix("abcdXYZ"));
+
+
testPrefixWithWildcard_Single(PrefixTree.fromPrefixWithWildCard("abcd").mergeWithFinalValue("abcd"));
+
testPrefixWithWildcard_Single(PrefixTree.fromFinalValue("abcd").mergeWithPrefix("abcd"));
+
testPrefixWithWildcard_Single(PrefixTree.fromPrefixWithWildCard("abcd").mergeWithFinalValue("abcdXYZ"));
+
testPrefixWithWildcard_Single(PrefixTree.fromFinalValue("abcdXYZ").mergeWithPrefix("abcd"));
+ }
+
+ private void testPrefixWithWildcard_Single(PrefixTree tree)
+ {
+ assertNotNull(tree);
+ assertEquals(1, tree.size());
+ for (final String str : tree)
+ {
+ assertEquals("abcd*", str);
+ }
+ assertEquals("abcd", tree.prefix());
+ assertEquals('a', tree.firstPrefixCharacter());
+ assertNotNull(tree.branches());
+ assertTrue(tree.branches().isEmpty());
+
+ assertTrue(tree.match("abcd"));
+ assertTrue(tree.match("abcd.e"));
+ assertFalse(tree.match("Abcdx"));
+ assertFalse(tree.match("abc"));
+ assertFalse(tree.match("ab"));
+ assertFalse(tree.match(""));
+ assertFalse(tree.match(null));
+ }
+
+ @Test
+ public void testPrefixWithWildcard()
+ {
+ final String[] strings = new String[]{"exchange.public.*",
"exchange.private.*", "response.public.*", "response.private.*", "response.*"};
+ for (final String[] strs : permute(strings, 0))
+ {
+ final PrefixTree tree = PrefixTree.from(strs[0])
+ .mergeWith(strs[1])
+ .mergeWith(strs[2])
+ .mergeWith(strs[3])
+ .mergeWith(strs[4]);
+ testPrefixWithWildcard(tree);
+ testPrefixWithWildcard(PrefixTree.from(Arrays.asList(strs)));
+ }
+ }
+
+ private void testPrefixWithWildcard(PrefixTree tree)
+ {
+ assertNotNull(tree);
+ assertEquals(3, tree.size());
+ final String[] array = new String[3];
+ int i = 0;
+ for (final String str : tree)
+ {
+ array[i++] = str;
+ }
+ assertArrayEquals(new String[]{"exchange.private.*",
"exchange.public.*", "response.*"}, array);
+ assertNotNull(tree.branches());
+ assertEquals(2, tree.branches().size());
+
+ PrefixTree branch = tree.branches().get('e');
+ assertNotNull(branch);
+ assertEquals('e', branch.firstPrefixCharacter());
+ assertEquals("exchange.p", branch.prefix());
+ assertEquals(2, branch.size());
+ assertEquals(2, branch.branches().size());
+ assertNotNull(branch.branches().get('r'));
+ assertEquals("rivate.", branch.branches().get('r').prefix());
+ assertNotNull(branch.branches().get('u'));
+ assertEquals("ublic.", branch.branches().get('u').prefix());
+
+ branch = tree.branches().get('r');
+ assertNotNull(branch);
+ assertEquals('r', branch.firstPrefixCharacter());
+ assertEquals("response.", branch.prefix());
+ assertEquals(1, branch.size());
+ assertEquals(0, branch.branches().size());
+
+ assertTrue(tree.match("response.x"));
+ assertTrue(tree.match("response."));
+ assertTrue(tree.match("exchange.private.A"));
+ assertTrue(tree.match("exchange.private."));
+ assertTrue(tree.match("exchange.public.B"));
+ assertTrue(tree.match("exchange.public."));
+ assertFalse(tree.match("response"));
+ assertFalse(tree.match("exchange.private"));
+ assertFalse(tree.match("exchange.public"));
+
+ assertFalse(tree.match("exchange.rest"));
+ assertFalse(tree.match("reg"));
+ assertFalse(tree.match("error"));
+ assertFalse(tree.match("warning"));
+ assertFalse(tree.match(""));
+ assertFalse(tree.match(null));
+ }
+
+ @Test
+ public void testPrefixWithWildcard_RootWith3Branches()
+ {
+ final String[] strings = new String[]{"A", "B", "C"};
+ for (final String[] strs : permute(strings, 0))
+ {
+ final PrefixTree tree = PrefixTree.fromPrefixWithWildCard(strs[0])
+ .mergeWithPrefix(strs[1])
+ .mergeWithPrefix(strs[2]);
+ testPrefixWithWildcard_RootWith3Branches(tree);
+ }
+ }
+
+ private void testPrefixWithWildcard_RootWith3Branches(PrefixTree tree)
+ {
+ assertNotNull(tree);
+ assertEquals(3, tree.size());
+ final String[] array = new String[3];
+ int i = 0;
+ for (final String str : tree)
+ {
+ array[i++] = str;
+ }
+ assertArrayEquals(new String[]{"A*", "B*", "C*"}, array);
+ assertNotNull(tree.branches());
+ assertEquals(3, tree.branches().size());
+
+ PrefixTree branch = tree.branches().get('A');
+ assertNotNull(branch);
+ assertEquals('A', branch.firstPrefixCharacter());
+ assertEquals("A", branch.prefix());
+ assertTrue(branch.branches().isEmpty());
+
+ branch = tree.branches().get('B');
+ assertNotNull(branch);
+ assertEquals('B', branch.firstPrefixCharacter());
+ assertEquals("B", branch.prefix());
+ assertTrue(branch.branches().isEmpty());
+
+ branch = tree.branches().get('C');
+ assertNotNull(branch);
+ assertEquals('C', branch.firstPrefixCharacter());
+ assertEquals("C", branch.prefix());
+ assertTrue(branch.branches().isEmpty());
+
+ assertTrue(tree.match("A"));
+ assertTrue(tree.match("Ax"));
+ assertTrue(tree.match("B"));
+ assertTrue(tree.match("Bx"));
+ assertTrue(tree.match("C"));
+ assertTrue(tree.match("Cx"));
+
+ assertFalse(tree.match("x"));
+ assertFalse(tree.match("b"));
+ assertFalse(tree.match("cC"));
+ assertFalse(tree.match(""));
+ assertFalse(tree.match(null));
+ }
+
+ @Test
+ public void testPrefixWithWildcard_BranchSplit()
+ {
+ final String[] strings = new String[]{"AB*", "AC*"};
+ for (final String[] strs : permute(strings, 0))
+ {
+ final PrefixTree tree = PrefixTree.from(strs[0])
+ .mergeWith(strs[1]);
+ testPrefixWithWildcard_BranchSplit(tree);
+
testPrefixWithWildcard_BranchSplit(PrefixTree.from(Arrays.asList(strs)));
+ }
+ }
+
+ private void testPrefixWithWildcard_BranchSplit(PrefixTree tree)
+ {
+ assertNotNull(tree);
+ assertEquals(2, tree.size());
+ final String[] array = new String[2];
+ int i = 0;
+ for (final String str : tree)
+ {
+ array[i++] = str;
+ }
+ assertArrayEquals(new String[]{"AB*", "AC*"}, array);
+ assertEquals(2, tree.size());
+ assertNotNull(tree.branches());
+ assertEquals(2, tree.branches().size());
+
+ assertEquals('A', tree.firstPrefixCharacter());
+ assertEquals("A", tree.prefix());
+
+ PrefixTree branch = tree.branches().get('B');
+ assertNotNull(branch);
+ assertEquals('B', branch.firstPrefixCharacter());
+ assertEquals("B", branch.prefix());
+ assertTrue(branch.branches().isEmpty());
+
+ branch = tree.branches().get('C');
+ assertNotNull(branch);
+ assertEquals('C', branch.firstPrefixCharacter());
+ assertEquals("C", branch.prefix());
+ assertTrue(branch.branches().isEmpty());
+
+ assertTrue(tree.match("AB"));
+ assertTrue(tree.match("ABx"));
+ assertTrue(tree.match("AC"));
+ assertTrue(tree.match("ACx"));
+
+ assertFalse(tree.match("A"));
+ assertFalse(tree.match("Ab"));
+ assertFalse(tree.match("Ac"));
+ assertFalse(tree.match("b"));
+ assertFalse(tree.match("cC"));
+ assertFalse(tree.match(""));
+ assertFalse(tree.match(null));
+ }
+
+ @Test
+ public void testExactString_Single()
+ {
+ testExactString_Single(PrefixTree.fromFinalValue("abcd"));
+
testExactString_Single(PrefixTree.fromFinalValue("abcd").mergeWithFinalValue("abcd"));
+ }
+
+ private void testExactString_Single(PrefixTree tree)
+ {
+ assertNotNull(tree);
+ assertEquals(1, tree.size());
+ for (final String str : tree)
+ {
+ assertEquals("abcd", str);
+ }
+ assertEquals("abcd", tree.prefix());
+ assertEquals('a', tree.firstPrefixCharacter());
+ assertNotNull(tree.branches());
+ assertTrue(tree.branches().isEmpty());
+
+ assertTrue(tree.match("abcd"));
+ assertFalse(tree.match("aBcd"));
+ assertFalse(tree.match("abcd."));
+ assertFalse(tree.match("abc"));
+ assertFalse(tree.match("x"));
+ assertFalse(tree.match(""));
+ assertFalse(tree.match(null));
+ }
+
+ @Test
+ public void testExactString()
+ {
+ final String[] strings = new String[]{"exchange.public",
"exchange.private", "response.public", "response.private", "response"};
+ for (final String[] strs : permute(strings, 0))
+ {
+ final PrefixTree tree = PrefixTree.from(strs[0])
+ .mergeWith(strs[1])
+ .mergeWith(strs[2])
+ .mergeWith(strs[3])
+ .mergeWith(strs[4]);
+ testExactString(tree);
+ testExactString(PrefixTree.from(Arrays.asList(strs)));
+ }
+ }
+
+ private void testExactString(PrefixTree tree)
+ {
+ assertNotNull(tree);
+ assertEquals(5, tree.size());
+ final String[] array = new String[5];
+ int i = 0;
+ for (final String str : tree)
+ {
+ array[i++] = str;
+ }
+ assertArrayEquals(new String[]{"exchange.private", "exchange.public",
"response", "response.private", "response.public"}, array);
+ assertNotNull(tree.branches());
+ assertEquals(2, tree.branches().size());
+
+ PrefixTree branch = tree.branches().get('e');
+ assertNotNull(branch);
+ assertEquals('e', branch.firstPrefixCharacter());
+ assertEquals("exchange.p", branch.prefix());
+ assertEquals(2, branch.size());
+ assertEquals(2, branch.branches().size());
+ assertNotNull(branch.branches().get('r'));
+ assertEquals("rivate", branch.branches().get('r').prefix());
+ assertNotNull(branch.branches().get('u'));
+ assertEquals("ublic", branch.branches().get('u').prefix());
+
+ branch = tree.branches().get('r');
+ assertNotNull(branch);
+ assertEquals('r', branch.firstPrefixCharacter());
+ assertEquals("response", branch.prefix());
+ assertEquals(3, branch.size());
+ assertEquals(1, branch.branches().size());
+
+ branch = branch.branches().get('.');
+ assertNotNull(branch);
+ assertEquals('.', branch.firstPrefixCharacter());
+ assertEquals(".p", branch.prefix());
+ assertEquals(2, branch.size());
+ assertEquals(2, branch.branches().size());
+
+ assertNotNull(branch.branches().get('r'));
+ assertEquals("rivate", branch.branches().get('r').prefix());
+ assertNotNull(branch.branches().get('u'));
+ assertEquals("ublic", branch.branches().get('u').prefix());
+
+ assertTrue(tree.match("exchange.private"));
+ assertTrue(tree.match("exchange.public"));
+ assertTrue(tree.match("response"));
+ assertTrue(tree.match("response.private"));
+ assertTrue(tree.match("response.public"));
+
+ assertFalse(tree.match("exchange.privat"));
+ assertFalse(tree.match("exchange.privateX"));
+ assertFalse(tree.match("exchange.publi"));
+ assertFalse(tree.match("exchange.publicX"));
+ assertFalse(tree.match("respons"));
+ assertFalse(tree.match("response.p"));
+ assertFalse(tree.match("response.privat"));
+ assertFalse(tree.match("response.privateX"));
+ assertFalse(tree.match("response.publi"));
+ assertFalse(tree.match("response.publicX"));
+
+ assertFalse(tree.match("exchange.rest"));
+ assertFalse(tree.match("reg"));
+ assertFalse(tree.match("error"));
+ assertFalse(tree.match("warning"));
+ assertFalse(tree.match(""));
+ assertFalse(tree.match(null));
+ }
+
+ @Test
+ public void testExactString_RootWith3Branches()
+ {
+ final String[] strings = new String[]{"A", "B", "C"};
+ for (final String[] strs : permute(strings, 0))
+ {
+ final PrefixTree tree = PrefixTree.fromFinalValue(strs[0])
+ .mergeWithFinalValue(strs[1])
+ .mergeWithFinalValue(strs[2]);
+ testExactString_RootWith3Branches(tree);
+ }
+ }
+
+ private void testExactString_RootWith3Branches(PrefixTree tree)
+ {
+ assertNotNull(tree);
+ assertEquals(3, tree.size());
+ final String[] array = new String[3];
+ int i = 0;
+ for (final String str : tree)
+ {
+ array[i++] = str;
+ }
+ assertArrayEquals(new String[]{"A", "B", "C"}, array);
+ assertNotNull(tree.branches());
+ assertEquals(3, tree.branches().size());
+
+ PrefixTree branch = tree.branches().get('A');
+ assertNotNull(branch);
+ assertEquals('A', branch.firstPrefixCharacter());
+ assertEquals("A", branch.prefix());
+ assertTrue(branch.branches().isEmpty());
+
+ branch = tree.branches().get('B');
+ assertNotNull(branch);
+ assertEquals('B', branch.firstPrefixCharacter());
+ assertEquals("B", branch.prefix());
+ assertTrue(branch.branches().isEmpty());
+
+ branch = tree.branches().get('C');
+ assertNotNull(branch);
+ assertEquals('C', branch.firstPrefixCharacter());
+ assertEquals("C", branch.prefix());
+ assertTrue(branch.branches().isEmpty());
+
+ assertTrue(tree.match("A"));
+ assertTrue(tree.match("B"));
+ assertTrue(tree.match("C"));
+
+ assertFalse(tree.match("Ax"));
+ assertFalse(tree.match("b"));
+ assertFalse(tree.match("Cc"));
+ assertFalse(tree.match(""));
+ assertFalse(tree.match(null));
+ }
+
+ @Test
+ public void testExactString_BranchSpit()
+ {
+ final String[] strings = new String[]{"A", "AB", "AC"};
+ for (final String[] strs : permute(strings, 0))
+ {
+ final PrefixTree tree = PrefixTree.from(strs[0])
+ .mergeWith(strs[1])
+ .mergeWith(strs[2]);
+ testExactString_BranchSpit(tree);
+ testExactString_BranchSpit(PrefixTree.from(Arrays.asList(strs)));
+ }
+ }
+
+ private void testExactString_BranchSpit(PrefixTree tree)
+ {
+ assertNotNull(tree);
+ assertEquals(3, tree.size());
+ final String[] array = new String[3];
+ int i = 0;
+ for (final String str : tree)
+ {
+ array[i++] = str;
+ }
+ assertArrayEquals(new String[]{"A", "AB", "AC"}, array);
+ assertNotNull(tree.branches());
+ assertEquals(2, tree.branches().size());
+ assertEquals("A", tree.prefix());
+ assertEquals('A', tree.firstPrefixCharacter());
+
+ PrefixTree branch = tree.branches().get('B');
+ assertNotNull(branch);
+ assertEquals('B', branch.firstPrefixCharacter());
+ assertEquals("B", branch.prefix());
+ assertTrue(branch.branches().isEmpty());
+
+ branch = tree.branches().get('C');
+ assertNotNull(branch);
+ assertEquals('C', branch.firstPrefixCharacter());
+ assertEquals("C", branch.prefix());
+ assertTrue(branch.branches().isEmpty());
+
+ assertTrue(tree.match("A"));
+ assertTrue(tree.match("AB"));
+ assertTrue(tree.match("AC"));
+
+ assertFalse(tree.match("Ax"));
+ assertFalse(tree.match("Ab"));
+ assertFalse(tree.match("Ac"));
+ assertFalse(tree.match("aa"));
+ assertFalse(tree.match(""));
+ assertFalse(tree.match(null));
+ }
+
+ @Test
+ public void testMixing()
+ {
+ final String[] strings = new String[]{"exchange.public",
"exchange.private.A", "exchange.private.*", "response.public",
"response.private", "response.p*", "response"};
+ for (final String[] strs : permute(strings, 0))
+ {
+ final PrefixTree tree = PrefixTree.from(strs[0])
+ .mergeWith(strs[1])
+ .mergeWith(strs[2])
+ .mergeWith(strs[3])
+ .mergeWith(strs[4])
+ .mergeWith(strs[5])
+ .mergeWith(strs[6]);
+ testMixing(tree);
+ testMixing(PrefixTree.from(Arrays.asList(strs)));
+ }
+ }
+
+ private void testMixing(PrefixTree tree)
+ {
+ assertNotNull(tree);
+ assertEquals(4, tree.size());
+ final String[] array = new String[4];
+ int i = 0;
+ for (final String str : tree)
+ {
+ array[i++] = str;
+ }
+ assertArrayEquals(new String[]{"exchange.private.*",
"exchange.public", "response", "response.p*"}, array);
+ assertNotNull(tree.branches());
+ assertEquals(2, tree.branches().size());
+
+ PrefixTree branch = tree.branches().get('e');
+ assertNotNull(branch);
+ assertEquals('e', branch.firstPrefixCharacter());
+ assertEquals("exchange.p", branch.prefix());
+ assertEquals(2, branch.size());
+ assertEquals(2, branch.branches().size());
+ assertNotNull(branch.branches().get('r'));
+ assertEquals("rivate.", branch.branches().get('r').prefix());
+ assertNotNull(branch.branches().get('u'));
+ assertEquals("ublic", branch.branches().get('u').prefix());
+
+ branch = tree.branches().get('r');
+ assertNotNull(branch);
+ assertEquals('r', branch.firstPrefixCharacter());
+ assertEquals("response", branch.prefix());
+ assertEquals(2, branch.size());
+ assertEquals(1, branch.branches().size());
+
+ branch = branch.branches().get('.');
+ assertNotNull(branch);
+ assertEquals('.', branch.firstPrefixCharacter());
+ assertEquals(".p", branch.prefix());
+ assertEquals(1, branch.size());
+ assertTrue(branch.branches().isEmpty());
+
+
+ assertTrue(tree.match("exchange.private.A"));
+ assertTrue(tree.match("exchange.private."));
+ assertTrue(tree.match("exchange.public"));
+ assertTrue(tree.match("response"));
+ assertTrue(tree.match("response.private"));
+ assertTrue(tree.match("response.public"));
+ assertTrue(tree.match("response.p"));
+
+ assertFalse(tree.match("exchange.privat"));
+ assertFalse(tree.match("exchange.privateX"));
+ assertFalse(tree.match("exchange.publi"));
+ assertFalse(tree.match("exchange.publicX"));
+ assertFalse(tree.match("respons"));
+ assertFalse(tree.match("response."));
+
+ assertFalse(tree.match("exchange.rest"));
+ assertFalse(tree.match("reg"));
+ assertFalse(tree.match("error"));
+ assertFalse(tree.match("warning"));
+ assertFalse(tree.match(""));
+ assertFalse(tree.match(null));
+ }
+
+ @Test
+ public void testMixing_BranchSplit()
+ {
+ final String[] strings = new String[]{"AB*", "AC*", "AD", "AE"};
+ for (final String[] strs : permute(strings, 0))
+ {
+ final PrefixTree tree = PrefixTree.from(strs[0])
+ .mergeWith(strs[1])
+ .mergeWith(strs[2])
+ .mergeWith(strs[3]);
+ testMixing_BranchSplit(tree);
+ testMixing_BranchSplit(PrefixTree.from(Arrays.asList(strs)));
+ }
+ }
+
+ private void testMixing_BranchSplit(PrefixTree tree)
+ {
+ assertNotNull(tree);
+ assertEquals(4, tree.size());
+ final String[] array = new String[4];
+ int i = 0;
+ for (final String str : tree)
+ {
+ array[i++] = str;
+ }
+ assertArrayEquals(new String[]{"AB*", "AC*", "AD", "AE"}, array);
+ assertNotNull(tree.branches());
+ assertEquals(4, tree.branches().size());
+
+ assertEquals('A', tree.firstPrefixCharacter());
+ assertEquals("A", tree.prefix());
+
+ PrefixTree branch = tree.branches().get('B');
+ assertNotNull(branch);
+ assertEquals('B', branch.firstPrefixCharacter());
+ assertEquals("B", branch.prefix());
+ assertTrue(branch.branches().isEmpty());
+
+ branch = tree.branches().get('C');
+ assertNotNull(branch);
+ assertEquals('C', branch.firstPrefixCharacter());
+ assertEquals("C", branch.prefix());
+ assertTrue(branch.branches().isEmpty());
+
+ branch = tree.branches().get('D');
+ assertNotNull(branch);
+ assertEquals('D', branch.firstPrefixCharacter());
+ assertEquals("D", branch.prefix());
+ assertTrue(branch.branches().isEmpty());
+
+ branch = tree.branches().get('E');
+ assertNotNull(branch);
+ assertEquals('E', branch.firstPrefixCharacter());
+ assertEquals("E", branch.prefix());
+ assertTrue(branch.branches().isEmpty());
+
+ assertTrue(tree.match("AB"));
+ assertTrue(tree.match("ABx"));
+ assertTrue(tree.match("AC"));
+ assertTrue(tree.match("ACx"));
+ assertTrue(tree.match("AD"));
+ assertTrue(tree.match("AE"));
+
+ assertFalse(tree.match("A"));
+ assertFalse(tree.match("Ab"));
+ assertFalse(tree.match("Ac"));
+ assertFalse(tree.match("Ad"));
+ assertFalse(tree.match("aE"));
+ assertFalse(tree.match("ADx"));
+ assertFalse(tree.match("b"));
+ assertFalse(tree.match("cC"));
+ assertFalse(tree.match(""));
+ assertFalse(tree.match(null));
+ }
+
+ @Test
+ public void testMixing_BranchSplit2()
+ {
+ final String[] strings = new String[]{"AXB*", "AXC*", "AYD", "AYE"};
+ for (final String[] strs : permute(strings, 0))
+ {
+ final PrefixTree tree = PrefixTree.from(strs[0])
+ .mergeWith(strs[1])
+ .mergeWith(strs[2])
+ .mergeWith(strs[3]);
+ testMixing_BranchSplit2(tree);
+ testMixing_BranchSplit2(PrefixTree.from(Arrays.asList(strs)));
+ }
+ }
+
+ private void testMixing_BranchSplit2(PrefixTree tree)
+ {
+ assertNotNull(tree);
+ assertEquals(4, tree.size());
+ final String[] array = new String[4];
+ int i = 0;
+ for (final String str : tree)
+ {
+ array[i++] = str;
+ }
+ assertArrayEquals(new String[]{"AXB*", "AXC*", "AYD", "AYE"}, array);
+ assertNotNull(tree.branches());
+ assertEquals(2, tree.branches().size());
+
+ assertEquals('A', tree.firstPrefixCharacter());
+ assertEquals("A", tree.prefix());
+
+ PrefixTree branch = tree.branches().get('X');
+ assertNotNull(branch);
+ assertEquals('X', branch.firstPrefixCharacter());
+ assertEquals("X", branch.prefix());
+ assertEquals(2, branch.branches().size());
+
+ PrefixTree subBranch = branch.branches().get('B');
+ assertNotNull(subBranch);
+ assertEquals('B', subBranch.firstPrefixCharacter());
+ assertEquals("B", subBranch.prefix());
+ assertTrue(subBranch.branches().isEmpty());
+
+ subBranch = branch.branches().get('C');
+ assertNotNull(branch);
+ assertEquals('C', subBranch.firstPrefixCharacter());
+ assertEquals("C", subBranch.prefix());
+ assertTrue(subBranch.branches().isEmpty());
+
+ branch = tree.branches().get('Y');
+ assertNotNull(branch);
+ assertEquals('Y', branch.firstPrefixCharacter());
+ assertEquals("Y", branch.prefix());
+ assertEquals(2, branch.branches().size());
+
+ subBranch = branch.branches().get('D');
+ assertNotNull(subBranch);
+ assertEquals('D', subBranch.firstPrefixCharacter());
+ assertEquals("D", subBranch.prefix());
+ assertTrue(subBranch.branches().isEmpty());
+
+ subBranch = branch.branches().get('E');
+ assertNotNull(subBranch);
+ assertEquals('E', subBranch.firstPrefixCharacter());
+ assertEquals("E", subBranch.prefix());
+ assertTrue(subBranch.branches().isEmpty());
+
+ assertTrue(tree.match("AXB"));
+ assertTrue(tree.match("AXBx"));
+ assertTrue(tree.match("AXC"));
+ assertTrue(tree.match("AXCx"));
+ assertTrue(tree.match("AYD"));
+ assertTrue(tree.match("AYE"));
+
+ assertFalse(tree.match("A"));
+ assertFalse(tree.match("AXb"));
+ assertFalse(tree.match("AXc"));
+ assertFalse(tree.match("AYd"));
+ assertFalse(tree.match("aYE"));
+ assertFalse(tree.match("AYDx"));
+ assertFalse(tree.match("b"));
+ assertFalse(tree.match("cC"));
+ assertFalse(tree.match(""));
+ assertFalse(tree.match(null));
+ }
+
+ @Test
+ public void testFrom_Exception()
+ {
+ try
+ {
+ PrefixTree.from((String) null);
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ assertNotNull(e.getMessage());
+ }
+
+ try
+ {
+ PrefixTree.from("");
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ assertNotNull(e.getMessage());
+ }
+ }
+
+ @Test
+ public void testFromFinalValue_Exception()
+ {
+ try
+ {
+ PrefixTree.fromFinalValue(null);
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ assertNotNull(e.getMessage());
+ }
+
+ try
+ {
+ PrefixTree.from("");
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ assertNotNull(e.getMessage());
+ }
+ }
+
+ @Test
+ public void testFromPrefixWithWildCard_Exception()
+ {
+ try
+ {
+ PrefixTree.fromPrefixWithWildCard(null);
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ assertNotNull(e.getMessage());
+ }
+
+ try
+ {
+ PrefixTree.fromPrefixWithWildCard("");
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ assertNotNull(e.getMessage());
+ }
+ }
+
+ @Test
+ public void testMergeWith_Exception()
+ {
+ final PrefixTree tree = PrefixTree.from("A");
+ try
+ {
+ tree.mergeWith((String) null);
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ assertNotNull(e.getMessage());
+ }
+
+ try
+ {
+ tree.mergeWith("");
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ assertNotNull(e.getMessage());
+ }
+ }
+
+ @Test
+ public void testMergeWithPrefix_Exception()
+ {
+ final PrefixTree tree = PrefixTree.from("A");
+ try
+ {
+ tree.mergeWithPrefix(null);
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ assertNotNull(e.getMessage());
+ }
+
+ try
+ {
+ tree.mergeWithPrefix("");
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ assertNotNull(e.getMessage());
+ }
+ }
+
+ @Test
+ public void testMergeWithFinalValue_Exception()
+ {
+ final PrefixTree tree = PrefixTree.from("A");
+ try
+ {
+ tree.mergeWithFinalValue(null);
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ assertNotNull(e.getMessage());
+ }
+
+ try
+ {
+ tree.mergeWithFinalValue("");
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ assertNotNull(e.getMessage());
+ }
+ }
+
+ @Test
+ public void testFirstPrefixCharacter()
+ {
+ final PrefixTree tree =
PrefixTree.fromFinalValue("A").mergeWithFinalValue("B");
+ try
+ {
+ tree.firstPrefixCharacter();
+ fail();
+ }
+ catch (UnsupportedOperationException e)
+ {
+ assertNotNull(e.getMessage());
+ }
+ }
+
+ private List<String[]> permute(String[] array, int startIndex)
+ {
+ final List<String[]> result = new ArrayList<>();
+ result.add(array);
+ for (int i = startIndex + 1; i < array.length; i++)
+ {
+ final String[] copy = Arrays.copyOf(array, array.length);
+ final String aux = copy[startIndex];
+ copy[startIndex] = copy[i];
+ copy[i] = aux;
+ result.addAll(permute(copy, startIndex + 1));
+ }
+ return result;
+ }
+
+ @Test
+ public void testIterator()
+ {
+ final String[] strings = new String[]{"AXB*", "AXC*", "AYD", "AYE",
"D"};
+ for (final String[] strs : permute(strings, 0))
+ {
+ final PrefixTree tree = PrefixTree.from(Arrays.asList(strs));
+ testIterator(tree, strings);
+ }
+ }
+
+ private void testIterator(PrefixTree tree, String[] strings)
+ {
+ assertNotNull(tree);
+
+ final List<String> list =
Streams.stream(tree).collect(Collectors.toList());
+ assertEquals(Arrays.asList(strings), list);
+
+ final Iterator<String> iterator = tree.iterator();
+ assertTrue(iterator.hasNext());
+ assertTrue(iterator.hasNext());
+ for (final String str : strings)
+ {
+ assertEquals(str, iterator.next());
+ }
+ assertFalse(iterator.hasNext());
+ assertFalse(iterator.hasNext());
+
+ try
+ {
+ iterator.next();
+ fail("An exception is expected");
+ }
+ catch (NoSuchElementException e)
+ {
+ // do nothing
+ }
+ }
+}
\ No newline at end of file
diff --git
a/broker-plugins/management-http/src/main/java/resources/js/qpid/management/accesscontrolprovider/RuleBased.js
b/broker-plugins/management-http/src/main/java/resources/js/qpid/management/accesscontrolprovider/RuleBased.js
index 8b32916..e1d7575 100644
---
a/broker-plugins/management-http/src/main/java/resources/js/qpid/management/accesscontrolprovider/RuleBased.js
+++
b/broker-plugins/management-http/src/main/java/resources/js/qpid/management/accesscontrolprovider/RuleBased.js
@@ -114,12 +114,24 @@ define(["dojo/_base/declare",
if (value)
{
markup = "<div class='keyValuePair'>";
- for (var key in value)
+ for (let key in value)
{
if (value.hasOwnProperty(key))
{
- markup += "<div>" +
entities.encode(String(key)) + "="
- +
entities.encode(String(value[key])) + "</div>";
+ const valueList = value[key];
+ if (Array.isArray(valueList))
+ {
+ for (let item of valueList)
+ {
+ markup += "<div>" +
entities.encode(String(key)) + "="
+ +
entities.encode(String(item)) + "</div>";
+ }
+ }
+ else
+ {
+ markup += "<div>" +
entities.encode(String(key)) + "="
+ +
entities.encode(String(valueList)) + "</div>";
+ }
}
}
markup += "</div>"
diff --git
a/doc/java-broker/src/docbkx/security/Java-Broker-Security-AccessControlProviders.xml
b/doc/java-broker/src/docbkx/security/Java-Broker-Security-AccessControlProviders.xml
index 8b92dd2..e700e4a 100644
---
a/doc/java-broker/src/docbkx/security/Java-Broker-Security-AccessControlProviders.xml
+++
b/doc/java-broker/src/docbkx/security/Java-Broker-Security-AccessControlProviders.xml
@@ -130,10 +130,15 @@
ACL rules follow this syntax:
</para>
<programlisting>
- ACL {permission} {<group-name>|<user-name>|ALL} {action|ALL}
[object|ALL] [property="<property-value>"]
+ ACL {permission} {<group-name>|<user-name>|ALL} {action|ALL}
[object|ALL] [property=<property-values>]
</programlisting>
<para>
+ The <property-values> can be a single value property="single
value" or a list of comma separated values in brackets
+ property=["value1", "value2", "value3"]. If a property repeats then it
will be interpreted as list of values,
+ for example name="n1" name="n2" name="n3" is interpreted as name=["n1",
"n2", "n3"].
+ </para>
+ <para>
Comments may be introduced with the hash (#) character and are ignored.
Long lines can be broken with the slash (\) character.
</para>
<programlisting>
@@ -509,7 +514,7 @@
The following ACL rules are given to the Broker.
</para>
<example>
- <title>Worked example 2 - Simple Messaging - Broker ACL rules</title>
+ <title>Worked example 2a - Simple Messaging - Broker ACL rules</title>
<programlisting><![CDATA[
# This gives the operate permission to delete messages on all queues on all
virtualhost
ACL ALLOW operator ACCESS MANAGEMENT
@@ -517,6 +522,14 @@ ACL ALLOW operator INVOKE QUEUE
method_name="deleteMessages"
ACL ALLOW operator INVOKE QUEUE method_name="getMessage*"]]>
</programlisting>
</example>
+ <example>
+ <title>Worked example 2b - Simple Messaging - Broker ACL rules with
multi-value property</title>
+ <programlisting><![CDATA[
+# This gives the operate permission to delete messages on all queues on all
virtualhost
+ACL ALLOW operator ACCESS MANAGEMENT
+ACL ALLOW operator INVOKE QUEUE method_name=["deleteMessages", "getMessage*"]
]]>
+ </programlisting>
+ </example>
<para>
And the following ACL rule-set is applied to the Virtualhost. The
default outcome of the
Access Control Provider must be <literal>DEFERED</literal>. This
means that if a request for
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]