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 75b1fc6 QPID-8487: [Broker-J] Enhance ACL rule evaluation 75b1fc6 is described below commit 75b1fc6e053ef0061ab6514d90b503d87bc7aa63 Author: Marek Laca <mkl...@users.noreply.github.com> AuthorDate: Wed Feb 23 07:58:14 2022 +0100 QPID-8487: [Broker-J] Enhance ACL rule evaluation This closes #115 --- .../security/access/config/LegacyOperation.java | 13 +- .../server/security/access/config/ObjectType.java | 43 +- .../qpid/server/security/access/config/Rule.java | 29 +- .../security/access/config/RuleCollector.java | 2 +- .../security/access/config/RuleInspector.java | 64 ++ .../security/access/config/RulePredicate.java | 11 + .../server/security/access/config/RuleSet.java | 279 +------- .../security/access/config/RuleSetBuilder.java | 204 ++++++ .../server/security/access/config/RuleSetImpl.java | 419 +++++++++++ ...stractCommonRuleBasedAccessControlProvider.java | 4 +- .../security/access/plugins/RuleOutcome.java | 61 +- .../security/access/config/AclFileParserTest.java | 68 +- .../access/config/AclRulePredicatesTest.java | 4 - .../access/config/LegacyOperationTest.java | 43 ++ .../security/access/config/ObjectTypeTest.java | 65 ++ .../security/access/config/RuleCollectorTest.java | 14 +- .../server/security/access/config/RuleSetTest.java | 767 ++++++++++++++++++++- .../server/security/access/config/RuleTest.java | 62 +- .../config/predicates/RulePredicateTest.java | 15 + .../security/access/plugins/RuleOutcomeTest.java | 62 ++ .../jms_1_1/extensions/acl/MessagingACLTest.java | 7 +- 21 files changed, 1866 insertions(+), 370 deletions(-) diff --git a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/LegacyOperation.java b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/LegacyOperation.java index 56ca67c..2270864 100644 --- a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/LegacyOperation.java +++ b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/LegacyOperation.java @@ -18,6 +18,8 @@ */ package org.apache.qpid.server.security.access.config; +import java.util.Locale; + /** * An enumeration of all possible actions that can form part of an access control v2 rule. */ @@ -38,10 +40,17 @@ public enum LegacyOperation SHUTDOWN, INVOKE; + private final String _description; + + LegacyOperation() + { + final String name = name(); + _description = name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1).toLowerCase(Locale.ENGLISH); + } + @Override public String toString() { - String name = name(); - return name.charAt(0) + name.substring(1).toLowerCase(); + return _description; } } diff --git a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/ObjectType.java b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/ObjectType.java index b2b4246..860ab5e 100644 --- a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/ObjectType.java +++ b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/ObjectType.java @@ -18,6 +18,11 @@ */ package org.apache.qpid.server.security.access.config; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Locale; +import java.util.Set; + import static org.apache.qpid.server.security.access.config.LegacyOperation.ACCESS; import static org.apache.qpid.server.security.access.config.LegacyOperation.ACCESS_LOGS; import static org.apache.qpid.server.security.access.config.LegacyOperation.BIND; @@ -32,18 +37,15 @@ import static org.apache.qpid.server.security.access.config.LegacyOperation.SHUT import static org.apache.qpid.server.security.access.config.LegacyOperation.UNBIND; import static org.apache.qpid.server.security.access.config.LegacyOperation.UPDATE; -import java.util.EnumSet; -import java.util.Set; - /** * An enumeration of all possible object types that can form part of an access control v2 rule. - * + * <p> * Each object type is valid only for a certain set of {@link LegacyOperation}s, which are passed as a list to * the constructor, and can be checked using the {@link #isSupported(LegacyOperation)} method. */ public enum ObjectType { - ALL(EnumSet.allOf(LegacyOperation.class)), + ALL, VIRTUALHOSTNODE(LegacyOperation.ALL, CREATE, DELETE, UPDATE, INVOKE), VIRTUALHOST(LegacyOperation.ALL, ACCESS, CREATE, DELETE, UPDATE, ACCESS_LOGS, INVOKE), MANAGEMENT(LegacyOperation.ALL, ACCESS), @@ -54,34 +56,41 @@ public enum ObjectType GROUP(LegacyOperation.ALL, CREATE, DELETE, UPDATE, INVOKE), BROKER(LegacyOperation.ALL, CONFIGURE, ACCESS_LOGS, SHUTDOWN, INVOKE); - private EnumSet<LegacyOperation> _operations; + private final EnumSet<LegacyOperation> _operations; + private final String _description; - ObjectType(LegacyOperation first, LegacyOperation... rest) + ObjectType(LegacyOperation... rest) { - this(EnumSet.of(first, rest)); + _operations = EnumSet.of(LegacyOperation.ALL, rest); + _description = description(); } - ObjectType(EnumSet<LegacyOperation> operations) + ObjectType() { - _operations = operations; + _operations = EnumSet.allOf(LegacyOperation.class); + _description = description(); } - + + private String description() + { + final String name = name(); + return name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1).toLowerCase(Locale.ENGLISH); + } + public Set<LegacyOperation> getOperations() { - return _operations; + return Collections.unmodifiableSet(_operations); } - + public boolean isSupported(LegacyOperation operation) { return _operations.contains(operation); } - + @Override public String toString() { - String name = name(); - return name.charAt(0) + name.substring(1).toLowerCase(); + return _description; } - } 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 1bf4213..13c9874 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 @@ -47,6 +47,8 @@ public class Rule private final AclRulePredicates _predicates; private final RuleOutcome _ruleOutcome; private final RulePredicate _rulePredicate; + private final boolean _isOwner; + private final boolean _isAll; public Rule(AclRule rule) { @@ -64,16 +66,24 @@ public class Rule _ruleOutcome = Objects.requireNonNull(ruleOutcome); _predicates = Objects.requireNonNull(predicates); _rulePredicate = Objects.requireNonNull(predicates.asSinglePredicate()); + + _isOwner = OWNER.equalsIgnoreCase(identity); + _isAll = ALL.equalsIgnoreCase(identity); + } + + public boolean isForAll() + { + return _isAll; } - public static boolean isAll(String identity) + public boolean isForOwner() { - return ALL.equalsIgnoreCase(identity); + return _isOwner; } - public static boolean isOwner(String identity) + public boolean isForOwnerOrAll() { - return OWNER.equalsIgnoreCase(identity); + return _isOwner || _isAll; } public String getIdentity() @@ -96,14 +106,18 @@ public class Rule { return operationsMatch(actionOperation) && objectTypesMatch(actionObjectType) && - propertiesAttributesMatch(actionOperation, actionObjectProperties, subject); + predicatesMatch(actionOperation, actionObjectProperties, subject); } - private boolean propertiesAttributesMatch(LegacyOperation operation, ObjectProperties objectProperties, Subject subject) + public boolean predicatesMatch(LegacyOperation operation, ObjectProperties objectProperties, Subject subject) { return _rulePredicate.matches(operation, objectProperties, subject); } + public boolean anyPropertiesMatch() { + return _rulePredicate.matchesAny(); + } + private boolean operationsMatch(LegacyOperation actionOperation) { return LegacyOperation.ALL == getOperation() || getOperation() == actionOperation; @@ -170,12 +184,13 @@ public class Rule ", permission=" + _ruleOutcome + ']'; } - private static class AclRuleImpl implements AclRule + private static final class AclRuleImpl implements AclRule { private final Rule _rule; AclRuleImpl(Rule rule) { + super(); _rule = Objects.requireNonNull(rule); } diff --git a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleCollector.java b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleCollector.java index 561b683..bf653a2 100644 --- a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleCollector.java +++ b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleCollector.java @@ -96,7 +96,7 @@ final class RuleCollector RuleSet createRuleSet(EventLoggerProvider eventLoggerProvider) { - return new RuleSet(eventLoggerProvider, _rules.values(), _defaultResult); + return RuleSet.newInstance(eventLoggerProvider, _rules.values(), _defaultResult); } private static final class RuleKey diff --git a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleInspector.java b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleInspector.java new file mode 100644 index 0000000..efee96d --- /dev/null +++ b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleInspector.java @@ -0,0 +1,64 @@ +/* + * 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; + +import java.util.Set; + +import javax.security.auth.Subject; + +import org.apache.qpid.server.security.Result; + +/** + * Checks the rule collection based on a subject, operation and objectType. + * <p> + * Checks only enabled rules with identity equal to all, owner, the same, or a group with identity as a member, + * and operation is either all or the same operation, + * and object type is either all or the same object. + * </p> + */ +@FunctionalInterface +public interface RuleInspector +{ + /** + * Check the authorisation granted to a particular identity for an operation on an object type with + * specific properties. + * <p> + * Looks up the entire rule set, which may be cached, for the user and operation and goes through the rules + * in order to find the first one that matches. Either defers if there are no rules, returns the result of + * the first match found, or denies access if there are no matching rules. Normally, it would be expected + * to have a default deny or allow rule at the end of an access configuration however. + * </p> + */ + Result check(Subject subject, + LegacyOperation operation, + ObjectType objectType, + ObjectProperties properties); + + interface RuleInspectorFactory + { + RuleInspector newInspector(Set<String> relevantPrincipals); + + Set<String> allRuleIdentities(); + + default boolean isConstant() + { + return allRuleIdentities().isEmpty(); + } + } +} diff --git a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RulePredicate.java b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RulePredicate.java index 76a3022..685370f 100644 --- a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RulePredicate.java +++ b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RulePredicate.java @@ -59,6 +59,11 @@ public interface RulePredicate || other.matches(operation, objectProperties, subject); } + default boolean matchesAny() + { + return false; + } + static RulePredicate any() { return Any.INSTANCE; @@ -95,6 +100,12 @@ public interface RulePredicate { return this; } + + @Override + public boolean matchesAny() + { + return true; + } } final class None implements RulePredicate diff --git a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSet.java b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSet.java index 0466118..434be50 100644 --- a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSet.java +++ b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSet.java @@ -1,274 +1,71 @@ /* - * 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 + * 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. * - * 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; -import java.security.Principal; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; -import java.util.EnumMap; -import java.util.Iterator; -import java.util.LinkedList; import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.WeakHashMap; import javax.security.auth.Subject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.apache.qpid.server.logging.EventLogger; import org.apache.qpid.server.logging.EventLoggerProvider; -import org.apache.qpid.server.logging.messages.AccessControlMessages; import org.apache.qpid.server.security.Result; -import org.apache.qpid.server.security.access.plugins.RuleOutcome; -import org.apache.qpid.server.security.auth.AuthenticatedPrincipal; /** * Models the rule configuration for the access control plugin. + * RuleSet checks the rule list based on a subject, operation and objectType. + * <p> + * Allows only enabled rules with identity equal to all, owner, the same, or a group with identity as a member, + * and operation is either all or the same operation, + * and object type is either all or the same object. + * </p> */ -public class RuleSet implements EventLoggerProvider +public interface RuleSet extends EventLoggerProvider, List<Rule>, RuleInspector { - private static final Logger LOGGER = LoggerFactory.getLogger(RuleSet.class); - - private final List<Rule> _rules; - private final Map<Subject, Map<LegacyOperation, Map<ObjectType, List<Rule>>>> _cache = - Collections.synchronizedMap(new WeakHashMap<Subject, Map<LegacyOperation, Map<ObjectType, List<Rule>>>>()); - - private final EventLoggerProvider _eventLogger; - private Result _defaultResult = Result.DENIED; - - public RuleSet(final EventLoggerProvider eventLogger, - final Collection<Rule> rules, - final Result defaultResult) - { - _eventLogger = eventLogger; - _rules = new ArrayList<>(rules); - _defaultResult = defaultResult; - } - - int getRuleCount() - { - return _rules.size(); - } - - /** - * Filtered rules list based on a subject and operation. - * - * Allows only enabled rules with identity equal to all, the same, or a group with identity as a member, - * and operation is either all or the same operation. - */ - private List<Rule> getRules(final Subject subject, final LegacyOperation operation, final ObjectType objectType) - { - final Map<ObjectType, List<Rule>> objects = getObjectToRuleCache(subject, operation); - - // Lookup object type rules for the operation - if (!objects.containsKey(objectType)) - { - final Set<Principal> principals = subject.getPrincipals(); - boolean controlled = false; - List<Rule> filtered = new LinkedList<>(); - for (Rule rule : _rules) - { - if ((rule.getOperation() == LegacyOperation.ALL || rule.getOperation() == operation) - && (rule.getObjectType() == ObjectType.ALL || rule.getObjectType() == objectType)) - { - controlled = true; - - if (isRelevant(principals,rule)) - { - filtered.add(rule); - } - } - } - - // Return null if there are no rules at all for this operation and object type - if (filtered.isEmpty() && !controlled) - { - filtered = null; - } - - // Save the rules we selected - objects.put(objectType, filtered == null ? null : Collections.unmodifiableList(filtered)); - - LOGGER.debug("Cached {} RulesList: {}", objectType, filtered); - } - - // Return the cached rules - List<Rule> rules = objects.get(objectType); - - LOGGER.debug("Returning RuleList: {}", rules); - - return rules; - } + @Override + Result check(Subject subject, + LegacyOperation operation, + ObjectType objectType, + ObjectProperties properties); - /** - * Check the authorisation granted to a particular identity for an operation on an object type with - * specific properties. - * - * Looks up the entire ruleset, which may be cached, for the user and operation and goes through the rules - * in order to find the first one that matches. Either defers if there are no rules, returns the result of - * the first match found, or denies access if there are no matching rules. Normally, it would be expected - * to have a default deny or allow rule at the end of an access configuration however. - */ - public Result check(Subject subject, - LegacyOperation operation, - ObjectType objectType, - ObjectProperties properties) + default Result getDefault() { - LOGGER.debug("Checking action: operation={}, object={}, properties={}", operation, objectType, properties); - - // get the list of rules relevant for this request - List<Rule> rules = getRules(subject, operation, objectType); - if (rules == null) - { - - LOGGER.debug("No rules found, returning default result"); - - return getDefault(); - } - - final boolean ownerRules = rules.stream() - .anyMatch(rule -> rule.getIdentity().equalsIgnoreCase(Rule.OWNER)); - - if (ownerRules) - { - rules = new LinkedList<>(rules); - - if (operation == LegacyOperation.CREATE) - { - rules.removeIf(rule -> rule.getIdentity().equalsIgnoreCase(Rule.OWNER)); - } - else - { - // Discard OWNER rules if the object wasn't created by the subject - final String objectCreator = (String) properties.get(Property.CREATED_BY); - final Principal principal = - AuthenticatedPrincipal.getOptionalAuthenticatedPrincipalFromSubject(subject); - if (principal == null || !principal.getName().equalsIgnoreCase(objectCreator)) - { - rules.removeIf(rule -> rule.getIdentity().equalsIgnoreCase(Rule.OWNER)); - } - } - } - - // Iterate through a filtered set of rules dealing with this identity and operation - for (Rule rule : rules) - { - LOGGER.debug("Checking against rule: {}", rule); - - if (rule.matches(operation, objectType, properties, subject)) - { - RuleOutcome ruleOutcome = rule.getOutcome(); - LOGGER.debug("Action matches. Result: {}", ruleOutcome); - boolean allowed = ruleOutcome.isAllowed(); - if(ruleOutcome.isLogged()) - { - if(allowed) - { - getEventLogger().message(AccessControlMessages.ALLOWED( - operation.toString(), - objectType.toString(), - properties.toString())); - } - else - { - getEventLogger().message(AccessControlMessages.DENIED( - operation.toString(), - objectType.toString(), - properties.toString())); - } - } - - - return allowed ? Result.ALLOWED : Result.DENIED; - } - } - LOGGER.debug("Deferring result of ACL check"); - // Defer to the next plugin of this type, if it exists - return Result.DEFER; + return Result.DENIED; } - /** Default deny. */ - public Result getDefault() + static Builder newBuilder(EventLoggerProvider eventLogger) { - return _defaultResult; - + return new RuleSetBuilder(eventLogger); } - /** - * Returns all rules in the {@link RuleSet}. Primarily intended to support unit-testing. - * @return map of rules - */ - public List<Rule> getAllRules() - { - return Collections.unmodifiableList(_rules); - } - - private boolean isRelevant(final Set<Principal> principals, final Rule rule) + static RuleSet newInstance(EventLoggerProvider eventLogger, Collection<? extends Rule> rules, Result defaultResult) { - if (rule.getIdentity().equalsIgnoreCase(Rule.ALL) || - rule.getIdentity().equalsIgnoreCase(Rule.OWNER)) - { - return true; - } - else - { - for (Iterator<Principal> iterator = principals.iterator(); iterator.hasNext();) - { - final Principal principal = iterator.next(); - - if (rule.getIdentity().equalsIgnoreCase(principal.getName())) - { - return true; - } - } - } - - return false; + return newBuilder(eventLogger).addAllRules(rules).setDefaultResult(defaultResult).build(); } - private Map<ObjectType, List<Rule>> getObjectToRuleCache(final Subject subject, final LegacyOperation operation) + interface Builder { - // Lookup identity in cache and create empty operation map if required - Map<LegacyOperation, Map<ObjectType, List<Rule>>> operations = _cache.get(subject); - if (operations == null) - { - operations = Collections.synchronizedMap(new EnumMap<LegacyOperation, Map<ObjectType, List<Rule>>>(LegacyOperation.class)); - _cache.put(subject, operations); - } + Builder setDefaultResult(Result result); - // Lookup operation and create empty object type map if required - Map<ObjectType, List<Rule>> objects = operations.get(operation); - if (objects == null) - { - objects = Collections.synchronizedMap(new EnumMap<ObjectType, List<Rule>>(ObjectType.class)); - operations.put(operation, objects); - } - return objects; - } + Builder addAllRules(Collection<? extends Rule> rules); - @Override - public EventLogger getEventLogger() - { - return _eventLogger.getEventLogger(); + RuleSet build(); } } diff --git a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSetBuilder.java b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSetBuilder.java new file mode 100644 index 0000000..539821c --- /dev/null +++ b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSetBuilder.java @@ -0,0 +1,204 @@ +/* + * 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; + +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + +import org.apache.qpid.server.logging.EventLoggerProvider; +import org.apache.qpid.server.security.Result; +import org.apache.qpid.server.security.access.config.RuleInspector.RuleInspectorFactory; +import org.apache.qpid.server.security.access.config.RuleSet.Builder; +import org.apache.qpid.server.security.access.config.RuleSetImpl.CachedInspector; +import org.apache.qpid.server.security.access.config.RuleSetImpl.DefaultResultInspector; +import org.apache.qpid.server.security.access.config.RuleSetImpl.RuleBasedInspectorFactory; +import org.apache.qpid.server.security.access.config.RuleSetImpl.RuleBasedInspectorWithOwnerFilteringFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +final class RuleSetBuilder extends AbstractList<Rule> implements Builder +{ + private static final Logger LOGGER = LoggerFactory.getLogger(RuleSetBuilder.class); + + private final Map<LegacyOperation, Map<ObjectType, List<Rule>>> _ruleMap = + new EnumMap<>(LegacyOperation.class); + + private final List<Rule> _ruleList = new ArrayList<>(); + + private final EventLoggerProvider _eventLogger; + + private DefaultResultInspector _defaultInspector = new DefaultResultInspector(Result.DENIED); + + RuleSetBuilder(EventLoggerProvider eventLogger) + { + super(); + _eventLogger = eventLogger; + } + + @Override + public Builder setDefaultResult(Result result) + { + _defaultInspector = new DefaultResultInspector(result); + return this; + } + + @Override + public Builder addAllRules(Collection<? extends Rule> rules) + { + addAll(rules); + return this; + } + + @Override + public RuleSet build() + { + return new RuleSetImpl(this); + } + + @Override + public Rule get(int index) + { + return _ruleList.get(index); + } + + @Override + public int size() + { + return _ruleList.size(); + } + + @Override + public boolean add(Rule rule) + { + if (validate(rule)) + { + addRuleToOperationMap(rule); + return _ruleList.add(rule); + } + return false; + } + + private boolean validate(Rule rule) + { + // Owner identity does not have any meaning in case of create operation. + if (rule.getOperation() == LegacyOperation.CREATE && rule.isForOwner()) + { + LOGGER.warn("Invalid rule with create operation for owner '{}'", rule); + return false; + } + return true; + } + + EventLoggerProvider getEventLogger() + { + return _eventLogger; + } + + DefaultResultInspector getDefaultInspector() + { + return _defaultInspector; + } + + Map<LegacyOperation, Map<ObjectType, RuleInspector>> buildCache() + { + final Map<LegacyOperation, Map<ObjectType, RuleInspector>> operationMap = + new EnumMap<>(LegacyOperation.class); + + for (final LegacyOperation operation : LegacyOperation.values()) + { + final Map<ObjectType, RuleInspector> objectMap = new EnumMap<>(ObjectType.class); + for (final ObjectType object : ObjectType.values()) + { + final List<Rule> rules = groupBy(operation, object); + objectMap.put(object, convertToInspector(rules)); + } + operationMap.put(operation, Collections.unmodifiableMap(objectMap)); + } + return Collections.unmodifiableMap(operationMap); + } + + private RuleInspector convertToInspector(List<? extends Rule> rules) + { + // Return fixed result if there are no rules at all for given operation and object type. + if (rules.isEmpty()) + { + return _defaultInspector; + } + // In case of any rule with 'owner' identity the special 'owner' logic is needed. + if (rules.stream().anyMatch(Rule::isForOwner)) + { + return newInspector(RuleBasedInspectorWithOwnerFilteringFactory.newInstance(rules, _eventLogger)); + } + return newInspector(RuleBasedInspectorFactory.newInstance(rules, _eventLogger)); + } + + private RuleInspector newInspector(RuleInspectorFactory factory) + { + // The constant factory provides the same inspector for any input. Hence, caching is not needed. + if (factory.isConstant()) + { + return factory.newInspector(Collections.emptySet()); + } + return new CachedInspector(factory).init(); + } + + private List<Rule> groupBy(LegacyOperation operation, ObjectType objectType) + { + return Collections.unmodifiableList(_ruleMap.getOrDefault(operation, Collections.emptyMap()) + .getOrDefault(objectType, Collections.emptyList())); + } + + private void addRuleToOperationMap(Rule rule) + { + if (LegacyOperation.ALL == rule.getOperation()) + { + for (final LegacyOperation operation : LegacyOperation.values()) + { + addRuleToObjectTypeMap(_ruleMap.computeIfAbsent(operation, + opera -> new EnumMap<>(ObjectType.class)), rule); + } + } + else + { + addRuleToObjectTypeMap(_ruleMap.computeIfAbsent(rule.getOperation(), + opera -> new EnumMap<>(ObjectType.class)), rule); + } + } + + private void addRuleToObjectTypeMap(Map<ObjectType, List<Rule>> objectTypeMap, Rule rule) + { + if (ObjectType.ALL == rule.getObjectType()) + { + for (final ObjectType object : ObjectType.values()) + { + objectTypeMap.computeIfAbsent(object, obj -> new ArrayList<>()).add(rule); + } + } + else + { + objectTypeMap.computeIfAbsent(rule.getObjectType(), obj -> new ArrayList<>()).add(rule); + } + } +} diff --git a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSetImpl.java b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSetImpl.java new file mode 100644 index 0000000..09bcb4b --- /dev/null +++ b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSetImpl.java @@ -0,0 +1,419 @@ +/* + * 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; + +import org.apache.qpid.server.logging.EventLogger; +import org.apache.qpid.server.logging.EventLoggerProvider; +import org.apache.qpid.server.security.Result; +import org.apache.qpid.server.security.auth.AuthenticatedPrincipal; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.security.auth.Subject; +import java.security.Principal; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * Composite inspector that delegates the rule check to specialized inspector for given operation and object type. + * Rule inspector is a 'smart' ordered list of rules. + */ +final class RuleSetImpl extends AbstractList<Rule> implements RuleSet +{ + private static final Logger LOGGER = LoggerFactory.getLogger(RuleSet.class); + + private static final String CHECKING_AGAINST_RULE = "Checking against rule: {}"; + private static final String CHECKING_ACTION_OPERATION_OBJECT_PROPERTIES = + "Checking action: operation={}, object={}, properties={}"; + + private final List<Rule> _rules; + + private final Map<LegacyOperation, Map<ObjectType, RuleInspector>> _cache; + + private final EventLoggerProvider _eventLogger; + + private final DefaultResultInspector _defaultInspector; + + RuleSetImpl(RuleSetBuilder builder) + { + super(); + _rules = new ArrayList<>(builder); + _eventLogger = builder.getEventLogger(); + + _defaultInspector = builder.getDefaultInspector(); + _cache = builder.buildCache(); + } + + @Override + public Result getDefault() + { + return _defaultInspector.getDefaultResult(); + } + + @Override + public Result check(Subject subject, + LegacyOperation operation, + ObjectType objectType, + ObjectProperties properties) + { + return _cache.get(operation) + .get(objectType) + .check(subject, operation, objectType, properties); + } + + @Override + public EventLogger getEventLogger() + { + return _eventLogger.getEventLogger(); + } + + @Override + public Rule get(int index) + { + return _rules.get(index); + } + + @Override + public int size() + { + return _rules.size(); + } + + /** + * Inspector for given operation and object type and so it contains only rules that match operation and object type. + * The inspector caches the specialized inspectors for given principals of the subject in a map. + */ + static final class CachedInspector implements RuleInspector + { + private final Map<Set<String>, RuleInspector> _cache = new ConcurrentHashMap<>(); + + private final Set<String> _ruleIdentities; + + private final RuleInspectorFactory _factory; + + CachedInspector(RuleInspectorFactory factory) + { + super(); + _ruleIdentities = new HashSet<>(factory.allRuleIdentities()); + _factory = factory; + } + + /** + * Populates the cache with basic cases, the set of principals without any relevant principal or + * the set with only one relevant principal. If the rules don't utilize groups than all possible cases will be + * cached at the beginning. + * @return Cached inspector with populated cache + */ + public CachedInspector init() + { + final Set<String> empty = Collections.emptySet(); + _cache.put(empty, _factory.newInspector(empty)); + + for (final String ruleIdentity : _ruleIdentities) + { + final Set<String> identities = Collections.singleton(ruleIdentity); + _cache.put(identities, _factory.newInspector(identities)); + } + return this; + } + + @Override + public Result check(Subject subject, LegacyOperation operation, + ObjectType objectType, ObjectProperties properties) + { + final Set<String> relevantPrincipals = collectPrincipalNames(subject); + relevantPrincipals.retainAll(_ruleIdentities); + + return _cache.computeIfAbsent(relevantPrincipals, _factory::newInspector) + .check(subject, operation, objectType, properties); + } + + private Set<String> collectPrincipalNames(Subject subject) + { + final Set<Principal> principals = subject.getPrincipals(); + final Set<String> principalNames = new HashSet<>(principals.size()); + for (final Principal principal : principals) + { + principalNames.add(principal.getName()); + } + return principalNames; + } + } + + /** + * If there is no rule for given operation and object type then the default result has to be returned. + * The default result is DENIED. + */ + static final class DefaultResultInspector implements RuleInspector + { + private final Result _defaultResult; + + DefaultResultInspector(Result defaultResult) + { + super(); + _defaultResult = Optional.ofNullable(defaultResult).orElse(Result.DENIED); + } + + @Override + public Result check(Subject subject, LegacyOperation operation, + ObjectType objectType, ObjectProperties properties) + { + LOGGER.debug("No rules found, returning default result"); + return _defaultResult; + } + + public Result getDefaultResult() + { + return _defaultResult; + } + } + + /** + * Rule inspector is a 'smart' ordered list of rules for given operation and object type that match subject + * principals. Hence, the rule list has been already filtered based on the subject principals and so it contains + * only rules that match the operation, object type and subject principals. + */ + private static final class RuleBasedInspector implements RuleInspector + { + private final EventLoggerProvider _logger; + + private final Rule[] _rules; + + RuleBasedInspector(Collection<? extends Rule> rules, EventLoggerProvider logger) + { + _logger = logger; + _rules = rules.toArray(new Rule[0]); + } + + @Override + public Result check(Subject subject, + LegacyOperation operation, + ObjectType objectType, + ObjectProperties properties) + { + LOGGER.debug(CHECKING_ACTION_OPERATION_OBJECT_PROPERTIES, operation, objectType, properties); + for (final Rule rule : _rules) + { + LOGGER.debug(CHECKING_AGAINST_RULE, rule); + if (rule.predicatesMatch(operation, properties, subject)) + { + return rule.getOutcome().logResult(_logger, operation, objectType, properties); + } + } + LOGGER.debug("Deferring result of ACL check"); + return Result.DEFER; + } + } + + /** + * Rule inspector with 'owner' logic. + */ + private static final class RuleBasedInspectorWithOwnerFiltering implements RuleInspector + { + // Iteration through array is faster than using a collection. + private final Rule[] _rules; + + private final EventLoggerProvider _logger; + + RuleBasedInspectorWithOwnerFiltering(Collection<? extends Rule> rules, EventLoggerProvider logger) + { + super(); + _logger = logger; + _rules = rules.toArray(new Rule[0]); + } + + @Override + public Result check(Subject subject, + LegacyOperation operation, + ObjectType objectType, + ObjectProperties properties) + { + LOGGER.debug(CHECKING_ACTION_OPERATION_OBJECT_PROPERTIES, operation, objectType, properties); + + final Object objectCreator = properties.get(Property.CREATED_BY); + final Principal principal = AuthenticatedPrincipal.getOptionalAuthenticatedPrincipalFromSubject(subject); + + // Discard OWNER rules if the object wasn't created by the subject + if (principal != null && principal.getName().equals(objectCreator)) + { + for (final Rule rule : _rules) + { + LOGGER.debug(CHECKING_AGAINST_RULE, rule); + if (rule.predicatesMatch(operation, properties, subject)) + { + return rule.getOutcome().logResult(_logger, operation, objectType, properties); + } + } + } + else + { + for (final Rule rule : _rules) + { + LOGGER.debug(CHECKING_AGAINST_RULE, rule); + // Discarding owner rule + if (!rule.isForOwner() && + rule.predicatesMatch(operation, properties, subject)) + { + return rule.getOutcome().logResult(_logger, operation, objectType, properties); + } + } + } + LOGGER.debug("Deferring result of ACL check"); + return Result.DEFER; + } + } + + private abstract static class AbstractInspectorFactory implements RuleInspectorFactory + { + private final EventLoggerProvider _logger; + + // Iteration through array is faster than using a collection. + private final Rule[] _rules; + + private final Set<String> _allRuleIdentities; + + abstract boolean matchAnyIdentity(Rule rule); + + abstract RuleInspector newInspector(List<? extends Rule> list, EventLoggerProvider logger); + + AbstractInspectorFactory(List<? extends Rule> rules, EventLoggerProvider logger) + { + super(); + final List<? extends Rule> filterRules = filterSuppressedRules(rules); + _rules = filterRules.toArray(new Rule[0]); + _logger = Objects.requireNonNull(logger); + _allRuleIdentities = collectRuleIdentities(filterRules); + } + + @Override + public Set<String> allRuleIdentities() + { + return Collections.unmodifiableSet(_allRuleIdentities); + } + + @Override + public RuleInspector newInspector(Set<String> principalNames) + { + final List<Rule> filteredRules = new ArrayList<>(); + for (final Rule rule : _rules) + { + if (matchAnyIdentity(rule) || principalNames.contains(rule.getIdentity())) + { + filteredRules.add(rule); + } + } + return newInspector(filteredRules, _logger); + } + + private Set<String> collectRuleIdentities(Collection<? extends Rule> rules) + { + return rules.stream() + .filter(rule -> !rule.isForOwnerOrAll()) + .map(Rule::getIdentity) + .collect(Collectors.toSet()); + } + + /** + * The final rule always match for any principal (identity). Hence, any rule after the final rule is never + * checked and the rules suppressed by the final rule can be ignored. + * + * @param rules the list of the rules + * @return a filtered list of the rules. If there is any final rule then the final rule is the last one. + */ + private List<? extends Rule> filterSuppressedRules(List<? extends Rule> rules) + { + final ListIterator<? extends Rule> iter = rules.listIterator(); + while (iter.hasNext()) + { + final Rule rule = iter.next(); + if (isFinalRule(rule)) + { + return rules.subList(0, iter.nextIndex()); + } + } + return rules; + } + + private boolean isFinalRule(Rule rule) + { + return rule.anyPropertiesMatch() && rule.isForAll(); + } + } + + static final class RuleBasedInspectorFactory extends AbstractInspectorFactory + { + public static RuleInspectorFactory newInstance(List<? extends Rule> rules, EventLoggerProvider logger) + { + return new RuleBasedInspectorFactory(rules, logger); + } + + RuleBasedInspectorFactory(List<? extends Rule> rules, EventLoggerProvider logger) + { + super(rules, logger); + } + + @Override + boolean matchAnyIdentity(Rule rule) + { + return rule.isForAll(); + } + + @Override + RuleInspector newInspector(List<? extends Rule> filteredRules, EventLoggerProvider logger) + { + return new RuleBasedInspector(filteredRules, logger); + } + } + + static final class RuleBasedInspectorWithOwnerFilteringFactory extends AbstractInspectorFactory + { + public static RuleInspectorFactory newInstance(List<? extends Rule> rules, EventLoggerProvider logger) + { + return new RuleBasedInspectorWithOwnerFilteringFactory(rules, logger); + } + + RuleBasedInspectorWithOwnerFilteringFactory(List<? extends Rule> rules, EventLoggerProvider logger) + { + super(rules, logger); + } + + @Override + boolean matchAnyIdentity(Rule rule) + { + return rule.isForOwnerOrAll(); + } + + @Override + RuleInspector newInspector(List<? extends Rule> filteredRules, EventLoggerProvider logger) + { + return new RuleBasedInspectorWithOwnerFiltering(filteredRules, logger); + } + } +} 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 37d90a1..f5a888b 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 @@ -81,7 +81,7 @@ abstract class AbstractCommonRuleBasedAccessControlProvider<X extends AbstractCo { rules.add(new Rule(configuredRule)); } - return new RuleBasedAccessControl(new RuleSet(this, rules, _defaultResult), getModel()); + return new RuleBasedAccessControl(RuleSet.newInstance(this, rules, _defaultResult), getModel()); } public Result getDefaultResult() @@ -98,7 +98,7 @@ abstract class AbstractCommonRuleBasedAccessControlProvider<X extends AbstractCo { final RuleSet ruleSet = AclFileParser.parse(path, this); final List<AclRule> aclRules = new ArrayList<>(); - for (final Rule rule : ruleSet.getAllRules()) + for (final Rule rule : ruleSet) { aclRules.add(rule.asAclRule()); } diff --git a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/RuleOutcome.java b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/RuleOutcome.java index a9b2880..976776b 100644 --- a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/RuleOutcome.java +++ b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/RuleOutcome.java @@ -20,32 +20,63 @@ */ package org.apache.qpid.server.security.access.plugins; +import org.apache.qpid.server.logging.EventLoggerProvider; +import org.apache.qpid.server.logging.messages.AccessControlMessages; +import org.apache.qpid.server.security.Result; +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.ObjectType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * An enumeration of all possible outcomes that can be applied to an access control v2 rule. */ public enum RuleOutcome { - ALLOW(true, false), - ALLOW_LOG(true, true), - DENY(false, false), - DENY_LOG(false, true); + ALLOW(Result.ALLOWED), + ALLOW_LOG(Result.ALLOWED) + { + @Override + public Result logResult(EventLoggerProvider logger, LegacyOperation operation, ObjectType objectType, ObjectProperties objectProperties) + { + LOGGER.debug(ACTION_MATCHES_RESULT, this); + logger.getEventLogger().message(AccessControlMessages.ALLOWED( + operation.toString(), + objectType.toString(), + objectProperties.toString())); + return _result; + } + }, + DENY(Result.DENIED), + DENY_LOG(Result.DENIED) + { + @Override + public Result logResult(EventLoggerProvider logger, LegacyOperation operation, ObjectType objectType, ObjectProperties objectProperties) + { + LOGGER.debug(ACTION_MATCHES_RESULT, this); + logger.getEventLogger().message(AccessControlMessages.DENIED( + operation.toString(), + objectType.toString(), + objectProperties.toString())); + return _result; + } + }; - private final boolean _allowed; - private final boolean _logged; + private static final Logger LOGGER = LoggerFactory.getLogger(RuleOutcome.class); - RuleOutcome(final boolean allowed, final boolean logged) - { - _allowed = allowed; - _logged = logged; - } + private static final String ACTION_MATCHES_RESULT = "Action matches. Result: {}"; + + final Result _result; - public boolean isAllowed() + RuleOutcome(final Result result) { - return _allowed; + _result = result; } - public boolean isLogged() + public Result logResult(EventLoggerProvider logger, LegacyOperation operation, ObjectType objectType, ObjectProperties objectProperties) { - return _logged; + LOGGER.debug(ACTION_MATCHES_RESULT, this); + return _result; } } diff --git a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/AclFileParserTest.java b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/AclFileParserTest.java index 486ae2e..5aea03f 100644 --- a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/AclFileParserTest.java +++ b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/AclFileParserTest.java @@ -66,7 +66,7 @@ public class AclFileParserTest extends UnitTestBase public void testEmptyRuleSetDefaults() throws Exception { final RuleSet ruleSet = writeACLConfig(); - assertEquals(0, ruleSet.getAllRules().size()); + assertEquals(0, ruleSet.size()); assertEquals(Result.DENIED, ruleSet.getDefault()); } @@ -270,19 +270,19 @@ public class AclFileParserTest extends UnitTestBase public void testValidConfig() throws Exception { RuleSet ruleSet = writeACLConfig("CONFIG defaultdefer=true"); - assertEquals("Unexpected number of rules", 0, ruleSet.getAllRules().size()); + assertEquals("Unexpected number of rules", 0, ruleSet.size()); assertEquals("Unexpected default outcome", Result.DEFER, ruleSet.getDefault()); ruleSet = writeACLConfig("CONFIG defaultdeny=true"); - assertEquals("Unexpected number of rules", 0, ruleSet.getAllRules().size()); + assertEquals("Unexpected number of rules", 0, ruleSet.size()); assertEquals("Unexpected default outcome", Result.DENIED, ruleSet.getDefault()); ruleSet = writeACLConfig("CONFIG defaultallow=true"); - assertEquals("Unexpected number of rules", 0, ruleSet.getAllRules().size()); + assertEquals("Unexpected number of rules", 0, ruleSet.size()); assertEquals("Unexpected default outcome", Result.ALLOWED, ruleSet.getDefault()); ruleSet = writeACLConfig("CONFIG defaultdefer=false defaultallow=true defaultdeny=false df=false"); - assertEquals("Unexpected number of rules", 0, ruleSet.getAllRules().size()); + assertEquals("Unexpected number of rules", 0, ruleSet.size()); assertEquals("Unexpected default outcome", Result.ALLOWED, ruleSet.getDefault()); } @@ -293,9 +293,9 @@ public class AclFileParserTest extends UnitTestBase public void testValidRule() throws Exception { final RuleSet rules = writeACLConfig("ACL DENY-LOG user1 ACCESS VIRTUALHOST"); - assertEquals(1, rules.getAllRules().size()); + assertEquals(1, rules.size()); - final Rule rule = rules.getAllRules().get(0); + final Rule rule = rules.get(0); assertEquals("Rule has unexpected identity", "user1", rule.getIdentity()); assertEquals("Rule has unexpected operation", LegacyOperation.ACCESS, rule.getOperation()); assertEquals("Rule has unexpected object type", ObjectType.VIRTUALHOST, rule.getObjectType()); @@ -309,9 +309,9 @@ public class AclFileParserTest extends UnitTestBase public void testValidRuleWithSingleQuotedProperty() throws Exception { final RuleSet rules = writeACLConfig("ACL ALLOW all CREATE EXCHANGE name = 'value'"); - assertEquals(1, rules.getAllRules().size()); + assertEquals(1, rules.size()); - final Rule rule = rules.getAllRules().get(0); + final Rule rule = rules.get(0); assertEquals("Rule has unexpected identity", "all", rule.getIdentity()); assertEquals("Rule has unexpected operation", LegacyOperation.CREATE, rule.getOperation()); assertEquals("Rule has unexpected object type", ObjectType.EXCHANGE, rule.getObjectType()); @@ -330,8 +330,8 @@ public class AclFileParserTest extends UnitTestBase { final RuleSet rules = writeACLConfig("ACL ALLOW all CREATE EXCHANGE name = \"value\""); - assertEquals(1, rules.getAllRules().size()); - final Rule rule = rules.getAllRules().get(0); + assertEquals(1, rules.size()); + final Rule rule = rules.get(0); assertEquals("Rule has unexpected identity", "all", rule.getIdentity()); assertEquals("Rule has unexpected operation", LegacyOperation.CREATE, rule.getOperation()); assertEquals("Rule has unexpected object type", ObjectType.EXCHANGE, rule.getObjectType()); @@ -349,9 +349,9 @@ public class AclFileParserTest extends UnitTestBase public void testValidRuleWithManyProperties() throws Exception { final RuleSet rules = writeACLConfig("ACL ALLOW admin DELETE QUEUE name=name1 owner = owner1"); - assertEquals(1, rules.getAllRules().size()); + assertEquals(1, rules.size()); - final Rule rule = rules.getAllRules().get(0); + final Rule rule = rules.get(0); assertEquals("Rule has unexpected identity", "admin", rule.getIdentity()); assertEquals("Rule has unexpected operation", LegacyOperation.DELETE, rule.getOperation()); assertEquals("Rule has unexpected object type", ObjectType.QUEUE, rule.getObjectType()); @@ -373,9 +373,9 @@ public class AclFileParserTest extends UnitTestBase final RuleSet rules = writeACLConfig("ACL ALLOW all CREATE EXCHANGE routingKey = 'news.#'", "ACL ALLOW all CREATE EXCHANGE routingKey = 'news.co.#'", "ACL ALLOW all CREATE EXCHANGE routingKey = *.co.medellin"); - assertEquals(3, rules.getAllRules().size()); + assertEquals(3, rules.size()); - final Rule rule1 = rules.getAllRules().get(0); + final Rule rule1 = rules.get(0); assertEquals("Rule has unexpected identity", "all", rule1.getIdentity()); assertEquals("Rule has unexpected operation", LegacyOperation.CREATE, rule1.getOperation()); assertEquals("Rule has object type", ObjectType.EXCHANGE, rule1.getObjectType()); @@ -385,14 +385,14 @@ public class AclFileParserTest extends UnitTestBase .build(); assertEquals("Rule has unexpected predicates", expectedPredicates1, rule1.getPredicates()); - final Rule rule2 = rules.getAllRules().get(1); + final Rule rule2 = rules.get(1); final AclRulePredicates expectedPredicates2 = new AclRulePredicatesBuilder() .put(Property.ROUTING_KEY, "news.co.#") .build(); assertEquals("Rule has unexpected predicates", expectedPredicates2, rule2.getPredicates()); - final Rule rule3 = rules.getAllRules().get(2); + final Rule rule3 = rules.get(2); final AclRulePredicates expectedPredicates3 = new AclRulePredicatesBuilder() .put(Property.ROUTING_KEY, "*.co.medellin") @@ -406,23 +406,23 @@ public class AclFileParserTest extends UnitTestBase final RuleSet rules = writeACLConfig("5 ACL DENY all CREATE EXCHANGE", "3 ACL ALLOW all CREATE EXCHANGE routingKey = 'news.co.#'", "1 ACL ALLOW all CREATE EXCHANGE routingKey = *.co.medellin"); - assertEquals(3, rules.getAllRules().size()); + assertEquals(3, rules.size()); - final Rule rule1 = rules.getAllRules().get(0); + final Rule rule1 = rules.get(0); final AclRulePredicates expectedPredicates1 = new AclRulePredicatesBuilder() .put(Property.ROUTING_KEY, "*.co.medellin") .build(); assertEquals("Rule has unexpected predicates", expectedPredicates1, rule1.getPredicates()); - final Rule rule3 = rules.getAllRules().get(1); + final Rule rule3 = rules.get(1); final AclRulePredicates expectedPredicates3 = new AclRulePredicatesBuilder() .put(Property.ROUTING_KEY, "news.co.#") .build(); assertEquals("Rule has unexpected predicates", expectedPredicates3, rule3.getPredicates()); - final Rule rule5 = rules.getAllRules().get(2); + final Rule rule5 = rules.get(2); assertEquals("Rule has unexpected identity", "all", rule5.getIdentity()); assertEquals("Rule has unexpected operation", LegacyOperation.CREATE, rule5.getOperation()); assertEquals("Rule has unexpected object type", ObjectType.EXCHANGE, rule5.getObjectType()); @@ -450,7 +450,7 @@ public class AclFileParserTest extends UnitTestBase public void testShortValidRule() throws Exception { final RuleSet rules = writeACLConfig("ACL DENY user UPDATE"); - assertEquals(1, rules.getAllRules().size()); + assertEquals(1, rules.size()); validateRule(rules, "user", LegacyOperation.UPDATE, ObjectType.ALL, EMPTY); } @@ -461,9 +461,9 @@ public class AclFileParserTest extends UnitTestBase public void testMixedCaseRuleInterpretation() throws Exception { final RuleSet rules = writeACLConfig("AcL deny-LOG User1 BiND Exchange Name=AmQ.dIrect"); - assertEquals(1, rules.getAllRules().size()); + assertEquals(1, rules.size()); - final Rule rule = rules.getAllRules().get(0); + final Rule rule = rules.get(0); assertEquals("Rule has unexpected identity", "User1", rule.getIdentity()); assertEquals("Rule has unexpected operation", LegacyOperation.BIND, rule.getOperation()); assertEquals("Rule has unexpected object type", ObjectType.EXCHANGE, rule.getObjectType()); @@ -485,9 +485,9 @@ public class AclFileParserTest extends UnitTestBase final RuleSet rules = writeACLConfig("#Comment", "ACL DENY-LOG user1 ACCESS VIRTUALHOST # another comment", " # final comment with leading whitespace"); - assertEquals(1, rules.getAllRules().size()); + assertEquals(1, rules.size()); - final Rule rule = rules.getAllRules().get(0); + final Rule rule = rules.get(0); assertEquals("Rule has unexpected identity", "user1", rule.getIdentity()); assertEquals("Rule has unexpected operation", LegacyOperation.ACCESS, rule.getOperation()); assertEquals("Rule has unexpected object type", ObjectType.VIRTUALHOST, rule.getObjectType()); @@ -501,9 +501,9 @@ public class AclFileParserTest extends UnitTestBase public void testWhitespace() throws Exception { final RuleSet rules = writeACLConfig("ACL\tDENY-LOG\t\t user1\t \tACCESS VIRTUALHOST"); - assertEquals(1, rules.getAllRules().size()); + assertEquals(1, rules.size()); - final Rule rule = rules.getAllRules().get(0); + final Rule rule = rules.get(0); assertEquals("Rule has unexpected identity", "user1", rule.getIdentity()); assertEquals("Rule has unexpected operation", LegacyOperation.ACCESS, rule.getOperation()); assertEquals("Rule has unexpected object type", ObjectType.VIRTUALHOST, rule.getObjectType()); @@ -514,9 +514,9 @@ public class AclFileParserTest extends UnitTestBase public void testWhitespace2() throws Exception { final RuleSet rules = writeACLConfig("ACL\u000B DENY-LOG\t\t user1\t \tACCESS VIRTUALHOST\u001E"); - assertEquals(1, rules.getAllRules().size()); + assertEquals(1, rules.size()); - final Rule rule = rules.getAllRules().get(0); + final Rule rule = rules.get(0); assertEquals("Rule has unexpected identity", "user1", rule.getIdentity()); assertEquals("Rule has unexpected operation", LegacyOperation.ACCESS, rule.getOperation()); assertEquals("Rule has unexpected object type", ObjectType.VIRTUALHOST, rule.getObjectType()); @@ -531,9 +531,9 @@ public class AclFileParserTest extends UnitTestBase { final RuleSet rules = writeACLConfig("ACL DENY-LOG user1 \\", "ACCESS VIRTUALHOST"); - assertEquals(1, rules.getAllRules().size()); + assertEquals(1, rules.size()); - final Rule rule = rules.getAllRules().get(0); + final Rule rule = rules.get(0); assertEquals("Rule has unexpected identity", "user1", rule.getIdentity()); assertEquals("Rule has unexpected operation", LegacyOperation.ACCESS, rule.getOperation()); assertEquals("Rule has unexpected object type", ObjectType.VIRTUALHOST, rule.getObjectType()); @@ -704,8 +704,8 @@ public class AclFileParserTest extends UnitTestBase ObjectType objectType, AclRulePredicates predicates) { - assertEquals(1, rules.getAllRules().size()); - final Rule rule = rules.getAllRules().get(0); + assertEquals(1, rules.size()); + final Rule rule = rules.get(0); assertEquals("Rule has unexpected identity", username, rule.getIdentity()); assertEquals("Rule has unexpected operation", operation, rule.getOperation()); assertEquals("Rule has unexpected object type", objectType, rule.getObjectType()); 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 91343fe..48b2a8e 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 @@ -23,10 +23,6 @@ import java.util.Collections; import java.util.Map; import java.util.Set; -import org.apache.qpid.server.security.access.config.AclRulePredicates; -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.FirewallRule; import org.apache.qpid.server.security.access.config.predicates.TestFirewallRule; import org.apache.qpid.server.security.access.firewall.FirewallRuleFactory; import org.apache.qpid.test.utils.UnitTestBase; diff --git a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/LegacyOperationTest.java b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/LegacyOperationTest.java new file mode 100644 index 0000000..19e2aff --- /dev/null +++ b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/LegacyOperationTest.java @@ -0,0 +1,43 @@ +/* + * 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; + +import junit.framework.TestCase; +import org.junit.Test; + +public class LegacyOperationTest extends TestCase +{ + @Test + public void testToString() { + assertEquals("Access", LegacyOperation.ACCESS.toString()); + assertEquals("Access_logs", LegacyOperation.ACCESS_LOGS.toString()); + assertEquals("All", LegacyOperation.ALL.toString()); + assertEquals("Bind", LegacyOperation.BIND.toString()); + assertEquals("Create", LegacyOperation.CREATE.toString()); + assertEquals("Configure", LegacyOperation.CONFIGURE.toString()); + assertEquals("Consume", LegacyOperation.CONSUME.toString()); + assertEquals("Delete", LegacyOperation.DELETE.toString()); + assertEquals("Invoke", LegacyOperation.INVOKE.toString()); + assertEquals("Publish", LegacyOperation.PUBLISH.toString()); + assertEquals("Purge", LegacyOperation.PURGE.toString()); + assertEquals("Shutdown", LegacyOperation.SHUTDOWN.toString()); + assertEquals("Unbind", LegacyOperation.UNBIND.toString()); + assertEquals("Update", LegacyOperation.UPDATE.toString()); + } +} diff --git a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/ObjectTypeTest.java b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/ObjectTypeTest.java new file mode 100644 index 0000000..fa9bb44 --- /dev/null +++ b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/ObjectTypeTest.java @@ -0,0 +1,65 @@ +/* + * 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; + +import junit.framework.TestCase; +import org.junit.Test; + +import java.util.EnumSet; + +public class ObjectTypeTest extends TestCase +{ + @Test + public void testIsSupported() + { + for (LegacyOperation operation : LegacyOperation.values()) + { + assertTrue(ObjectType.ALL.isSupported(operation)); + } + + for (ObjectType objectType : ObjectType.values()) + { + for (LegacyOperation operation : objectType.getOperations()) + { + assertTrue(objectType.isSupported(operation)); + } + final EnumSet<LegacyOperation> legacyOperations = EnumSet.allOf(LegacyOperation.class); + legacyOperations.removeAll(objectType.getOperations()); + for (LegacyOperation operation : legacyOperations) + { + assertFalse(objectType.isSupported(operation)); + } + } + } + + @Test + public void testTestToString() + { + assertEquals("All", ObjectType.ALL.toString()); + assertEquals("Broker", ObjectType.BROKER.toString()); + assertEquals("Exchange", ObjectType.EXCHANGE.toString()); + assertEquals("Group", ObjectType.GROUP.toString()); + assertEquals("Management", ObjectType.MANAGEMENT.toString()); + assertEquals("Method", ObjectType.METHOD.toString()); + assertEquals("Queue", ObjectType.QUEUE.toString()); + assertEquals("User", ObjectType.USER.toString()); + assertEquals("Virtualhost", ObjectType.VIRTUALHOST.toString()); + assertEquals("Virtualhostnode", ObjectType.VIRTUALHOSTNODE.toString()); + } +} diff --git a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/RuleCollectorTest.java b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/RuleCollectorTest.java index 9218caf..9ccbf45 100644 --- a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/RuleCollectorTest.java +++ b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/RuleCollectorTest.java @@ -44,10 +44,9 @@ public class RuleCollectorTest extends UnitTestBase RuleSet ruleSet = creator.createRuleSet(Mockito.mock(EventLoggerProvider.class)); assertNotNull(ruleSet); - assertEquals(2, ruleSet.getAllRules().size()); - assertEquals(newRule(RuleOutcome.ALLOW, LegacyOperation.ACCESS), ruleSet.getAllRules().get(1)); - assertEquals(newRule(RuleOutcome.DENY, LegacyOperation.PUBLISH, ObjectType.EXCHANGE), - ruleSet.getAllRules().get(0)); + assertEquals(2, ruleSet.size()); + assertEquals(newRule(RuleOutcome.ALLOW, LegacyOperation.ACCESS), ruleSet.get(1)); + assertEquals(newRule(RuleOutcome.DENY, LegacyOperation.PUBLISH, ObjectType.EXCHANGE), ruleSet.get(0)); } @Test @@ -61,10 +60,9 @@ public class RuleCollectorTest extends UnitTestBase RuleSet ruleSet = creator.createRuleSet(Mockito.mock(EventLoggerProvider.class)); assertNotNull(ruleSet); - assertEquals(2, ruleSet.getAllRules().size()); - assertEquals(newRule(RuleOutcome.ALLOW, LegacyOperation.ACCESS), ruleSet.getAllRules().get(1)); - assertEquals(newRule(RuleOutcome.DENY, LegacyOperation.PUBLISH, ObjectType.EXCHANGE), - ruleSet.getAllRules().get(0)); + assertEquals(2, ruleSet.size()); + assertEquals(newRule(RuleOutcome.ALLOW, LegacyOperation.ACCESS), ruleSet.get(1)); + assertEquals(newRule(RuleOutcome.DENY, LegacyOperation.PUBLISH, ObjectType.EXCHANGE), ruleSet.get(0)); } @Test diff --git a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/RuleSetTest.java b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/RuleSetTest.java index af2f643..165abfe 100644 --- a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/RuleSetTest.java +++ b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/RuleSetTest.java @@ -22,21 +22,31 @@ package org.apache.qpid.server.security.access.config; 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; import static org.mockito.Mockito.mock; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.ListIterator; import javax.security.auth.Subject; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; +import org.apache.qpid.server.logging.EventLogger; import org.apache.qpid.server.logging.EventLoggerProvider; +import org.apache.qpid.server.model.AuthenticationProvider; 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 org.apache.qpid.server.security.auth.TestPrincipalUtils; +import org.apache.qpid.server.security.auth.UsernamePrincipal; import org.apache.qpid.test.utils.UnitTestBase; /** @@ -99,7 +109,7 @@ public class RuleSetTest extends UnitTestBase .withPredicates(properties) .build()); ruleSet = createRuleSet(); - assertEquals(1, ruleSet.getRuleCount()); + assertEquals(1, ruleSet.size()); assertEquals(Result.ALLOWED, ruleSet.check(subject, operation, objectType, properties)); } @@ -108,7 +118,7 @@ public class RuleSetTest extends UnitTestBase { final RuleSet ruleSet = createRuleSet(); assertNotNull(ruleSet); - assertEquals(ruleSet.getRuleCount(), 0); + assertEquals(0, ruleSet.size()); assertEquals(ruleSet.getDefault(), ruleSet.check(_testSubject, LegacyOperation.ACCESS, ObjectType.VIRTUALHOST, EMPTY)); } @@ -325,7 +335,7 @@ public class RuleSetTest extends UnitTestBase @Test public void testExchangeCreate() { - ObjectProperties properties = new ObjectProperties(_exchangeName); + final ObjectProperties properties = new ObjectProperties(_exchangeName); properties.put(Property.TYPE, _exchangeType); assertDenyGrantAllow(_testSubject, LegacyOperation.CREATE, ObjectType.EXCHANGE, properties); @@ -367,7 +377,7 @@ public class RuleSetTest extends UnitTestBase .withPredicates(temporary) .build()); ruleSet = createRuleSet(); - assertEquals(1, ruleSet.getRuleCount()); + assertEquals(1, ruleSet.size()); assertEquals(Result.ALLOWED, ruleSet.check(_testSubject, LegacyOperation.CONSUME, ObjectType.QUEUE, temporary)); @@ -409,7 +419,7 @@ public class RuleSetTest extends UnitTestBase .build()); ruleSet = createRuleSet(); - assertEquals(2, ruleSet.getRuleCount()); + assertEquals(2, ruleSet.size()); assertEquals(Result.ALLOWED, ruleSet.check(_testSubject, LegacyOperation.CONSUME, ObjectType.QUEUE, normal)); @@ -450,7 +460,7 @@ public class RuleSetTest extends UnitTestBase .build()); ruleSet = createRuleSet(); - assertEquals(2, ruleSet.getRuleCount()); + assertEquals(2, ruleSet.size()); assertEquals(Result.ALLOWED, ruleSet.check(_testSubject, LegacyOperation.CONSUME, ObjectType.QUEUE, normal)); @@ -494,7 +504,7 @@ public class RuleSetTest extends UnitTestBase .build()); ruleSet = createRuleSet(); - assertEquals(2, ruleSet.getRuleCount()); + assertEquals(2, ruleSet.size()); assertEquals(Result.ALLOWED, ruleSet.check(_testSubject, LegacyOperation.CREATE, ObjectType.QUEUE, named)); @@ -533,7 +543,7 @@ public class RuleSetTest extends UnitTestBase .build()); ruleSet = createRuleSet(); - assertEquals(2, ruleSet.getRuleCount()); + assertEquals(2, ruleSet.size()); assertEquals(Result.ALLOWED, ruleSet.check(_testSubject, LegacyOperation.CREATE, ObjectType.QUEUE, named)); @@ -584,7 +594,7 @@ public class RuleSetTest extends UnitTestBase .build()); ruleSet = createRuleSet(); - assertEquals(3, ruleSet.getRuleCount()); + assertEquals(3, ruleSet.size()); assertEquals(Result.ALLOWED, ruleSet.check(_testSubject, LegacyOperation.CREATE, ObjectType.QUEUE, named)); @@ -620,7 +630,7 @@ public class RuleSetTest extends UnitTestBase .withPredicates(named) .build()); ruleSet = createRuleSet(); - assertEquals(2, ruleSet.getRuleCount()); + assertEquals(2, ruleSet.size()); assertEquals(Result.ALLOWED, ruleSet.check(_testSubject, LegacyOperation.CREATE, ObjectType.QUEUE, named)); @@ -654,7 +664,7 @@ public class RuleSetTest extends UnitTestBase .withPredicates(named) .build()); ruleSet = createRuleSet(); - assertEquals(2, ruleSet.getRuleCount()); + assertEquals(2, ruleSet.size()); assertEquals(Result.DENIED, ruleSet.check(_testSubject, LegacyOperation.CREATE, ObjectType.QUEUE, named)); assertEquals(Result.ALLOWED, @@ -674,7 +684,7 @@ public class RuleSetTest extends UnitTestBase .withObject(ObjectType.VIRTUALHOST) .build()); final RuleSet ruleSet = createRuleSet(); - assertEquals(1, ruleSet.getRuleCount()); + assertEquals(1, ruleSet.size()); assertEquals(Result.ALLOWED, ruleSet.check(TestPrincipalUtils.createTestSubject("usera"), LegacyOperation.ACCESS, ObjectType.VIRTUALHOST, EMPTY)); @@ -701,7 +711,7 @@ public class RuleSetTest extends UnitTestBase .withObject(ObjectType.VIRTUALHOST) .build()); final RuleSet ruleSet = createRuleSet(); - assertEquals(2, ruleSet.getRuleCount()); + assertEquals(2, ruleSet.size()); assertEquals(Result.ALLOWED, ruleSet.check(TestPrincipalUtils.createTestSubject("usera", allowGroup), LegacyOperation @@ -720,8 +730,8 @@ public class RuleSetTest extends UnitTestBase @Test public void testAllowDeterminedByRuleOrder() { - String group = "group"; - String user = "user"; + final String group = "group"; + final String user = "user"; _ruleCollector.addRule(1, new Builder() .withIdentity(user) @@ -736,7 +746,7 @@ public class RuleSetTest extends UnitTestBase .withObject(ObjectType.VIRTUALHOST) .build()); final RuleSet ruleSet = createRuleSet(); - assertEquals(2, ruleSet.getRuleCount()); + assertEquals(2, ruleSet.size()); assertEquals(Result.ALLOWED, ruleSet.check(TestPrincipalUtils.createTestSubject(user, group), LegacyOperation.ACCESS, ObjectType.VIRTUALHOST, EMPTY)); @@ -749,8 +759,8 @@ public class RuleSetTest extends UnitTestBase @Test public void testDenyDeterminedByRuleOrder() { - String group = "aclgroup"; - String user = "usera"; + final String group = "aclgroup"; + final String user = "usera"; _ruleCollector.addRule(1, new Builder() .withIdentity(group) @@ -766,7 +776,7 @@ public class RuleSetTest extends UnitTestBase .build()); final RuleSet ruleSet = createRuleSet(); - assertEquals(2, ruleSet.getRuleCount()); + assertEquals(2, ruleSet.size()); assertEquals(Result.DENIED, ruleSet.check(TestPrincipalUtils.createTestSubject(user, group), LegacyOperation.ACCESS, ObjectType.VIRTUALHOST, EMPTY)); @@ -847,7 +857,7 @@ public class RuleSetTest extends UnitTestBase .withObject(ObjectType.QUEUE) .build()); final RuleSet ruleSet = createRuleSet(); - assertEquals(1, ruleSet.getRuleCount()); + assertEquals(1, ruleSet.size()); assertEquals(Result.ALLOWED, ruleSet.check(_testSubject, @@ -872,7 +882,7 @@ public class RuleSetTest extends UnitTestBase .withObject(ObjectType.QUEUE) .build()); final RuleSet ruleSet = createRuleSet(); - assertEquals(1, ruleSet.getRuleCount()); + assertEquals(1, ruleSet.size()); assertEquals(Result.ALLOWED, ruleSet.check(_testSubject, @@ -887,4 +897,719 @@ public class RuleSetTest extends UnitTestBase new ObjectProperties(Property.CREATED_BY, "anotherUser"))); } + + @Test + public void testSuppressedRules() + { + _ruleCollector.addRule(1, new Builder() + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .withPredicate(Property.NAME, "testExchange") + .build()); + _ruleCollector.addRule(2, new Builder() + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.DENY) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .build()); + + _ruleCollector.addRule(3, new Builder() + .withIdentity(Rule.ALL) + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .build()); + _ruleCollector.addRule(4, new Builder() + .withIdentity(Rule.ALL) + .withOutcome(RuleOutcome.DENY) + .withOperation(LegacyOperation.ALL) + .withObject(ObjectType.ALL) + .build()); + + final RuleSet ruleSet = createRuleSet(); + assertEquals(4, ruleSet.size()); + + assertEquals(Result.ALLOWED, ruleSet.check(_testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, new ObjectProperties("testExchange"))); + assertEquals(Result.DENIED, ruleSet.check(_testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, new ObjectProperties("exchange"))); + assertEquals(Result.DENIED, ruleSet.check(_testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, new ObjectProperties())); + } + + @Test + public void testPublishToExchange() + { + _ruleCollector.addRule(1, new Builder() + .withPredicate(Property.NAME, "broadcast") + .withPredicate(Property.ROUTING_KEY, "broadcast.*") + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .build()); + + _ruleCollector.addRule(3, new Builder() + .withPredicate(Property.NAME, "broadcast") + .withPredicate(Property.ROUTING_KEY, "rs.broadcast.*") + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .build()); + + _ruleCollector.addRule(7, new Builder() + .withPredicate(Property.NAME, "rs.broadcast") + .withPredicate(Property.ROUTING_KEY, "rs.broadcast.*") + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .build()); + + _ruleCollector.addRule(17, new Builder() + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.DENY) + .withOperation(LegacyOperation.ALL) + .withObject(ObjectType.ALL) + .build()); + + final RuleSet ruleSet = createRuleSet(); + assertEquals(4, ruleSet.size()); + + assertEquals(Result.DENIED, ruleSet.check(_testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, new ObjectProperties())); + + ObjectProperties object = new ObjectProperties("broadcast"); + assertEquals(Result.DENIED, ruleSet.check(_testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, object)); + + object = new ObjectProperties("broadcast"); + object.put(Property.ROUTING_KEY, "broadcast.public"); + assertEquals(Result.ALLOWED, ruleSet.check(_testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, object)); + + object = new ObjectProperties("broadcast"); + object.put(Property.ROUTING_KEY, "rs.broadcast.public"); + assertEquals(Result.ALLOWED, ruleSet.check(_testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, object)); + + object = new ObjectProperties("rs.broadcast"); + object.put(Property.ROUTING_KEY, "rs.broadcast.public"); + assertEquals(Result.ALLOWED, ruleSet.check(_testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, object)); + + object = new ObjectProperties("broadcast"); + object.put(Property.ROUTING_KEY, "queue"); + assertEquals(Result.DENIED, ruleSet.check(_testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, object)); + + object = new ObjectProperties("brs"); + object.put(Property.ROUTING_KEY, "rs.broadcast.public"); + assertEquals(Result.DENIED, ruleSet.check(_testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, object)); + + // Another user + final Subject testSubject = TestPrincipalUtils.createTestSubject("Java"); + object = new ObjectProperties("rs.broadcast"); + object.put(Property.ROUTING_KEY, "rs.broadcast.public"); + assertEquals(Result.DEFER, ruleSet.check(testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, object)); + } + + @Test + public void testPublishToExchange_OwnerBased() + { + _ruleCollector.addRule(1, new Builder() + .withPredicate(Property.NAME, "broadcast") + .withPredicate(Property.ROUTING_KEY, "broadcast.*") + .withOwner() + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .build()); + + _ruleCollector.addRule(3, new Builder() + .withPredicate(Property.NAME, "broadcast") + .withPredicate(Property.ROUTING_KEY, "rs.broadcast.*") + .withOwner() + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .build()); + + _ruleCollector.addRule(11, new Builder() + .withPredicate(Property.NAME, "broadcast") + .withPredicate(Property.QUEUE_NAME, "QQ") + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .build()); + + _ruleCollector.addRule(17, new Builder() + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.DENY) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.ALL) + .build()); + + final RuleSet ruleSet = createRuleSet(); + assertEquals(4, ruleSet.size()); + + assertEquals(Result.DENIED, ruleSet.check(_testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, new ObjectProperties())); + + // User = owner + ObjectProperties object = new ObjectProperties("broadcast"); + object.put(Property.ROUTING_KEY, "brs"); + object.setCreatedBy(TEST_USER); + assertEquals(Result.DENIED, ruleSet.check(_testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, object)); + + object = new ObjectProperties("broadcast"); + object.put(Property.ROUTING_KEY, "rs.broadcast.public"); + object.setCreatedBy(TEST_USER); + assertEquals(Result.ALLOWED, ruleSet.check(_testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, object)); + } + + @Test + public void testPublishToExchange_OwnerBased_withoutAuthPrincipal() + { + _ruleCollector.addRule(1, new Builder() + .withPredicate(Property.NAME, "broadcast") + .withPredicate(Property.ROUTING_KEY, "broadcast.*") + .withOwner() + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .build()); + + _ruleCollector.addRule(3, new Builder() + .withPredicate(Property.NAME,"broadcast") + .withPredicate(Property.ROUTING_KEY, "rs.broadcast.*") + .withOwner() + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .build()); + + _ruleCollector.addRule(11, new Builder() + .withPredicate(Property.NAME,"broadcast") + .withPredicate(Property.QUEUE_NAME, "QQ") + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .build()); + + _ruleCollector.addRule(17, new Builder() + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.DENY) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.ALL) + .build()); + + final RuleSet ruleSet = createRuleSet(); + assertEquals(4, ruleSet.size()); + + // User without authentication principal + final Subject notAuthentificated = new Subject(false, + Collections.singleton(new UsernamePrincipal(TEST_USER, Mockito.mock(AuthenticationProvider.class))), + Collections.emptySet(), + Collections.emptySet()); + + ObjectProperties object = new ObjectProperties("broadcast"); + object.put(Property.ROUTING_KEY, "rs.broadcast.public"); + object.setCreatedBy(TEST_USER); + assertEquals(Result.DENIED, ruleSet.check(notAuthentificated, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, object)); + + object = new ObjectProperties("broadcast"); + object.put(Property.QUEUE_NAME, "QQ"); + assertEquals(Result.ALLOWED, ruleSet.check(notAuthentificated, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, object)); + } + + @Test + public void testPublishToExchange_OwnerBased_byAnotherUser() + { + _ruleCollector.addRule(1, new Builder() + .withPredicate(Property.NAME,"broadcast") + .withPredicate(Property.ROUTING_KEY, "broadcast.*") + .withOwner() + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .build()); + + _ruleCollector.addRule(3, new Builder() + .withPredicate(Property.NAME,"broadcast") + .withPredicate(Property.ROUTING_KEY, "rs.broadcast.*") + .withOwner() + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .build()); + + _ruleCollector.addRule(11, new Builder() + .withPredicate(Property.NAME,"broadcast") + .withPredicate(Property.QUEUE_NAME, "QQ") + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .build()); + + _ruleCollector.addRule(17, new Builder() + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.DENY) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.ALL) + .build()); + + final RuleSet ruleSet = createRuleSet(); + assertEquals(4, ruleSet.size()); + + // Created be other user + ObjectProperties object = new ObjectProperties("broadcast"); + object.put(Property.ROUTING_KEY, "broadcast.public"); + object.setCreatedBy("another"); + assertEquals(Result.DENIED, ruleSet.check(_testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, object)); + + object = new ObjectProperties("broadcast"); + object.put(Property.QUEUE_NAME, "QQ"); + object.setCreatedBy("another"); + assertEquals(Result.ALLOWED, ruleSet.check(_testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, object)); + + // Action is performed by another user + final Subject testSubject = TestPrincipalUtils.createTestSubject("Java"); + object = new ObjectProperties("broadcast"); + assertEquals(Result.DEFER, ruleSet.check(testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, object)); + + // Action is performed by another user == owner + object = new ObjectProperties("broadcast"); + object.setCreatedBy("Java"); + assertEquals(Result.DEFER, ruleSet.check(testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, object)); + + object = new ObjectProperties("broadcast"); + object.put(Property.ROUTING_KEY, "rs.broadcast.public"); + object.setCreatedBy("Java"); + assertEquals(Result.ALLOWED, ruleSet.check(testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, object)); + } + + @Test + public void testPublishToExchange_OwnerBased_withGenericRule() + { + _ruleCollector.addRule(1, new Builder() + .withPredicate(Property.NAME, "broadcast") + .withPredicate(Property.ROUTING_KEY, "broadcast.*") + .withOwner() + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .build()); + + _ruleCollector.addRule(3, new Builder() + .withPredicate(Property.NAME, "broadcast") + .withPredicate(Property.ROUTING_KEY, "rs.broadcast.*") + .withOwner() + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .build()); + + _ruleCollector.addRule(11, new Builder() + .withPredicate(Property.NAME, "broadcast") + .withPredicate(Property.QUEUE_NAME, "QQ") + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .build()); + + _ruleCollector.addRule(17, new Builder() + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.DENY) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.ALL) + .build()); + + _ruleCollector.addRule(27, new Builder() + .withIdentity(Rule.ALL) + .withOutcome(RuleOutcome.DENY) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.ALL) + .build()); + + final RuleSet ruleSet = createRuleSet(); + assertEquals(5, ruleSet.size()); + + // Action is performed by another user + final Subject testSubject = TestPrincipalUtils.createTestSubject("Java"); + + ObjectProperties object = new ObjectProperties("broadcast"); + assertEquals(Result.DENIED, ruleSet.check(testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, object)); + + // Action is performed by another user == owner + object = new ObjectProperties("broadcast"); + object.setCreatedBy("Java"); + assertEquals(Result.DENIED, ruleSet.check(testSubject, LegacyOperation.PUBLISH, ObjectType.EXCHANGE, object)); + } + + @Test + public void testList_UnsupportedException() + { + _ruleCollector.addRule(1, new Builder() + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .withPredicate(Property.NAME, "broadcast") + .build()); + _ruleCollector.addRule(3, new Builder() + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.ACCESS) + .withObject(ObjectType.VIRTUALHOST) + .build()); + _ruleCollector.addRule(17, new Builder() + .withIdentity(Rule.ALL) + .withOutcome(RuleOutcome.DENY) + .withOperation(LegacyOperation.ALL) + .withObject(ObjectType.ALL) + .build()); + + final RuleSet ruleSet = createRuleSet(); + assertEquals(3, ruleSet.size()); + + final Rule rule = new Builder() + .withIdentity(TEST_USER) + .withOperation(LegacyOperation.ACCESS) + .withOutcome(RuleOutcome.ALLOW) + .build(); + try + { + ruleSet.add(rule); + fail("An exception is expected!"); + } + catch (RuntimeException e) + { + // Nothing to do + } + + try + { + ruleSet.remove(ruleSet.get(1)); + fail("An exception is expected!"); + } + catch (RuntimeException e) + { + // Nothing to do + } + + try + { + ruleSet.addAll(Collections.singleton(rule)); + fail("An exception is expected!"); + } + catch (RuntimeException e) + { + // Nothing to do + } + + try + { + ruleSet.removeAll(new ArrayList<>(ruleSet)); + fail("An exception is expected!"); + } + catch (RuntimeException e) + { + // Nothing to do + } + + try + { + ruleSet.retainAll(Collections.singleton(rule)); + fail("An exception is expected!"); + } + catch (RuntimeException e) + { + // Nothing to do + } + + try + { + ruleSet.clear(); + fail("An exception is expected!"); + } + catch (RuntimeException e) + { + // Nothing to do + } + + try + { + ruleSet.addAll(1, Collections.singleton(rule)); + fail("An exception is expected!"); + } + catch (RuntimeException e) + { + // Nothing to do + } + + try + { + ruleSet.set(1, rule); + fail("An exception is expected!"); + } + catch (RuntimeException e) + { + // Nothing to do + } + + try + { + ruleSet.add(1, rule); + fail("An exception is expected!"); + } + catch (RuntimeException e) + { + // Nothing to do + } + + try + { + ruleSet.remove(1); + fail("An exception is expected!"); + } + catch (RuntimeException e) + { + // Nothing to do + } + } + + @Test + public void testList() + { + _ruleCollector.addRule(1, new Builder() + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .withPredicate(Property.NAME, "broadcast") + .build()); + + _ruleCollector.addRule(3, new Builder() + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.ACCESS) + .withObject(ObjectType.VIRTUALHOST) + .build()); + + _ruleCollector.addRule(17, new Builder() + .withIdentity(Rule.ALL) + .withOutcome(RuleOutcome.DENY) + .withOperation(LegacyOperation.ALL) + .withObject(ObjectType.ALL) + .build()); + + final RuleSet ruleSet = createRuleSet(); + assertNotNull(ruleSet); + assertEquals(3, ruleSet.size()); + assertFalse(ruleSet.isEmpty()); + + final Rule rule = new Builder() + .withIdentity(TEST_USER) + .withOperation(LegacyOperation.ACCESS) + .withObject(ObjectType.VIRTUALHOST) + .withOutcome(RuleOutcome.ALLOW) + .build(); + + final Rule all = new Builder() + .withIdentity(Rule.ALL) + .withOperation(LegacyOperation.ALL) + .withObject(ObjectType.ALL) + .withOutcome(RuleOutcome.DENY) + .build(); + + assertTrue(ruleSet.contains(rule)); + assertTrue(ruleSet.containsAll(Arrays.asList(rule, all))); + assertEquals(rule, ruleSet.get(1)); + assertEquals(1, ruleSet.indexOf(rule)); + assertEquals(1, ruleSet.lastIndexOf(rule)); + } + + @Test + public void testList_Arrays() + { + _ruleCollector.addRule(1, new Builder() + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .withPredicate(Property.NAME, "broadcast") + .build()); + + _ruleCollector.addRule(3, new Builder() + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.ACCESS) + .withObject(ObjectType.VIRTUALHOST) + .build()); + + _ruleCollector.addRule(17, new Builder() + .withIdentity(Rule.ALL) + .withOutcome(RuleOutcome.DENY) + .withOperation(LegacyOperation.ALL) + .withObject(ObjectType.ALL) + .build()); + + final RuleSet ruleSet = createRuleSet(); + assertNotNull(ruleSet); + + Object[] array = ruleSet.toArray(); + Rule[] ruleArray = ruleSet.toArray(new Rule[0]); + assertEquals(3, array.length); + assertEquals(3, ruleArray.length); + + for (int i = 0; i < array.length; i++) + { + assertEquals(ruleSet.get(i), array[i]); + assertEquals(ruleSet.get(i), ruleArray[i]); + } + } + + @Test + public void testList_Iterators() + { + _ruleCollector.addRule(1, new Builder() + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .withPredicate(Property.NAME, "broadcast") + .build()); + _ruleCollector.addRule(3, new Builder() + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.ACCESS) + .withObject(ObjectType.VIRTUALHOST) + .build()); + _ruleCollector.addRule(17, new Builder() + .withIdentity(Rule.ALL) + .withOutcome(RuleOutcome.DENY) + .withOperation(LegacyOperation.ALL) + .withObject(ObjectType.ALL) + .build()); + + final RuleSet ruleSet = createRuleSet(); + assertNotNull(ruleSet); + + int j = 0; + for (Rule r : ruleSet) + { + assertEquals(ruleSet.get(j++), r); + } + + ListIterator<Rule> iterator = ruleSet.listIterator(); + assertNotNull(iterator); + while (iterator.hasNext()) + { + assertEquals(ruleSet.get(iterator.nextIndex()), iterator.next()); + try + { + iterator.remove(); + fail("An exception is expected!"); + } + catch (RuntimeException e) + { + // + } + } + + iterator = ruleSet.listIterator(1); + assertNotNull(iterator); + while (iterator.hasNext()) + { + assertEquals(ruleSet.get(iterator.nextIndex()), iterator.next()); + try + { + iterator.remove(); + fail("An exception is expected!"); + } + catch (RuntimeException e) + { + // + } + } + } + + @Test + public void testList_subList() + { + _ruleCollector.addRule(1, new Builder() + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.PUBLISH) + .withObject(ObjectType.EXCHANGE) + .withPredicate(Property.NAME, "broadcast") + .build()); + + _ruleCollector.addRule(3, new Builder() + .withIdentity(TEST_USER) + .withOutcome(RuleOutcome.ALLOW) + .withOperation(LegacyOperation.ACCESS) + .withObject(ObjectType.VIRTUALHOST) + .build()); + + _ruleCollector.addRule(17, new Builder() + .withIdentity(Rule.ALL) + .withOutcome(RuleOutcome.DENY) + .withOperation(LegacyOperation.ALL) + .withObject(ObjectType.ALL) + .build()); + + final Rule rule = new Builder() + .withIdentity(TEST_USER) + .withOperation(LegacyOperation.ACCESS) + .withObject(ObjectType.VIRTUALHOST) + .withOutcome(RuleOutcome.ALLOW) + .build(); + + final RuleSet ruleSet = createRuleSet(); + assertNotNull(ruleSet); + + assertNotNull(ruleSet.subList(1, 2)); + assertEquals(rule, ruleSet.subList(1, 2).get(0)); + + try + { + ruleSet.subList(1, 2).add(rule); + fail("An exception is expected!"); + } + catch (RuntimeException e) + { + // + } + } + + @Test + public void testGetEventLogger() + { + final Rule rule = new Builder() + .withIdentity(TEST_USER) + .withOperation(LegacyOperation.ACCESS) + .withObject(ObjectType.VIRTUALHOST) + .withOutcome(RuleOutcome.ALLOW) + .build(); + + final EventLogger logger = mock(EventLogger.class); + final RuleSet ruleSet = RuleSet.newInstance(() -> logger, Collections.singletonList(rule), Result.DENIED); + assertNotNull(ruleSet); + assertEquals(logger, ruleSet.getEventLogger()); + } + + @Test + public void testGetDefault() + { + final Rule rule = new Builder() + .withIdentity(TEST_USER) + .withOperation(LegacyOperation.ACCESS) + .withObject(ObjectType.VIRTUALHOST) + .withOutcome(RuleOutcome.ALLOW) + .build(); + + final EventLoggerProvider logger = mock(EventLoggerProvider.class); + final RuleSet ruleSet = RuleSet.newInstance(logger, Collections.singletonList(rule), Result.ALLOWED); + assertNotNull(ruleSet); + assertEquals(Result.ALLOWED, ruleSet.getDefault()); + } } diff --git a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/RuleTest.java b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/RuleTest.java index f04d3ba..8697491 100644 --- a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/RuleTest.java +++ b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/RuleTest.java @@ -432,22 +432,60 @@ public class RuleTest extends UnitTestBase @Test public void testIsAll() { - assertTrue(Rule.isAll("all")); - assertTrue(Rule.isAll("All")); - assertTrue(Rule.isAll("ALL")); - assertFalse(Rule.isAll("any")); - assertFalse(Rule.isAll("Any")); - assertFalse(Rule.isAll("ANY")); + Rule rule = new Rule.Builder().withIdentity("all").build(); + assertTrue(rule.isForAll()); + assertTrue(rule.isForOwnerOrAll()); + + rule = new Rule.Builder().withIdentity("All").build(); + assertTrue(rule.isForAll()); + assertTrue(rule.isForOwnerOrAll()); + + rule = new Rule.Builder().withIdentity("ALL").build(); + assertTrue(rule.isForAll()); + assertTrue(rule.isForOwnerOrAll()); + + rule = new Rule.Builder().withIdentity("any").build(); + assertFalse(rule.isForAll()); + assertFalse(rule.isForOwnerOrAll()); + + rule = new Rule.Builder().withIdentity("Any").build(); + assertFalse(rule.isForAll()); + assertFalse(rule.isForOwnerOrAll()); + + rule = new Rule.Builder().withIdentity("ANY").build(); + assertFalse(rule.isForAll()); + assertFalse(rule.isForOwnerOrAll()); } @Test public void testIsOwner() { - assertTrue(Rule.isOwner("owner")); - assertTrue(Rule.isOwner("Owner")); - assertTrue(Rule.isOwner("OWNER")); - assertFalse(Rule.isOwner("any")); - assertFalse(Rule.isOwner("Any")); - assertFalse(Rule.isOwner("ANY")); + Rule rule = new Rule.Builder().withIdentity("owner").build(); + assertTrue(rule.isForOwner()); + assertTrue(rule.isForOwnerOrAll()); + + rule = new Rule.Builder().withIdentity("Owner").build(); + assertTrue(rule.isForOwner()); + assertTrue(rule.isForOwnerOrAll()); + + rule = new Rule.Builder().withIdentity("OWNER").build(); + assertTrue(rule.isForOwner()); + assertTrue(rule.isForOwnerOrAll()); + + rule = new Rule.Builder().withIdentity("any").build(); + assertFalse(rule.isForOwner()); + assertFalse(rule.isForOwnerOrAll()); + + rule = new Rule.Builder().withIdentity("Any").build(); + assertFalse(rule.isForOwner()); + assertFalse(rule.isForOwnerOrAll()); + + rule = new Rule.Builder().withIdentity("ANY").build(); + assertFalse(rule.isForOwner()); + assertFalse(rule.isForOwnerOrAll()); + + rule = new Rule.Builder().withOwner().build(); + assertTrue(rule.isForOwner()); + assertTrue(rule.isForOwnerOrAll()); } } 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 4eb6630..a9baaea 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 @@ -77,6 +77,7 @@ public class RulePredicateTest extends UnitTestBase assertTrue(rule.matches(LegacyOperation.UPDATE, ObjectType.VIRTUALHOST, action, _subject)); assertFalse(rule.matches(LegacyOperation.ACCESS, ObjectType.VIRTUALHOST, action, _subject)); + assertTrue(rule.anyPropertiesMatch()); } @Test @@ -97,6 +98,7 @@ public class RulePredicateTest extends UnitTestBase assertFalse(rule.matches(LegacyOperation.UPDATE, ObjectType.VIRTUALHOST, action, _subject)); assertTrue(rule.matches(LegacyOperation.ACCESS, ObjectType.VIRTUALHOST, action, _subject)); + assertFalse(rule.anyPropertiesMatch()); } @Test @@ -117,6 +119,7 @@ public class RulePredicateTest extends UnitTestBase assertFalse(rule.matches(LegacyOperation.UPDATE, ObjectType.VIRTUALHOST, action, _subject)); assertFalse(rule.matches(LegacyOperation.ACCESS, ObjectType.VIRTUALHOST, action, _subject)); + assertFalse(rule.anyPropertiesMatch()); } @Test @@ -137,6 +140,7 @@ public class RulePredicateTest extends UnitTestBase action.put(Property.METHOD_NAME, "publish"); assertTrue(rule.matches(LegacyOperation.PUBLISH, ObjectType.EXCHANGE, action, _subject)); + assertFalse(rule.anyPropertiesMatch()); } @Test @@ -157,6 +161,7 @@ public class RulePredicateTest extends UnitTestBase assertFalse(rule.matches(LegacyOperation.PUBLISH, ObjectType.EXCHANGE, action, _subject)); assertFalse(rule.matches(LegacyOperation.PUBLISH, ObjectType.EXCHANGE, new ObjectProperties(), _subject)); + assertFalse(rule.anyPropertiesMatch()); } @Test @@ -176,6 +181,7 @@ public class RulePredicateTest extends UnitTestBase action.put(Property.METHOD_NAME, "publish"); assertTrue(rule.matches(LegacyOperation.PUBLISH, ObjectType.EXCHANGE, action, _subject)); + assertFalse(rule.anyPropertiesMatch()); } @Test @@ -196,6 +202,7 @@ public class RulePredicateTest extends UnitTestBase assertFalse(rule.matches(LegacyOperation.PUBLISH, ObjectType.EXCHANGE, action, _subject)); assertFalse(rule.matches(LegacyOperation.PUBLISH, ObjectType.EXCHANGE, new ObjectProperties(), _subject)); + assertFalse(rule.anyPropertiesMatch()); } @Test @@ -215,6 +222,7 @@ public class RulePredicateTest extends UnitTestBase action.put(Property.METHOD_NAME, "publish"); assertTrue(rule.matches(LegacyOperation.PUBLISH, ObjectType.EXCHANGE, action, _subject)); + assertFalse(rule.anyPropertiesMatch()); } @Test @@ -235,6 +243,7 @@ public class RulePredicateTest extends UnitTestBase assertFalse(rule.matches(LegacyOperation.PUBLISH, ObjectType.EXCHANGE, action, _subject)); assertFalse(rule.matches(LegacyOperation.PUBLISH, ObjectType.EXCHANGE, new ObjectProperties(), _subject)); + assertFalse(rule.anyPropertiesMatch()); } @Test @@ -255,6 +264,7 @@ public class RulePredicateTest extends UnitTestBase action.put(Property.METHOD_NAME, "publish"); assertTrue(rule.matches(LegacyOperation.PUBLISH, ObjectType.EXCHANGE, action, _subject)); + assertFalse(rule.anyPropertiesMatch()); } @Test @@ -269,6 +279,7 @@ public class RulePredicateTest extends UnitTestBase .withOutcome(RuleOutcome.ALLOW) .build(_firewallRuleFactory); + assertFalse(rule.anyPropertiesMatch()); ObjectProperties action = new ObjectProperties(); action.put(Property.ROUTING_KEY, "generic.public"); action.setName("broadcast"); @@ -315,6 +326,7 @@ public class RulePredicateTest extends UnitTestBase action.put(Property.DURABLE, true); assertTrue(rule.matches(LegacyOperation.PUBLISH, ObjectType.EXCHANGE, action, _subject)); + assertFalse(rule.anyPropertiesMatch()); } @Test @@ -331,6 +343,7 @@ public class RulePredicateTest extends UnitTestBase .withOutcome(RuleOutcome.ALLOW) .build(_firewallRuleFactory); + assertFalse(rule.anyPropertiesMatch()); ObjectProperties action = new ObjectProperties(); action.put(Property.ROUTING_KEY, "broadcast.public"); action.setName("broadcast"); @@ -386,6 +399,7 @@ public class RulePredicateTest extends UnitTestBase .build(_firewallRuleFactory); assertTrue(rule.matches(LegacyOperation.PUBLISH, ObjectType.EXCHANGE, new ObjectProperties(), _subject)); + assertFalse(rule.anyPropertiesMatch()); } @Test @@ -400,5 +414,6 @@ public class RulePredicateTest extends UnitTestBase .build(_firewallRuleFactory); assertFalse(rule.matches(LegacyOperation.PUBLISH, ObjectType.EXCHANGE, new ObjectProperties(), _subject)); + assertFalse(rule.anyPropertiesMatch()); } } \ No newline at end of file diff --git a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleOutcomeTest.java b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleOutcomeTest.java new file mode 100644 index 0000000..03b32bd --- /dev/null +++ b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleOutcomeTest.java @@ -0,0 +1,62 @@ +package org.apache.qpid.server.security.access.plugins; + +import junit.framework.TestCase; +import org.apache.qpid.server.logging.EventLogger; +import org.apache.qpid.server.logging.EventLoggerProvider; +import org.apache.qpid.server.logging.LogMessage; +import org.apache.qpid.server.security.Result; +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.ObjectType; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +public class RuleOutcomeTest extends TestCase +{ + private EventLogger _logger; + private EventLoggerProvider _provider; + + @Override + public void setUp() + { + _logger = Mockito.mock(EventLogger.class); + _provider =() ->_logger; + } + + @Test + public void testLogResult() + { + assertEquals(Result.ALLOWED, RuleOutcome.ALLOW.logResult(_provider, LegacyOperation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties())); + assertEquals(Result.DENIED, RuleOutcome.DENY.logResult(_provider, LegacyOperation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties())); + Mockito.verify(_logger, Mockito.never()).message(Mockito.any(LogMessage.class)); + } + + @Test + public void testLogDeniedResult() { + assertEquals(Result.DENIED, RuleOutcome.DENY_LOG.logResult(_provider, LegacyOperation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties())); + + final ArgumentCaptor<LogMessage> captor = ArgumentCaptor.forClass(LogMessage.class); + Mockito.verify(_logger, Mockito.times(1)).message(captor.capture()); + + final LogMessage message = captor.getValue(); + assertNotNull(message); + assertTrue(message.toString().contains("Denied")); + assertTrue(message.toString().contains(LegacyOperation.ACCESS.toString())); + assertTrue(message.toString().contains(ObjectType.VIRTUALHOST.toString())); + } + + @Test + public void testLogAllowResult() { + assertEquals(Result.ALLOWED, RuleOutcome.ALLOW_LOG.logResult(_provider, LegacyOperation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties())); + + final ArgumentCaptor<LogMessage> captor = ArgumentCaptor.forClass(LogMessage.class); + Mockito.verify(_logger, Mockito.times(1)).message(captor.capture()); + + final LogMessage message = captor.getValue(); + assertNotNull(message); + assertTrue(message.toString().contains("Allowed")); + assertTrue(message.toString().contains(LegacyOperation.ACCESS.toString())); + assertTrue(message.toString().contains(ObjectType.VIRTUALHOST.toString())); + } +} diff --git a/systests/qpid-systests-jms_1.1/src/test/java/org/apache/qpid/systests/jms_1_1/extensions/acl/MessagingACLTest.java b/systests/qpid-systests-jms_1.1/src/test/java/org/apache/qpid/systests/jms_1_1/extensions/acl/MessagingACLTest.java index ba01908..e3b77f5 100644 --- a/systests/qpid-systests-jms_1.1/src/test/java/org/apache/qpid/systests/jms_1_1/extensions/acl/MessagingACLTest.java +++ b/systests/qpid-systests-jms_1.1/src/test/java/org/apache/qpid/systests/jms_1_1/extensions/acl/MessagingACLTest.java @@ -65,14 +65,10 @@ import org.apache.qpid.server.model.Group; import org.apache.qpid.server.model.GroupMember; import org.apache.qpid.server.model.Protocol; import org.apache.qpid.server.security.access.config.AclFileParser; -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.ObjectType; import org.apache.qpid.server.security.access.config.Rule; import org.apache.qpid.server.security.access.config.RuleSet; import org.apache.qpid.server.security.access.plugins.AclRule; import org.apache.qpid.server.security.access.plugins.RuleBasedVirtualHostAccessControlProvider; -import org.apache.qpid.server.security.access.plugins.RuleOutcome; import org.apache.qpid.server.security.group.GroupProviderImpl; import org.apache.qpid.systests.JmsTestBase; @@ -852,8 +848,7 @@ public class MessagingACLTest extends JmsTestBase try(StringReader stringReader = new StringReader(Arrays.stream(rules).collect(Collectors.joining(LINE_SEPARATOR)))) { RuleSet ruleSet = AclFileParser.parse(stringReader, eventLoggerProvider); - final List<Rule> parsedRules = ruleSet.getAllRules(); - for(final Rule rule: parsedRules) + for (final Rule rule: ruleSet) { aclRules.add(rule.asAclRule()); } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org For additional commands, e-mail: commits-h...@qpid.apache.org