diff options
Diffstat (limited to 'java/broker-plugins/access-control')
33 files changed, 2040 insertions, 584 deletions
diff --git a/java/broker-plugins/access-control/MANIFEST.MF b/java/broker-plugins/access-control/MANIFEST.MF deleted file mode 100644 index a8fb99995e..0000000000 --- a/java/broker-plugins/access-control/MANIFEST.MF +++ /dev/null @@ -1,41 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Qpid Broker-Plugins Access Control -Bundle-SymbolicName: broker-plugins-access-control -Bundle-Description: Access control plugin for Qpid. -Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt -Bundle-DocURL: http://qpid.apache.org/acl.html -Bundle-Version: 1.0.0 -Bundle-Activator: org.apache.qpid.server.security.access.plugins.AccessControlActivator -Bundle-RequiredExecutionEnvironment: JavaSE-1.5 -Bundle-ClassPath: . -Bundle-ActivationPolicy: lazy -Import-Package: org.apache.qpid, - org.apache.qpid.exchange, - org.apache.qpid.framing, - org.apache.qpid.protocol, - org.apache.qpid.server.configuration, - org.apache.qpid.server.configuration.plugins, - org.apache.qpid.server.exchange, - org.apache.qpid.server.logging, - org.apache.qpid.server.logging.actors, - org.apache.qpid.server.logging.subjects, - org.apache.qpid.server.plugins, - org.apache.qpid.server.queue, - org.apache.qpid.server.registry, - org.apache.qpid.server.security, - org.apache.qpid.server.security.access, - org.apache.qpid.server.virtualhost, - org.apache.qpid.util, - org.apache.commons.configuration;version=1.0.0, - org.apache.commons.lang;version=1.0.0, - org.apache.commons.lang.builder;version=1.0.0, - org.apache.log4j;version=1.0.0, - javax.management;version=1.0.0, - javax.management.openmbean;version=1.0.0, - javax.security.auth;version=1.0.0, - org.osgi.util.tracker;version=1.0.0, - org.osgi.framework;version=1.3 -Private-Package: org.apache.qpid.server.security.access.config, - org.apache.qpid.server.security.access.logging -Export-Package: org.apache.qpid.server.security.access.plugins diff --git a/java/broker-plugins/access-control/build.xml b/java/broker-plugins/access-control/build.xml index df3346788c..4debdcb95a 100644 --- a/java/broker-plugins/access-control/build.xml +++ b/java/broker-plugins/access-control/build.xml @@ -18,13 +18,13 @@ --> <project name="Qpid Broker-Plugins Access Control" default="build"> <property name="module.depends" value="common broker" /> - <property name="module.test.depends" value="test common/test broker/test management/common systests" /> + <property name="module.test.depends" value="common/tests broker/tests management/common" /> - <property name="module.manifest" value="MANIFEST.MF" /> - <property name="module.plugin" value="true" /> <property name="module.genpom" value="true"/> <property name="module.genpom.args" value="-Sqpid-common=provided -Sqpid-broker=provided"/> + <property name="broker.plugin" value="true"/> + <property name="broker-plugins-access-control.libs" value=""/> <import file="../../module.xml" /> diff --git a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AbstractConfiguration.java b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AbstractConfiguration.java index f04dd38aca..f87374ac80 100644 --- a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AbstractConfiguration.java +++ b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AbstractConfiguration.java @@ -22,13 +22,8 @@ package org.apache.qpid.server.security.access.config; import java.io.File; -import org.apache.commons.configuration.ConfigurationException; -import org.apache.log4j.Logger; - public abstract class AbstractConfiguration implements ConfigurationFile { - private static final Logger _logger = Logger.getLogger(ConfigurationFile.class); - private File _file; private RuleSet _config; @@ -42,7 +37,7 @@ public abstract class AbstractConfiguration implements ConfigurationFile return _file; } - public RuleSet load() throws ConfigurationException + public RuleSet load() { _config = new RuleSet(); return _config; diff --git a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclAction.java b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclAction.java new file mode 100644 index 0000000000..e4bf21a082 --- /dev/null +++ b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclAction.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.server.security.access.config; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.apache.qpid.server.security.access.ObjectProperties; +import org.apache.qpid.server.security.access.ObjectType; +import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.security.access.firewall.FirewallRule; + +public class AclAction +{ + private Action _action; + private FirewallRule _firewallRule; + + public AclAction(Operation operation, ObjectType object, AclRulePredicates predicates) + { + _action = new Action(operation, object, predicates.getObjectProperties()); + _firewallRule = predicates.getFirewallRule(); + } + + public AclAction(Operation operation) + { + _action = new Action(operation); + } + + public AclAction(Operation operation, ObjectType object, ObjectProperties properties) + { + _action = new Action(operation, object, properties); + } + + public FirewallRule getFirewallRule() + { + return _firewallRule; + } + + public Action getAction() + { + return _action; + } + + public boolean isAllowed() + { + return _action.isAllowed(); + } + + @Override + public int hashCode() + { + return new HashCodeBuilder() + .append(_action) + .append(_firewallRule).toHashCode(); + } + + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + if (obj == this) + { + return true; + } + if (obj.getClass() != getClass()) + { + return false; + } + AclAction rhs = (AclAction) obj; + return new EqualsBuilder() + .append(_action, rhs._action) + .append(_firewallRule, rhs._firewallRule).isEquals(); + } + + @Override + public String toString() + { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) + .append(_action) + .append(_firewallRule).toString(); + } +} diff --git a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclRulePredicates.java b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclRulePredicates.java new file mode 100644 index 0000000000..45af85be6c --- /dev/null +++ b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclRulePredicates.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.server.security.access.config; + +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.apache.log4j.Logger; +import org.apache.qpid.server.security.access.ObjectProperties; +import org.apache.qpid.server.security.access.ObjectProperties.Property; +import org.apache.qpid.server.security.access.firewall.FirewallRule; +import org.apache.qpid.server.security.access.firewall.FirewallRuleFactory; + +/** + * Represents the predicates on an ACL rule by combining predicates relating to the object being operated on + * (e.g. name=foo) with firewall rules. + */ +public class AclRulePredicates +{ + private static final Logger _logger = Logger.getLogger(AclRulePredicates.class); + + private static final String SEPARATOR = ","; + + private ObjectProperties _properties = new ObjectProperties(); + + private FirewallRule _firewallRule; + + private FirewallRuleFactory _firewallRuleFactory = new FirewallRuleFactory(); + + public void parse(String key, String value) + { + ObjectProperties.Property property = ObjectProperties.Property.parse(key); + + if(property == Property.FROM_HOSTNAME) + { + checkFirewallRuleNotAlreadyDefined(key, value); + _firewallRule = _firewallRuleFactory.createForHostname(value.split(SEPARATOR)); + } + else if(property == Property.FROM_NETWORK) + { + checkFirewallRuleNotAlreadyDefined(key, value); + _firewallRule = _firewallRuleFactory.createForNetwork(value.split(SEPARATOR)); + } + else + { + _properties.put(property, value); + } + + if (_logger.isDebugEnabled()) + { + _logger.debug("Parsed " + property + " with value " + value); + } + } + + private void checkFirewallRuleNotAlreadyDefined(String key, String value) + { + if(_firewallRule != null) + { + throw new IllegalStateException( + "Cannot parse " + key + "=" + value + + " because firewall rule " + _firewallRule + " has already been defined"); + } + } + + @Override + public String toString() + { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) + .append(_properties) + .append(_firewallRule).toString(); + } + + public FirewallRule getFirewallRule() + { + return _firewallRule; + } + + public ObjectProperties getObjectProperties() + { + return _properties; + } + + void setFirewallRuleFactory(FirewallRuleFactory firewallRuleFactory) + { + _firewallRuleFactory = firewallRuleFactory; + } +} diff --git a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Action.java b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Action.java index b887d1e079..4fff0bebf5 100644 --- a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Action.java +++ b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Action.java @@ -20,8 +20,6 @@ */ package org.apache.qpid.server.security.access.config; -import java.util.Comparator; - import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.lang.builder.ToStringBuilder; @@ -32,7 +30,7 @@ import org.apache.qpid.server.security.access.Operation; /** * An access control v2 rule action. - * + * * An action consists of an {@link Operation} on an {@link ObjectType} with certain properties, stored in a {@link java.util.Map}. * The operation and object should be an allowable combination, based on the {@link ObjectType#isAllowed(Operation)} * method of the object, which is exposed as the {@link #isAllowed()} method here. The internal {@link #propertiesMatch(Map)} @@ -45,104 +43,96 @@ import org.apache.qpid.server.security.access.Operation; */ public class Action { - private Operation _operation; - private ObjectType _object; - private ObjectProperties _properties; - + private final Operation _operation; + private final ObjectType _object; + private final ObjectProperties _properties; + public Action(Operation operation) { this(operation, ObjectType.ALL); } - + public Action(Operation operation, ObjectType object, String name) { this(operation, object, new ObjectProperties(name)); } - + public Action(Operation operation, ObjectType object) { this(operation, object, ObjectProperties.EMPTY); } - + public Action(Operation operation, ObjectType object, ObjectProperties properties) { - setOperation(operation); - setObjectType(object); - setProperties(properties); + _operation = operation; + _object = object; + _properties = properties; } - + public Operation getOperation() { return _operation; } - public void setOperation(Operation operation) - { - _operation = operation; - } - public ObjectType getObjectType() { return _object; } - public void setObjectType(ObjectType object) - { - _object = object; - } - public ObjectProperties getProperties() { return _properties; } - - public void setProperties(ObjectProperties properties) - { - _properties = properties; - } - + public boolean isAllowed() { return _object.isAllowed(_operation); } - /** @see Comparable#compareTo(Object) */ public boolean matches(Action a) { - return ((Operation.ALL == a.getOperation() || getOperation() == a.getOperation()) - && (ObjectType.ALL == a.getObjectType() || getObjectType() == a.getObjectType()) - && _properties.matches(a.getProperties())); + if (!operationsMatch(a)) + { + return false; + } + + if (!objectTypesMatch(a)) + { + return false; + } + + if (!propertiesMatch(a)) + { + return false; + } + + return true; + } + + private boolean operationsMatch(Action a) + { + return Operation.ALL == a.getOperation() || getOperation() == a.getOperation(); } - /** - * An ordering based on specificity - * - * @see Comparator#compare(Object, Object) - */ - public class Specificity implements Comparator<Action> + private boolean objectTypesMatch(Action a) { - public int compare(Action a, Action b) + return ObjectType.ALL == a.getObjectType() || getObjectType() == a.getObjectType(); + } + + private boolean propertiesMatch(Action a) + { + boolean propertiesMatch = false; + if (_properties != null) + { + propertiesMatch = _properties.matches(a.getProperties()); + } + else if (a.getProperties() == null) { - if (a.getOperation() == Operation.ALL && b.getOperation() != Operation.ALL) - { - return 1; // B is more specific - } - else if (b.getOperation() == Operation.ALL && a.getOperation() != Operation.ALL) - { - return 1; // A is more specific - } - else if (a.getOperation() == b.getOperation()) - { - return 1; // b is more specific - } - else // Different operations - { - return a.getOperation().compareTo(b.getOperation()); // Arbitrary - } + propertiesMatch = true; } + return propertiesMatch; } - /** @see Object#equals(Object) */ @Override public boolean equals(Object o) { @@ -151,26 +141,24 @@ public class Action return false; } Action a = (Action) o; - + return new EqualsBuilder() .append(_operation, a.getOperation()) .append(_object, a.getObjectType()) - .appendSuper(_properties.equals(a.getProperties())) + .append(_properties, a.getProperties()) .isEquals(); } - /** @see Object#hashCode() */ @Override public int hashCode() { return new HashCodeBuilder() .append(_operation) - .append(_operation) + .append(_object) .append(_properties) .toHashCode(); } - /** @see Object#toString() */ @Override public String toString() { diff --git a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/ClientAction.java b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/ClientAction.java new file mode 100644 index 0000000000..fed20a56c8 --- /dev/null +++ b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/ClientAction.java @@ -0,0 +1,88 @@ +/* + * 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.net.InetAddress; + +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.apache.qpid.server.security.access.ObjectProperties; +import org.apache.qpid.server.security.access.ObjectType; +import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.security.access.firewall.FirewallRule; + +/** + * I represent an {@link Action} taken by a client from a known address. The address is used to + * determine if I match an {@link AclAction}, which may contain firewall rules. + */ +public class ClientAction +{ + private Action _clientAction; + + public ClientAction(Action clientAction) + { + _clientAction = clientAction; + } + + public ClientAction(Operation operation, ObjectType objectType, ObjectProperties properties) + { + _clientAction = new Action(operation, objectType, properties); + } + + public boolean matches(AclAction ruleAction, InetAddress addressOfClient) + { + return _clientAction.matches(ruleAction.getAction()) + && addressOfClientMatches(ruleAction, addressOfClient); + } + + private boolean addressOfClientMatches(AclAction ruleAction, InetAddress addressOfClient) + { + FirewallRule firewallRule = ruleAction.getFirewallRule(); + if(firewallRule == null || addressOfClient == null) + { + return true; + } + else + { + return firewallRule.matches(addressOfClient); + } + } + + public Operation getOperation() + { + return _clientAction.getOperation(); + } + + public ObjectType getObjectType() + { + return _clientAction.getObjectType(); + } + + public ObjectProperties getProperties() + { + return _clientAction.getProperties(); + } + + @Override + public String toString() + { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) + .append(_clientAction).toString(); + } +} diff --git a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/ConfigurationFile.java b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/ConfigurationFile.java index 8b1a00259b..966c32e24e 100644 --- a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/ConfigurationFile.java +++ b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/ConfigurationFile.java @@ -22,7 +22,7 @@ package org.apache.qpid.server.security.access.config; import java.io.File; -import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.server.configuration.IllegalConfigurationException; public interface ConfigurationFile { @@ -33,19 +33,17 @@ public interface ConfigurationFile /** * Load this configuration file's contents into a {@link RuleSet}. - * - * @throws ConfigurationException if the configuration file has errors. + * @throws IllegalConfigurationException if the configuration file has errors. * @throws IllegalArgumentException if individual tokens cannot be parsed. */ - RuleSet load() throws ConfigurationException; + RuleSet load() throws IllegalConfigurationException; /** * Reload this configuration file's contents. - * - * @throws ConfigurationException if the configuration file has errors. + * @throws IllegalConfigurationException if the configuration file has errors. * @throws IllegalArgumentException if individual tokens cannot be parsed. */ - RuleSet reload() throws ConfigurationException; + RuleSet reload() throws IllegalConfigurationException; RuleSet getConfiguration(); diff --git a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java index 9a08eb6499..ab309c54ce 100644 --- a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java +++ b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java @@ -1,5 +1,5 @@ /* - * + * * 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 @@ -7,16 +7,16 @@ * 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; @@ -32,55 +32,65 @@ import java.util.List; import java.util.Map; import java.util.Stack; -import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.lang.StringUtils; -import org.apache.qpid.server.security.access.ObjectProperties; +import org.apache.log4j.Logger; +import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.security.access.ObjectType; import org.apache.qpid.server.security.access.Operation; import org.apache.qpid.server.security.access.Permission; public class PlainConfiguration extends AbstractConfiguration { + private static final Logger _logger = Logger.getLogger(PlainConfiguration.class); + public static final Character COMMENT = '#'; public static final Character CONTINUATION = '\\'; - public static final String GROUP = "group"; public static final String ACL = "acl"; public static final String CONFIG = "config"; - public static final String UNRECOGNISED_INITIAL_MSG = "Unrecognised initial token '%s' at line %d"; - public static final String NOT_ENOUGH_TOKENS_MSG = "Not enough tokens at line %d"; - public static final String NUMBER_NOT_ALLOWED_MSG = "Number not allowed before '%s' at line %d"; - public static final String CANNOT_LOAD_MSG = "Cannot load config file %s"; - public static final String PREMATURE_CONTINUATION_MSG = "Premature continuation character at line %d"; - public static final String PREMATURE_EOF_MSG = "Premature end of file reached at line %d"; - public static final String PARSE_TOKEN_FAILED_MSG = "Failed to parse token at line %d"; - public static final String CONFIG_NOT_FOUND_MSG = "Cannot find config file %s"; - public static final String NOT_ENOUGH_GROUP_MSG = "Not enough data for a group at line %d"; - public static final String NOT_ENOUGH_ACL_MSG = "Not enough data for an acl at line %d"; - public static final String NOT_ENOUGH_CONFIG_MSG = "Not enough data for config at line %d"; - public static final String BAD_ACL_RULE_NUMBER_MSG = "Invalid rule number at line %d"; - public static final String PROPERTY_KEY_ONLY_MSG = "Incomplete property (key only) at line %d"; - public static final String PROPERTY_NO_EQUALS_MSG = "Incomplete property (no equals) at line %d"; - public static final String PROPERTY_NO_VALUE_MSG = "Incomplete property (no value) at line %d"; - + static final String UNRECOGNISED_INITIAL_MSG = "Unrecognised initial token '%s' at line %d"; + static final String NOT_ENOUGH_TOKENS_MSG = "Not enough tokens at line %d"; + static final String NUMBER_NOT_ALLOWED_MSG = "Number not allowed before '%s' at line %d"; + static final String CANNOT_LOAD_MSG = "Cannot load config file %s"; + static final String CANNOT_CLOSE_MSG = "Cannot close config file %s"; + static final String PREMATURE_CONTINUATION_MSG = "Premature continuation character at line %d"; + static final String PREMATURE_EOF_MSG = "Premature end of file reached at line %d"; + static final String PARSE_TOKEN_FAILED_MSG = "Failed to parse token at line %d"; + static final String CONFIG_NOT_FOUND_MSG = "Cannot find config file %s"; + static final String NOT_ENOUGH_ACL_MSG = "Not enough data for an acl at line %d"; + static final String NOT_ENOUGH_CONFIG_MSG = "Not enough data for config at line %d"; + static final String BAD_ACL_RULE_NUMBER_MSG = "Invalid rule number at line %d"; + static final String PROPERTY_KEY_ONLY_MSG = "Incomplete property (key only) at line %d"; + static final String PROPERTY_NO_EQUALS_MSG = "Incomplete property (no equals) at line %d"; + static final String PROPERTY_NO_VALUE_MSG = "Incomplete property (no value) at line %d"; + private StreamTokenizer _st; public PlainConfiguration(File file) { super(file); } - + @Override - public RuleSet load() throws ConfigurationException + public RuleSet load() { RuleSet ruleSet = super.load(); - + + File file = getFile(); + FileReader fileReader = null; + try { - _st = new StreamTokenizer(new BufferedReader(new FileReader(getFile()))); + if(_logger.isDebugEnabled()) + { + _logger.debug("About to load ACL file " + file); + } + + fileReader = new FileReader(file); + _st = new StreamTokenizer(new BufferedReader(fileReader)); _st.resetSyntax(); // setup the tokenizer - + _st.commentChar(COMMENT); // single line comments _st.eolIsSignificant(true); // return EOL as a token _st.ordinaryChar('='); // equals is a token @@ -97,7 +107,7 @@ public class PlainConfiguration extends AbstractConfiguration _st.wordChars('*', '*'); // star _st.wordChars('@', '@'); // at _st.wordChars(':', ':'); // colon - + // parse the acl file lines Stack<String> stack = new Stack<String>(); int current; @@ -111,21 +121,21 @@ public class PlainConfiguration extends AbstractConfiguration { break; // blank line } - + // pull out the first token from the bottom of the stack and check arguments exist String first = stack.firstElement(); stack.removeElementAt(0); if (stack.isEmpty()) { - throw new ConfigurationException(String.format(NOT_ENOUGH_TOKENS_MSG, getLine())); + throw new IllegalConfigurationException(String.format(NOT_ENOUGH_TOKENS_MSG, getLine())); } - + // check for and parse optional initial number for ACL lines Integer number = null; if (StringUtils.isNumeric(first)) { // set the acl number and get the next element - number = Integer.valueOf(first); + number = Integer.valueOf(first); first = stack.firstElement(); stack.removeElementAt(0); } @@ -136,9 +146,9 @@ public class PlainConfiguration extends AbstractConfiguration } else if (number == null) { - if (StringUtils.equalsIgnoreCase(GROUP, first)) + if(StringUtils.equalsIgnoreCase("GROUP", first)) { - parseGroup(stack); + throw new IllegalConfigurationException(String.format("GROUP keyword not supported. Groups should defined via a Group Provider, not in the ACL file.", getLine())); } else if (StringUtils.equalsIgnoreCase(CONFIG, first)) { @@ -146,14 +156,14 @@ public class PlainConfiguration extends AbstractConfiguration } else { - throw new ConfigurationException(String.format(UNRECOGNISED_INITIAL_MSG, first, getLine())); + throw new IllegalConfigurationException(String.format(UNRECOGNISED_INITIAL_MSG, first, getLine())); } } else { - throw new ConfigurationException(String.format(NUMBER_NOT_ALLOWED_MSG, first, getLine())); + throw new IllegalConfigurationException(String.format(NUMBER_NOT_ALLOWED_MSG, first, getLine())); } - + // reset stack, start next line stack.clear(); break; @@ -171,9 +181,9 @@ public class PlainConfiguration extends AbstractConfiguration { break; // continue reading next line } - + // invalid location for continuation character (add one to line beacuse we ate the EOL) - throw new ConfigurationException(String.format(PREMATURE_CONTINUATION_MSG, getLine() + 1)); + throw new IllegalConfigurationException(String.format(PREMATURE_CONTINUATION_MSG, getLine() + 1)); } else if (_st.ttype == '\'' || _st.ttype == '"') { @@ -185,54 +195,59 @@ public class PlainConfiguration extends AbstractConfiguration } } } while (current != StreamTokenizer.TT_EOF); - + if (!stack.isEmpty()) { - throw new ConfigurationException(String.format(PREMATURE_EOF_MSG, getLine())); + throw new IllegalConfigurationException(String.format(PREMATURE_EOF_MSG, getLine())); } } catch (IllegalArgumentException iae) { - throw new ConfigurationException(String.format(PARSE_TOKEN_FAILED_MSG, getLine()), iae); + throw new IllegalConfigurationException(String.format(PARSE_TOKEN_FAILED_MSG, getLine()), iae); } catch (FileNotFoundException fnfe) { - throw new ConfigurationException(String.format(CONFIG_NOT_FOUND_MSG, getFile().getName()), fnfe); + throw new IllegalConfigurationException(String.format(CONFIG_NOT_FOUND_MSG, file.getName()), fnfe); } catch (IOException ioe) { - throw new ConfigurationException(String.format(CANNOT_LOAD_MSG, getFile().getName()), ioe); + throw new IllegalConfigurationException(String.format(CANNOT_LOAD_MSG, file.getName()), ioe); } - - return ruleSet; - } - - private void parseGroup(List<String> args) throws ConfigurationException - { - if (args.size() < 2) + finally { - throw new ConfigurationException(String.format(NOT_ENOUGH_GROUP_MSG, getLine())); + if(fileReader != null) + { + try + { + fileReader.close(); + } + catch (IOException e) + { + throw new IllegalConfigurationException(String.format(CANNOT_CLOSE_MSG, file.getName()), e); + } + } } - - getConfiguration().addGroup(args.get(0), args.subList(1, args.size())); + + + return ruleSet; } - - private void parseAcl(Integer number, List<String> args) throws ConfigurationException + + private void parseAcl(Integer number, List<String> args) { if (args.size() < 3) { - throw new ConfigurationException(String.format(NOT_ENOUGH_ACL_MSG, getLine())); + throw new IllegalConfigurationException(String.format(NOT_ENOUGH_ACL_MSG, getLine())); } Permission permission = Permission.parse(args.get(0)); String identity = args.get(1); Operation operation = Operation.parse(args.get(2)); - + if (number != null && !getConfiguration().isValidNumber(number)) { - throw new ConfigurationException(String.format(BAD_ACL_RULE_NUMBER_MSG, getLine())); + throw new IllegalConfigurationException(String.format(BAD_ACL_RULE_NUMBER_MSG, getLine())); } - + if (args.size() == 3) { getConfiguration().grant(number, identity, permission, operation); @@ -240,55 +255,52 @@ public class PlainConfiguration extends AbstractConfiguration else { ObjectType object = ObjectType.parse(args.get(3)); - ObjectProperties properties = toObjectProperties(args.subList(4, args.size())); + AclRulePredicates predicates = toRulePredicates(args.subList(4, args.size())); - getConfiguration().grant(number, identity, permission, operation, object, properties); + getConfiguration().grant(number, identity, permission, operation, object, predicates); } } - - private void parseConfig(List<String> args) throws ConfigurationException + + private void parseConfig(List<String> args) { if (args.size() < 3) { - throw new ConfigurationException(String.format(NOT_ENOUGH_CONFIG_MSG, getLine())); + throw new IllegalConfigurationException(String.format(NOT_ENOUGH_CONFIG_MSG, getLine())); } Map<String, Boolean> properties = toPluginProperties(args); - + getConfiguration().configure(properties); } - - /** Converts a {@link List} of "name", "=", "value" tokens into a {@link Map}. */ - protected ObjectProperties toObjectProperties(List<String> args) throws ConfigurationException + + private AclRulePredicates toRulePredicates(List<String> args) { - ObjectProperties properties = new ObjectProperties(); + AclRulePredicates predicates = new AclRulePredicates(); Iterator<String> i = args.iterator(); while (i.hasNext()) { String key = i.next(); if (!i.hasNext()) { - throw new ConfigurationException(String.format(PROPERTY_KEY_ONLY_MSG, getLine())); + throw new IllegalConfigurationException(String.format(PROPERTY_KEY_ONLY_MSG, getLine())); } if (!"=".equals(i.next())) { - throw new ConfigurationException(String.format(PROPERTY_NO_EQUALS_MSG, getLine())); + throw new IllegalConfigurationException(String.format(PROPERTY_NO_EQUALS_MSG, getLine())); } if (!i.hasNext()) { - throw new ConfigurationException(String.format(PROPERTY_NO_VALUE_MSG, getLine())); + throw new IllegalConfigurationException(String.format(PROPERTY_NO_VALUE_MSG, getLine())); } String value = i.next(); - - // parse property key - ObjectProperties.Property property = ObjectProperties.Property.parse(key); - properties.put(property, value); + + predicates.parse(key, value); } - return properties; + return predicates; } - + /** Converts a {@link List} of "name", "=", "value" tokens into a {@link Map}. */ - protected Map<String, Boolean> toPluginProperties(List<String> args) throws ConfigurationException + protected Map<String, Boolean> toPluginProperties(List<String> args) { Map<String, Boolean> properties = new HashMap<String, Boolean>(); Iterator<String> i = args.iterator(); @@ -297,24 +309,24 @@ public class PlainConfiguration extends AbstractConfiguration String key = i.next().toLowerCase(); if (!i.hasNext()) { - throw new ConfigurationException(String.format(PROPERTY_KEY_ONLY_MSG, getLine())); + throw new IllegalConfigurationException(String.format(PROPERTY_KEY_ONLY_MSG, getLine())); } if (!"=".equals(i.next())) { - throw new ConfigurationException(String.format(PROPERTY_NO_EQUALS_MSG, getLine())); + throw new IllegalConfigurationException(String.format(PROPERTY_NO_EQUALS_MSG, getLine())); } if (!i.hasNext()) { - throw new ConfigurationException(String.format(PROPERTY_NO_VALUE_MSG, getLine())); + throw new IllegalConfigurationException(String.format(PROPERTY_NO_VALUE_MSG, getLine())); } - + // parse property value and save Boolean value = Boolean.valueOf(i.next()); properties.put(key, value); } return properties; } - + protected int getLine() { return _st.lineno() - 1; diff --git a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Rule.java b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Rule.java index 15d6b67192..cef9a8696b 100644 --- a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Rule.java +++ b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Rule.java @@ -29,7 +29,7 @@ import org.apache.qpid.server.security.access.Permission; /** * An access control v2 rule. - * + * * A rule consists of {@link Permission} for a particular identity to perform an {@link Action}. The identity * may be either a user or a group. */ @@ -37,41 +37,41 @@ public class Rule implements Comparable<Rule> { /** String indicating all identitied. */ public static final String ALL = "all"; - + private Integer _number; private Boolean _enabled = Boolean.TRUE; private String _identity; - private Action _action; + private AclAction _action; private Permission _permission; - - public Rule(Integer number, String identity, Action action, Permission permission) + + public Rule(Integer number, String identity, AclAction action, Permission permission) { setNumber(number); setIdentity(identity); setAction(action); setPermission(permission); } - - public Rule(String identity, Action action, Permission permission) + + public Rule(String identity, AclAction action, Permission permission) { this(null, identity, action, permission); } - + public boolean isEnabled() { return _enabled; } - + public void setEnabled(boolean enabled) { _enabled = enabled; } - + public void enable() { _enabled = Boolean.TRUE; } - + public void disable() { _enabled = Boolean.FALSE; @@ -96,13 +96,18 @@ public class Rule implements Comparable<Rule> { _identity = identity; } - + public Action getAction() { + return _action.getAction(); + } + + public AclAction getAclAction() + { return _action; } - public void setAction(Action action) + public void setAction(AclAction action) { _action = action; } @@ -117,7 +122,7 @@ public class Rule implements Comparable<Rule> _permission = permission; } - /** @see Comparable#compareTo(Object) */ + @Override public int compareTo(Rule r) { return new CompareToBuilder() @@ -127,7 +132,6 @@ public class Rule implements Comparable<Rule> .toComparison(); } - /** @see Object#equals(Object) */ @Override public boolean equals(Object o) { @@ -136,33 +140,31 @@ public class Rule implements Comparable<Rule> return false; } Rule r = (Rule) o; - + return new EqualsBuilder() .append(getIdentity(), r.getIdentity()) - .append(getAction(), r.getAction()) + .append(getAclAction(), r.getAclAction()) .append(getPermission(), r.getPermission()) .isEquals(); } - /** @see Object#hashCode() */ @Override public int hashCode() { return new HashCodeBuilder() .append(getIdentity()) - .append(getAction()) + .append(getAclAction()) .append(getPermission()) .toHashCode(); } - /** @see Object#toString() */ @Override public String toString() { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) .append("#", getNumber()) .append("identity", getIdentity()) - .append("action", getAction()) + .append("action", getAclAction()) .append("permission", getPermission()) .append("enabled", isEnabled()) .toString(); diff --git a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSet.java b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSet.java index 815df99f80..e61370fced 100644 --- a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSet.java +++ b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSet.java @@ -18,8 +18,8 @@ */ package org.apache.qpid.server.security.access.config; +import java.net.InetAddress; import java.security.Principal; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumMap; @@ -54,7 +54,7 @@ import org.apache.qpid.server.security.access.logging.AccessControlMessages; */ public class RuleSet { - public static final Logger _logger = Logger.getLogger(RuleSet.class); + private static final Logger _logger = Logger.getLogger(RuleSet.class); private static final String AT = "@"; private static final String SLASH = "/"; @@ -66,7 +66,6 @@ public class RuleSet private static final Integer _increment = 10; - private final Map<String, List<String>> _aclGroups = new HashMap<String, List<String>>(); private final SortedMap<Integer, Rule> _rules = new TreeMap<Integer, Rule>(); private final Map<Subject, Map<Operation, Map<ObjectType, List<Rule>>>> _cache = new WeakHashMap<Subject, Map<Operation, Map<ObjectType, List<Rule>>>>(); @@ -79,14 +78,13 @@ public class RuleSet } /** - * Clear the contents, including acl groups, rules and configuration. + * Clear the contents, including acl rules and configuration. */ public void clear() { _rules.clear(); _cache.clear(); _config.clear(); - _aclGroups.clear(); } public int getRuleCount() @@ -157,21 +155,27 @@ public class RuleSet public void grant(Integer number, String identity, Permission permission, Operation operation) { - Action action = new Action(operation); + AclAction action = new AclAction(operation); addRule(number, identity, permission, action); } public void grant(Integer number, String identity, Permission permission, Operation operation, ObjectType object, ObjectProperties properties) { - Action action = new Action(operation, object, properties); + AclAction action = new AclAction(operation, object, properties); addRule(number, identity, permission, action); } - public boolean ruleExists(String identity, Action action) + public void grant(Integer number, String identity, Permission permission, Operation operation, ObjectType object, AclRulePredicates predicates) + { + AclAction aclAction = new AclAction(operation, object, predicates); + addRule(number, identity, permission, aclAction); + } + + public boolean ruleExists(String identity, AclAction action) { for (Rule rule : _rules.values()) { - if (rule.getIdentity().equals(identity) && rule.getAction().equals(action)) + if (rule.getIdentity().equals(identity) && rule.getAclAction().equals(action)) { return true; } @@ -179,8 +183,7 @@ public class RuleSet return false; } - // TODO make this work when group membership is not known at file parse time - public void addRule(Integer number, String identity, Permission permission, Action action) + public void addRule(Integer number, String identity, Permission permission, AclAction action) { _cache.clear(); @@ -222,53 +225,6 @@ public class RuleSet _rules.get(Integer.valueOf(ruleNumber)).disable(); } - public boolean addGroup(String group, List<String> constituents) - { - _cache.clear(); - - if (_aclGroups.containsKey(group)) - { - // cannot redefine - return false; - } - else - { - _aclGroups.put(group, new ArrayList<String>()); - } - - for (String name : constituents) - { - if (name.equalsIgnoreCase(group)) - { - // recursive definition - return false; - } - - if (!checkName(name)) - { - // invalid name - return false; - } - - if (_aclGroups.containsKey(name)) - { - // is a group - _aclGroups.get(group).addAll(_aclGroups.get(name)); - } - else - { - // is a user - if (!isvalidUserName(name)) - { - // invalid username - return false; - } - _aclGroups.get(group).add(name); - } - } - return true; - } - /** Return true if the name is well-formed (contains legal characters). */ protected boolean checkName(String name) { @@ -312,11 +268,15 @@ public class RuleSet return true; } - // CPP broker authorise function prototype - // virtual bool authorise(const std::string& id, const Action& action, const ObjectType& objType, - // const std::string& name, std::map<Property, std::string>* params=0) - - // Possibly add a String name paramater? + /** + * Checks for the case when the client's address is not known. + * + * @see #check(Subject, Operation, ObjectType, ObjectProperties, InetAddress) + */ + public Result check(Subject subject, Operation operation, ObjectType objectType, ObjectProperties properties) + { + return check(subject, operation, objectType, properties, null); + } /** * Check the authorisation granted to a particular identity for an operation on an object type with @@ -327,10 +287,9 @@ public class RuleSet * 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, Operation operation, ObjectType objectType, ObjectProperties properties) + public Result check(Subject subject, Operation operation, ObjectType objectType, ObjectProperties properties, InetAddress addressOfClient) { - // Create the action to check - Action action = new Action(operation, objectType, properties); + ClientAction action = new ClientAction(operation, objectType, properties); if(_logger.isDebugEnabled()) { @@ -349,27 +308,31 @@ public class RuleSet } // Iterate through a filtered set of rules dealing with this identity and operation - for (Rule current : rules) + for (Rule rule : rules) { if(_logger.isDebugEnabled()) { - _logger.debug("Checking against rule: " + current); + _logger.debug("Checking against rule: " + rule); } - // Check if action matches - if (action.matches(current.getAction())) + + if (action.matches(rule.getAclAction(), addressOfClient)) { - Permission permission = current.getPermission(); + Permission permission = rule.getPermission(); switch (permission) { case ALLOW_LOG: CurrentActor.get().message(AccessControlMessages.ALLOWED( - action.getOperation().toString(), action.getObjectType().toString(), action.getProperties().toString())); + action.getOperation().toString(), + action.getObjectType().toString(), + action.getProperties().toString())); case ALLOW: return Result.ALLOWED; case DENY_LOG: CurrentActor.get().message(AccessControlMessages.DENIED( - action.getOperation().toString(), action.getObjectType().toString(), action.getProperties().toString())); + action.getOperation().toString(), + action.getObjectType().toString(), + action.getProperties().toString())); case DENY: return Result.DENIED; } @@ -446,8 +409,7 @@ public class RuleSet { final Principal principal = iterator.next(); - if (rule.getIdentity().equalsIgnoreCase(principal.getName()) - || (_aclGroups.containsKey(rule.getIdentity()) && _aclGroups.get(rule.getIdentity()).contains(principal.getName()))) + if (rule.getIdentity().equalsIgnoreCase(principal.getName())) { return true; } @@ -476,5 +438,4 @@ public class RuleSet } return objects; } - } diff --git a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/AccessControlFirewallException.java b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/AccessControlFirewallException.java new file mode 100644 index 0000000000..d08a052efd --- /dev/null +++ b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/AccessControlFirewallException.java @@ -0,0 +1,47 @@ +/* + * + * 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.firewall; + +public class AccessControlFirewallException extends RuntimeException +{ + /** serialVersionUID */ + private static final long serialVersionUID = 4526157149690917805L; + + public AccessControlFirewallException() + { + super(); + } + + public AccessControlFirewallException(String message) + { + super(message); + } + + public AccessControlFirewallException(String message, Throwable cause) + { + super(message, cause); + } + + public AccessControlFirewallException(Throwable cause) + { + super(cause); + } +}
\ No newline at end of file diff --git a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/FirewallRule.java b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/FirewallRule.java new file mode 100644 index 0000000000..482a795693 --- /dev/null +++ b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/FirewallRule.java @@ -0,0 +1,26 @@ +/* + * 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.firewall; + +import java.net.InetAddress; + +public interface FirewallRule +{ + boolean matches(InetAddress addressOfClient); +} diff --git a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControlActivator.java b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/FirewallRuleFactory.java index 7c83446cf1..64be26c209 100644 --- a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControlActivator.java +++ b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/FirewallRuleFactory.java @@ -1,5 +1,4 @@ /* - * * 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 @@ -7,35 +6,28 @@ * 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.plugins; +package org.apache.qpid.server.security.access.firewall; -import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory; -import org.apache.qpid.server.security.SecurityPluginActivator; -import org.apache.qpid.server.security.SecurityPluginFactory; - -/** - * The OSGi {@link org.osgi.framework.BundleActivator} for {@link AccessControl}. - */ -public class AccessControlActivator extends SecurityPluginActivator +public class FirewallRuleFactory { - public SecurityPluginFactory getFactory() - { - return AccessControl.FACTORY; - } + public FirewallRule createForHostname(String[] hostnames) + { + return new HostnameFirewallRule(hostnames); + } - public ConfigurationPluginFactory getConfigurationFactory() + public FirewallRule createForNetwork(String[] networks) { - return AccessControlConfiguration.FACTORY; + return new NetworkFirewallRule(networks); } + } diff --git a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/HostnameFirewallRule.java b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/HostnameFirewallRule.java new file mode 100644 index 0000000000..fb13426fbb --- /dev/null +++ b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/HostnameFirewallRule.java @@ -0,0 +1,156 @@ +/* + * 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.firewall; + +import java.net.InetAddress; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.apache.log4j.Logger; + +public class HostnameFirewallRule implements FirewallRule +{ + private static final Logger _logger = Logger.getLogger(HostnameFirewallRule.class); + + private static final long DNS_TIMEOUT = 30000; + private static final ExecutorService DNS_LOOKUP = Executors.newCachedThreadPool(); + + private Pattern[] _hostnamePatterns; + private String[] _hostnames; + + public HostnameFirewallRule(String... hostnames) + { + _hostnames = hostnames; + + int i = 0; + _hostnamePatterns = new Pattern[hostnames.length]; + for (String hostname : hostnames) + { + _hostnamePatterns[i++] = Pattern.compile(hostname); + } + + if(_logger.isDebugEnabled()) + { + _logger.debug("Created " + this); + } + } + + @Override + public boolean matches(InetAddress remote) + { + String hostname = getHostname(remote); + if (hostname == null) + { + throw new AccessControlFirewallException("DNS lookup failed for address " + remote); + } + for (Pattern pattern : _hostnamePatterns) + { + boolean hostnameMatches = pattern.matcher(hostname).matches(); + + if (hostnameMatches) + { + if(_logger.isDebugEnabled()) + { + _logger.debug("Hostname " + hostname + " matches rule " + pattern.toString()); + } + return true; + } + } + + if(_logger.isDebugEnabled()) + { + _logger.debug("Hostname " + hostname + " matches no configured hostname patterns"); + } + + return false; + } + + + /** + * @param remote + * the InetAddress to look up + * @return the hostname, null if not found, takes longer than + * {@value #DNS_LOOKUP} to find or otherwise fails + */ + private String getHostname(final InetAddress remote) throws AccessControlFirewallException + { + FutureTask<String> lookup = new FutureTask<String>(new Callable<String>() + { + public String call() + { + return remote.getCanonicalHostName(); + } + }); + DNS_LOOKUP.execute(lookup); + + try + { + return lookup.get(DNS_TIMEOUT, TimeUnit.MILLISECONDS); + } + catch (Exception e) + { + _logger.warn("Unable to look up hostname from address " + remote, e); + return null; + } + finally + { + lookup.cancel(true); + } + } + + @Override + public int hashCode() + { + return new HashCodeBuilder().append(_hostnames).toHashCode(); + } + + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + if (obj == this) + { + return true; + } + if (obj.getClass() != getClass()) + { + return false; + } + HostnameFirewallRule rhs = (HostnameFirewallRule) obj; + return new EqualsBuilder().append(_hostnames, rhs._hostnames).isEquals(); + } + + @Override + public String toString() + { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) + .append(_hostnames).toString(); + } +} diff --git a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/InetNetwork.java b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/InetNetwork.java new file mode 100644 index 0000000000..2e979b38f1 --- /dev/null +++ b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/InetNetwork.java @@ -0,0 +1,177 @@ +/* + * 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.firewall; + +import java.net.InetAddress; + +class InetNetwork +{ + /* + * Implements network masking, and is compatible with RFC 1518 and + * RFC 1519, which describe CIDR: Classless Inter-Domain Routing. + */ + + private InetAddress network; + private InetAddress netmask; + + public InetNetwork(InetAddress ip, InetAddress netmask) + { + this.network = maskIP(ip, netmask); + this.netmask = netmask; + } + + public boolean contains(final String name) throws java.net.UnknownHostException + { + return network.equals(maskIP(InetAddress.getByName(name), netmask)); + } + + public boolean contains(final InetAddress ip) + { + return network.equals(maskIP(ip, netmask)); + } + + @Override + public String toString() + { + return network.getHostAddress() + "/" + netmask.getHostAddress(); + } + + @Override + public int hashCode() + { + return maskIP(network, netmask).hashCode(); + } + + @Override + public boolean equals(Object obj) + { + return (obj != null) && + (obj instanceof InetNetwork) && + ((InetNetwork)obj).network.equals(network) && + ((InetNetwork)obj).netmask.equals(netmask); + } + + public static InetNetwork getFromString(String netspec) throws java.net.UnknownHostException + { + if (netspec.endsWith("*")) + { + netspec = normalizeFromAsterisk(netspec); + } + else + { + int iSlash = netspec.indexOf('/'); + if (iSlash == -1) + { + netspec += "/255.255.255.255"; + } + else if (netspec.indexOf('.', iSlash) == -1) + { + netspec = normalizeFromCIDR(netspec); + } + } + + return new InetNetwork( + InetAddress.getByName(netspec.substring(0, netspec.indexOf('/'))), + InetAddress.getByName(netspec.substring(netspec.indexOf('/') + 1))); + } + + public static InetAddress maskIP(final byte[] ip, final byte[] mask) + { + try + { + return getByAddress( + new byte[] + { + (byte) (mask[0] & ip[0]), + (byte) (mask[1] & ip[1]), + (byte) (mask[2] & ip[2]), + (byte) (mask[3] & ip[3]) + } + ); + } + catch (Exception _) + { + return null; + } + } + + public static InetAddress maskIP(final InetAddress ip, final InetAddress mask) + { + return maskIP(ip.getAddress(), mask.getAddress()); + } + + /* + * This converts from an uncommon "wildcard" CIDR format + * to "address + mask" format: + * + * * => 000.000.000.0/000.000.000.0 + * xxx.* => xxx.000.000.0/255.000.000.0 + * xxx.xxx.* => xxx.xxx.000.0/255.255.000.0 + * xxx.xxx.xxx.* => xxx.xxx.xxx.0/255.255.255.0 + */ + static private String normalizeFromAsterisk(final String netspec) + { + String[] masks = { "0.0.0.0/0.0.0.0", "0.0.0/255.0.0.0", "0.0/255.255.0.0", "0/255.255.255.0" }; + char[] srcb = netspec.toCharArray(); + int octets = 0; + for (int i = 1; i < netspec.length(); i++) + { + if (srcb[i] == '.') + { + octets++; + } + } + return (octets == 0) ? masks[0] : netspec.substring(0, netspec.length() -1 ).concat(masks[octets]); + } + + /* + * RFC 1518, 1519 - Classless Inter-Domain Routing (CIDR) + * This converts from "prefix + prefix-length" format to + * "address + mask" format, e.g. from xxx.xxx.xxx.xxx/yy + * to xxx.xxx.xxx.xxx/yyy.yyy.yyy.yyy. + */ + static private String normalizeFromCIDR(final String netspec) + { + final int bits = 32 - Integer.parseInt(netspec.substring(netspec.indexOf('/')+1)); + final int mask = (bits == 32) ? 0 : 0xFFFFFFFF - ((1 << bits)-1); + + return netspec.substring(0, netspec.indexOf('/') + 1) + + Integer.toString(mask >> 24 & 0xFF, 10) + "." + + Integer.toString(mask >> 16 & 0xFF, 10) + "." + + Integer.toString(mask >> 8 & 0xFF, 10) + "." + + Integer.toString(mask >> 0 & 0xFF, 10); + } + + private static InetAddress getByAddress(byte[] ip) throws java.net.UnknownHostException + { + InetAddress addr = InetAddress.getByAddress(ip); + + if (addr == null) { + addr = InetAddress.getByName + ( + Integer.toString(ip[0] & 0xFF, 10) + "." + + Integer.toString(ip[1] & 0xFF, 10) + "." + + Integer.toString(ip[2] & 0xFF, 10) + "." + + Integer.toString(ip[3] & 0xFF, 10) + ); + } + + return addr; + } +}
\ No newline at end of file diff --git a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/NetworkFirewallRule.java b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/NetworkFirewallRule.java new file mode 100644 index 0000000000..ad619a0e0b --- /dev/null +++ b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/NetworkFirewallRule.java @@ -0,0 +1,117 @@ +/* + * 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.firewall; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.apache.log4j.Logger; + +public class NetworkFirewallRule implements FirewallRule +{ + private static final Logger _logger = Logger.getLogger(NetworkFirewallRule.class); + + private List<InetNetwork> _networks; + + public NetworkFirewallRule(String... networks) + { + _networks = new ArrayList<InetNetwork>(); + for (int i = 0; i < networks.length; i++) + { + String network = networks[i]; + try + { + InetNetwork inetNetwork = InetNetwork.getFromString(network); + if (!_networks.contains(inetNetwork)) + { + _networks.add(inetNetwork); + } + } + catch (java.net.UnknownHostException uhe) + { + _logger.error("Cannot resolve address: " + network, uhe); + } + } + + if(_logger.isDebugEnabled()) + { + _logger.debug("Created " + this); + } + } + + @Override + public boolean matches(InetAddress ip) + { + for (InetNetwork network : _networks) + { + if (network.contains(ip)) + { + if(_logger.isDebugEnabled()) + { + _logger.debug("Client address " + ip + " matches configured network " + network); + } + return true; + } + } + + if(_logger.isDebugEnabled()) + { + _logger.debug("Client address " + ip + " does not match any configured networks"); + } + + return false; + } + + @Override + public int hashCode() + { + return new HashCodeBuilder().append(_networks).toHashCode(); + } + + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + if (obj == this) + { + return true; + } + if (obj.getClass() != getClass()) + { + return false; + } + NetworkFirewallRule rhs = (NetworkFirewallRule) obj; + return new EqualsBuilder().append(_networks, rhs._networks).isEquals(); + } + + @Override + public String toString() + { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) + .append(_networks).toString(); + } +} diff --git a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/logging/AccessControl_logmessages.properties b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/logging/AccessControl_logmessages.properties index bf80df3722..2a5eb7b3be 100644 --- a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/logging/AccessControl_logmessages.properties +++ b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/logging/AccessControl_logmessages.properties @@ -25,4 +25,4 @@ ALLOWED = ACL-1001 : Allowed : {0} {1} {2} # 'deny-log' rule message -DENIED = ACL-1002 : Denied : {0} {1} {2}
\ No newline at end of file +DENIED = ACL-1002 : Denied : {0} {1} {2} diff --git a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControlConfiguration.java b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControlConfiguration.java deleted file mode 100644 index c4db6db820..0000000000 --- a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControlConfiguration.java +++ /dev/null @@ -1,83 +0,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. - * - */ -package org.apache.qpid.server.security.access.plugins; - -import java.io.File; -import java.util.Arrays; -import java.util.List; - -import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.ConfigurationException; -import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; -import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory; -import org.apache.qpid.server.security.access.config.ConfigurationFile; -import org.apache.qpid.server.security.access.config.PlainConfiguration; -import org.apache.qpid.server.security.access.config.RuleSet; - -public class AccessControlConfiguration extends ConfigurationPlugin -{ - public static final ConfigurationPluginFactory FACTORY = new ConfigurationPluginFactory() - { - public ConfigurationPlugin newInstance(String path, Configuration config) throws ConfigurationException - { - ConfigurationPlugin instance = new AccessControlConfiguration(); - instance.setConfiguration(path, config); - return instance; - } - - public List<String> getParentPaths() - { - return Arrays.asList("security.acl", "virtualhosts.virtualhost.security.acl"); - } - }; - - private RuleSet _ruleSet; - - public String[] getElementsProcessed() - { - return new String[] { "" }; - } - - public String getFileName() - { - return getConfig().getString(""); - } - - public void validateConfiguration() throws ConfigurationException - { - String filename = getFileName(); - if (filename == null) - { - throw new ConfigurationException("No ACL file name specified"); - } - - File aclFile = new File(filename); - - ConfigurationFile configFile = new PlainConfiguration(aclFile); - _ruleSet = configFile.load(); - } - - public RuleSet getRuleSet() - { - return _ruleSet; - } - -} diff --git a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControl.java b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControl.java index d8a5bd4085..6f7885da94 100644 --- a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControl.java +++ b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControl.java @@ -20,56 +20,47 @@ */ package org.apache.qpid.server.security.access.plugins; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.io.File; + import javax.security.auth.Subject; import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.lang.ObjectUtils; import org.apache.log4j.Logger; -import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; -import org.apache.qpid.server.security.AbstractPlugin; import org.apache.qpid.server.security.Result; import org.apache.qpid.server.security.SecurityManager; -import org.apache.qpid.server.security.SecurityPluginFactory; +import org.apache.qpid.server.security.AccessControl; import org.apache.qpid.server.security.access.ObjectProperties; import org.apache.qpid.server.security.access.ObjectType; import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.security.access.config.ConfigurationFile; +import org.apache.qpid.server.security.access.config.PlainConfiguration; import org.apache.qpid.server.security.access.config.RuleSet; -/** - * This access control plugin implements version two plain text access control. - */ -public class AccessControl extends AbstractPlugin +public class DefaultAccessControl implements AccessControl { - public static final Logger _logger = Logger.getLogger(AccessControl.class); - + private static final Logger _logger = Logger.getLogger(DefaultAccessControl.class); + private RuleSet _ruleSet; - - public static final SecurityPluginFactory<AccessControl> FACTORY = new SecurityPluginFactory<AccessControl>() - { - public Class<AccessControl> getPluginClass() - { - return AccessControl.class; - } - public String getPluginName() + public DefaultAccessControl(String fileName) + { + if (_logger.isDebugEnabled()) { - return AccessControl.class.getName(); + _logger.debug("Creating AccessControl instance using file: " + fileName); } + File aclFile = new File(fileName); - public AccessControl newInstance(ConfigurationPlugin config) throws ConfigurationException - { - AccessControlConfiguration configuration = config.getConfiguration(AccessControlConfiguration.class.getName()); - - // If there is no configuration for this plugin then don't load it. - if (configuration == null) - { - return null; - } + ConfigurationFile configFile = new PlainConfiguration(aclFile); + _ruleSet = configFile.load(); + } - AccessControl plugin = new AccessControl(); - plugin.configure(configuration); - return plugin; - } - }; + DefaultAccessControl(RuleSet rs) throws ConfigurationException + { + _ruleSet = rs; + } public Result getDefault() { @@ -82,11 +73,18 @@ public class AccessControl extends AbstractPlugin * Delegate to the {@link #authorise(Operation, ObjectType, ObjectProperties)} method, with * the operation set to ACCESS and no object properties. */ - public Result access(ObjectType objectType, Object instance) + public Result access(ObjectType objectType, Object inetSocketAddress) { - return authorise(Operation.ACCESS, objectType, ObjectProperties.EMPTY); + InetAddress addressOfClient = null; + + if(inetSocketAddress != null) + { + addressOfClient = ((InetSocketAddress) inetSocketAddress).getAddress(); + } + + return authoriseFromAddress(Operation.ACCESS, objectType, ObjectProperties.EMPTY, addressOfClient); } - + /** * Check if an operation is authorised by asking the configuration object about the access * control rules granted to the current thread's {@link Subject}. If there is no current @@ -94,23 +92,31 @@ public class AccessControl extends AbstractPlugin */ public Result authorise(Operation operation, ObjectType objectType, ObjectProperties properties) { + return authoriseFromAddress(operation, objectType, properties, null); + } + + public Result authoriseFromAddress(Operation operation, ObjectType objectType, ObjectProperties properties, InetAddress addressOfClient) + { final Subject subject = SecurityManager.getThreadSubject(); // Abstain if there is no subject/principal associated with this thread if (subject == null || subject.getPrincipals().size() == 0) { return Result.ABSTAIN; } - - _logger.debug("Checking " + operation + " " + objectType); - return _ruleSet.check(subject, operation, objectType, properties); - } - public void configure(ConfigurationPlugin config) - { - super.configure(config); - - AccessControlConfiguration accessConfig = (AccessControlConfiguration) getConfig(); + if(_logger.isDebugEnabled()) + { + _logger.debug("Checking " + operation + " " + objectType + " " + ObjectUtils.defaultIfNull(addressOfClient, "")); + } - _ruleSet = accessConfig.getRuleSet(); + try + { + return _ruleSet.check(subject, operation, objectType, properties, addressOfClient); + } + catch(Exception e) + { + _logger.error("Unable to check " + operation + " " + objectType + " " + ObjectUtils.defaultIfNull(addressOfClient, ""), e); + return Result.DENIED; + } } } diff --git a/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactory.java b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactory.java new file mode 100644 index 0000000000..a3d7823caf --- /dev/null +++ b/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactory.java @@ -0,0 +1,59 @@ +/* + * + * 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.plugins; + +import java.io.File; +import java.util.Map; + +import org.apache.qpid.server.configuration.IllegalConfigurationException; +import org.apache.qpid.server.plugin.AccessControlFactory; +import org.apache.qpid.server.security.AccessControl; + +public class DefaultAccessControlFactory implements AccessControlFactory +{ + public static final String ATTRIBUTE_ACL_FILE = "aclFile"; + + public AccessControl createInstance(Map<String, Object> aclConfiguration) + { + if (aclConfiguration != null) + { + Object aclFile = aclConfiguration.get(ATTRIBUTE_ACL_FILE); + if (aclFile != null) + { + if (aclFile instanceof String) + { + String aclPath = (String) aclFile; + if (!new File(aclPath).exists()) + { + throw new IllegalConfigurationException("ACL file '" + aclPath + "' is not found"); + } + return new DefaultAccessControl(aclPath); + } + else + { + throw new IllegalConfigurationException("Expected '" + ATTRIBUTE_ACL_FILE + "' attribute value of type String but was " + aclFile.getClass() + + ": " + aclFile); + } + } + } + return null; + } +} diff --git a/java/broker-plugins/access-control/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.AccessControlFactory b/java/broker-plugins/access-control/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.AccessControlFactory new file mode 100644 index 0000000000..b6c429baab --- /dev/null +++ b/java/broker-plugins/access-control/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.AccessControlFactory @@ -0,0 +1,19 @@ +# +# 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. +# +org.apache.qpid.server.security.access.plugins.DefaultAccessControlFactory diff --git a/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/AclActionTest.java b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/AclActionTest.java new file mode 100644 index 0000000000..14620cff70 --- /dev/null +++ b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/AclActionTest.java @@ -0,0 +1,66 @@ +/* + * 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 static org.mockito.Mockito.*; + +import org.apache.qpid.server.security.access.ObjectProperties; +import org.apache.qpid.server.security.access.ObjectType; +import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.security.access.firewall.FirewallRule; + +import junit.framework.TestCase; + +public class AclActionTest extends TestCase +{ + public void testEqualsAndHashCode() + { + AclRulePredicates predicates = createAclRulePredicates(); + ObjectType objectType = ObjectType.EXCHANGE; + Operation operation = Operation.ACCESS; + + AclAction aclAction = new AclAction(operation, objectType, predicates); + AclAction equalAclAction = new AclAction(operation, objectType, predicates); + + assertTrue(aclAction.equals(aclAction)); + assertTrue(aclAction.equals(equalAclAction)); + assertTrue(equalAclAction.equals(aclAction)); + + assertTrue(aclAction.hashCode() == equalAclAction.hashCode()); + + assertFalse("Different operation should cause aclActions to be unequal", + aclAction.equals(new AclAction(Operation.BIND, objectType, predicates))); + + assertFalse("Different operation type should cause aclActions to be unequal", + aclAction.equals(new AclAction(operation, ObjectType.GROUP, predicates))); + + assertFalse("Different predicates should cause aclActions to be unequal", + aclAction.equals(new AclAction(operation, objectType, createAclRulePredicates()))); + + } + + private AclRulePredicates createAclRulePredicates() + { + AclRulePredicates predicates = mock(AclRulePredicates.class); + when(predicates.getFirewallRule()).thenReturn(mock(FirewallRule.class)); + when(predicates.getObjectProperties()).thenReturn(mock(ObjectProperties.class)); + return predicates; + } + +} diff --git a/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/AclRulePredicatesTest.java b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/AclRulePredicatesTest.java new file mode 100644 index 0000000000..93b765d0fb --- /dev/null +++ b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/AclRulePredicatesTest.java @@ -0,0 +1,87 @@ +/* + * 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 static org.apache.qpid.server.security.access.ObjectProperties.Property.*; + +import org.apache.qpid.server.security.access.firewall.FirewallRule; +import org.apache.qpid.server.security.access.firewall.FirewallRuleFactory; + +import static org.mockito.Mockito.*; + +import junit.framework.TestCase; + +public class AclRulePredicatesTest extends TestCase +{ + private AclRulePredicates _aclRulePredicates = new AclRulePredicates(); + private FirewallRuleFactory _firewallRuleFactory = mock(FirewallRuleFactory.class); + + @Override + protected void setUp() throws Exception + { + _aclRulePredicates.setFirewallRuleFactory(_firewallRuleFactory); + + when(_firewallRuleFactory.createForHostname((String[]) any())).thenReturn(mock(FirewallRule.class)); + when(_firewallRuleFactory.createForNetwork((String[]) any())).thenReturn(mock(FirewallRule.class)); + } + + public void testParse() + { + String name = "name"; + String className = "class"; + + _aclRulePredicates.parse(NAME.name(), name); + _aclRulePredicates.parse(CLASS.name(), className); + + assertEquals(name, _aclRulePredicates.getObjectProperties().get(NAME)); + assertEquals(className, _aclRulePredicates.getObjectProperties().get(CLASS)); + } + + public void testParseHostnameFirewallRule() + { + String hostname = "hostname1,hostname2"; + _aclRulePredicates.parse(FROM_HOSTNAME.name(), hostname); + + verify(_firewallRuleFactory).createForHostname(new String[] {"hostname1", "hostname2"}); + } + + public void testParseNetworkFirewallRule() + { + _aclRulePredicates.setFirewallRuleFactory(_firewallRuleFactory); + + String networks = "network1,network2"; + _aclRulePredicates.parse(FROM_NETWORK.name(), networks); + + verify(_firewallRuleFactory).createForNetwork(new String[] {"network1", "network2"}); + } + + public void testParseThrowsExceptionIfBothHostnameAndNetworkSpecified() + { + _aclRulePredicates.parse(FROM_NETWORK.name(), "network1,network2"); + try + { + _aclRulePredicates.parse(FROM_HOSTNAME.name(), "hostname1,hostname2"); + fail("Exception not thrown"); + } + catch(IllegalStateException e) + { + // pass + } + } +} diff --git a/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/ActionTest.java b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/ActionTest.java new file mode 100644 index 0000000000..00e06106bf --- /dev/null +++ b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/ActionTest.java @@ -0,0 +1,95 @@ +/* + * 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 static org.mockito.Mockito.*; + +import org.apache.qpid.server.security.access.ObjectProperties; +import org.apache.qpid.server.security.access.ObjectType; +import org.apache.qpid.server.security.access.Operation; + +import junit.framework.TestCase; + +public class ActionTest extends TestCase +{ + private ObjectProperties _properties1 = mock(ObjectProperties.class); + private ObjectProperties _properties2 = mock(ObjectProperties.class); + + public void testMatchesReturnsTrueForMatchingActions() + { + when(_properties1.matches(_properties2)).thenReturn(true); + + assertMatches( + new Action(Operation.CONSUME, ObjectType.QUEUE, _properties1), + new Action(Operation.CONSUME, ObjectType.QUEUE, _properties2)); + } + + public void testMatchesReturnsFalseWhenOperationsDiffer() + { + assertDoesntMatch( + new Action(Operation.CONSUME, ObjectType.QUEUE, _properties1), + new Action(Operation.CREATE, ObjectType.QUEUE, _properties1)); + } + + public void testMatchesReturnsFalseWhenOperationTypesDiffer() + { + assertDoesntMatch( + new Action(Operation.CREATE, ObjectType.QUEUE, _properties1), + new Action(Operation.CREATE, ObjectType.EXCHANGE, _properties1)); + } + + public void testMatchesReturnsFalseWhenOperationPropertiesDiffer() + { + assertDoesntMatch( + new Action(Operation.CREATE, ObjectType.QUEUE, _properties1), + new Action(Operation.CREATE, ObjectType.QUEUE, _properties2)); + } + + public void testMatchesReturnsFalseWhenMyOperationPropertiesIsNull() + { + assertDoesntMatch( + new Action(Operation.CREATE, ObjectType.QUEUE, (ObjectProperties)null), + new Action(Operation.CREATE, ObjectType.QUEUE, _properties1)); + } + + public void testMatchesReturnsFalseWhenOtherOperationPropertiesIsNull() + { + assertDoesntMatch( + new Action(Operation.CREATE, ObjectType.QUEUE, _properties1), + new Action(Operation.CREATE, ObjectType.QUEUE, (ObjectProperties)null)); + } + + public void testMatchesReturnsTrueWhenBothOperationPropertiesAreNull() + { + assertMatches( + new Action(Operation.CREATE, ObjectType.QUEUE, (ObjectProperties)null), + new Action(Operation.CREATE, ObjectType.QUEUE, (ObjectProperties)null)); + } + + private void assertMatches(Action action1, Action action2) + { + assertTrue(action1 + " should match " + action2, action1.matches(action2)); + } + + private void assertDoesntMatch(Action action1, Action action2) + { + assertFalse(action1 + " should not match " + action2, action1.matches(action2)); + } + +} diff --git a/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/ClientActionTest.java b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/ClientActionTest.java new file mode 100644 index 0000000000..ae5d3fda74 --- /dev/null +++ b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/ClientActionTest.java @@ -0,0 +1,79 @@ +/* + * 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 static org.mockito.Mockito.*; + +import java.net.InetAddress; + +import org.apache.qpid.server.security.access.firewall.FirewallRule; + +import junit.framework.TestCase; + +public class ClientActionTest extends TestCase +{ + private Action _action = mock(Action.class); + private AclAction _ruleAction = mock(AclAction.class); + private InetAddress _addressOfClient = mock(InetAddress.class); + + private ClientAction _clientAction = new ClientAction(_action); + + public void testMatches_returnsTrueWhenActionsMatchAndNoFirewallRule() + { + when(_action.matches(any(Action.class))).thenReturn(true); + when(_ruleAction.getFirewallRule()).thenReturn(null); + + assertTrue(_clientAction.matches(_ruleAction, _addressOfClient)); + } + + public void testMatches_returnsFalseWhenActionsDontMatch() + { + FirewallRule firewallRule = mock(FirewallRule.class); + when(firewallRule.matches(_addressOfClient)).thenReturn(true); + + when(_action.matches(any(Action.class))).thenReturn(false); + when(_ruleAction.getFirewallRule()).thenReturn(firewallRule); + + assertFalse(_clientAction.matches(_ruleAction, _addressOfClient)); + } + + public void testMatches_returnsTrueWhenActionsAndFirewallRuleMatch() + { + FirewallRule firewallRule = mock(FirewallRule.class); + when(firewallRule.matches(_addressOfClient)).thenReturn(true); + + when(_action.matches(any(Action.class))).thenReturn(true); + when(_ruleAction.getFirewallRule()).thenReturn(firewallRule); + + assertTrue(_clientAction.matches(_ruleAction, _addressOfClient)); + } + + public void testMatches_ignoresFirewallRuleIfClientAddressIsNull() + { + FirewallRule firewallRule = mock(FirewallRule.class); + + when(_action.matches(any(Action.class))).thenReturn(true); + when(_ruleAction.getFirewallRule()).thenReturn(firewallRule); + + assertTrue(_clientAction.matches(_ruleAction, null)); + + verifyZeroInteractions(firewallRule); + } + +} diff --git a/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/PlainConfigurationTest.java b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/PlainConfigurationTest.java index c2282694fb..cbfc9003c8 100644 --- a/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/PlainConfigurationTest.java +++ b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/PlainConfigurationTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.qpid.server.security.access.plugins; +package org.apache.qpid.server.security.access.config; import java.io.File; import java.io.FileNotFoundException; @@ -26,7 +26,7 @@ import java.util.Map; import junit.framework.TestCase; -import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.security.access.ObjectProperties; import org.apache.qpid.server.security.access.ObjectProperties.Property; import org.apache.qpid.server.security.access.ObjectType; @@ -73,7 +73,7 @@ public class PlainConfigurationTest extends TestCase fail("fail"); } - catch (ConfigurationException ce) + catch (IllegalConfigurationException ce) { assertEquals(String.format(PlainConfiguration.CONFIG_NOT_FOUND_MSG, "doesnotexist"), ce.getMessage()); assertTrue(ce.getCause() instanceof FileNotFoundException); @@ -87,7 +87,7 @@ public class PlainConfigurationTest extends TestCase writeACLConfig("ACL ALLOW ALL \\ ALL"); fail("fail"); } - catch (ConfigurationException ce) + catch (IllegalConfigurationException ce) { assertEquals(String.format(PlainConfiguration.PREMATURE_CONTINUATION_MSG, 1), ce.getMessage()); } @@ -100,7 +100,7 @@ public class PlainConfigurationTest extends TestCase writeACLConfig("ACL unparsed ALL ALL"); fail("fail"); } - catch (ConfigurationException ce) + catch (IllegalConfigurationException ce) { assertEquals(String.format(PlainConfiguration.PARSE_TOKEN_FAILED_MSG, 1), ce.getMessage()); assertTrue(ce.getCause() instanceof IllegalArgumentException); @@ -108,19 +108,6 @@ public class PlainConfigurationTest extends TestCase } } - public void testACLFileSyntaxNotEnoughGroup() throws Exception - { - try - { - writeACLConfig("GROUP blah"); - fail("fail"); - } - catch (ConfigurationException ce) - { - assertEquals(String.format(PlainConfiguration.NOT_ENOUGH_GROUP_MSG, 1), ce.getMessage()); - } - } - public void testACLFileSyntaxNotEnoughACL() throws Exception { try @@ -128,7 +115,7 @@ public class PlainConfigurationTest extends TestCase writeACLConfig("ACL ALLOW"); fail("fail"); } - catch (ConfigurationException ce) + catch (IllegalConfigurationException ce) { assertEquals(String.format(PlainConfiguration.NOT_ENOUGH_ACL_MSG, 1), ce.getMessage()); } @@ -141,7 +128,7 @@ public class PlainConfigurationTest extends TestCase writeACLConfig("CONFIG"); fail("fail"); } - catch (ConfigurationException ce) + catch (IllegalConfigurationException ce) { assertEquals(String.format(PlainConfiguration.NOT_ENOUGH_TOKENS_MSG, 1), ce.getMessage()); } @@ -154,7 +141,7 @@ public class PlainConfigurationTest extends TestCase writeACLConfig("INVALID"); fail("fail"); } - catch (ConfigurationException ce) + catch (IllegalConfigurationException ce) { assertEquals(String.format(PlainConfiguration.NOT_ENOUGH_TOKENS_MSG, 1), ce.getMessage()); } @@ -167,7 +154,7 @@ public class PlainConfigurationTest extends TestCase writeACLConfig("ACL ALLOW adk CREATE QUEUE name"); fail("fail"); } - catch (ConfigurationException ce) + catch (IllegalConfigurationException ce) { assertEquals(String.format(PlainConfiguration.PROPERTY_KEY_ONLY_MSG, 1), ce.getMessage()); } @@ -180,7 +167,7 @@ public class PlainConfigurationTest extends TestCase writeACLConfig("ACL ALLOW adk CREATE QUEUE name test"); fail("fail"); } - catch (ConfigurationException ce) + catch (IllegalConfigurationException ce) { assertEquals(String.format(PlainConfiguration.PROPERTY_NO_EQUALS_MSG, 1), ce.getMessage()); } @@ -193,7 +180,7 @@ public class PlainConfigurationTest extends TestCase writeACLConfig("ACL ALLOW adk CREATE QUEUE name ="); fail("fail"); } - catch (ConfigurationException ce) + catch (IllegalConfigurationException ce) { assertEquals(String.format(PlainConfiguration.PROPERTY_NO_VALUE_MSG, 1), ce.getMessage()); } @@ -391,4 +378,86 @@ public class PlainConfigurationTest extends TestCase assertEquals("Rule has unexpected object properties", ObjectProperties.EMPTY, rule.getAction().getProperties()); } + public void testUserRuleParsing() throws Exception + { + validateRule(writeACLConfig("ACL ALLOW user1 CREATE USER"), + "user1", Operation.CREATE, ObjectType.USER, ObjectProperties.EMPTY); + validateRule(writeACLConfig("ACL ALLOW user1 CREATE USER name=\"otherUser\""), + "user1", Operation.CREATE, ObjectType.USER, new ObjectProperties("otherUser")); + + validateRule(writeACLConfig("ACL ALLOW user1 DELETE USER"), + "user1", Operation.DELETE, ObjectType.USER, ObjectProperties.EMPTY); + validateRule(writeACLConfig("ACL ALLOW user1 DELETE USER name=\"otherUser\""), + "user1", Operation.DELETE, ObjectType.USER, new ObjectProperties("otherUser")); + + validateRule(writeACLConfig("ACL ALLOW user1 UPDATE USER"), + "user1", Operation.UPDATE, ObjectType.USER, ObjectProperties.EMPTY); + validateRule(writeACLConfig("ACL ALLOW user1 UPDATE USER name=\"otherUser\""), + "user1", Operation.UPDATE, ObjectType.USER, new ObjectProperties("otherUser")); + + validateRule(writeACLConfig("ACL ALLOW user1 ALL USER"), + "user1", Operation.ALL, ObjectType.USER, ObjectProperties.EMPTY); + validateRule(writeACLConfig("ACL ALLOW user1 ALL USER name=\"otherUser\""), + "user1", Operation.ALL, ObjectType.USER, new ObjectProperties("otherUser")); + } + + public void testGroupRuleParsing() throws Exception + { + validateRule(writeACLConfig("ACL ALLOW user1 CREATE GROUP"), + "user1", Operation.CREATE, ObjectType.GROUP, ObjectProperties.EMPTY); + validateRule(writeACLConfig("ACL ALLOW user1 CREATE GROUP name=\"groupName\""), + "user1", Operation.CREATE, ObjectType.GROUP, new ObjectProperties("groupName")); + + validateRule(writeACLConfig("ACL ALLOW user1 DELETE GROUP"), + "user1", Operation.DELETE, ObjectType.GROUP, ObjectProperties.EMPTY); + validateRule(writeACLConfig("ACL ALLOW user1 DELETE GROUP name=\"groupName\""), + "user1", Operation.DELETE, ObjectType.GROUP, new ObjectProperties("groupName")); + + validateRule(writeACLConfig("ACL ALLOW user1 UPDATE GROUP"), + "user1", Operation.UPDATE, ObjectType.GROUP, ObjectProperties.EMPTY); + validateRule(writeACLConfig("ACL ALLOW user1 UPDATE GROUP name=\"groupName\""), + "user1", Operation.UPDATE, ObjectType.GROUP, new ObjectProperties("groupName")); + + validateRule(writeACLConfig("ACL ALLOW user1 ALL GROUP"), + "user1", Operation.ALL, ObjectType.GROUP, ObjectProperties.EMPTY); + validateRule(writeACLConfig("ACL ALLOW user1 ALL GROUP name=\"groupName\""), + "user1", Operation.ALL, ObjectType.GROUP, new ObjectProperties("groupName")); + } + + /** explicitly test for exception indicating that this functionality has been moved to Group Providers */ + public void testGroupDefinitionThrowsException() throws Exception + { + try + { + writeACLConfig("GROUP group1 bob alice"); + fail("Expected exception not thrown"); + } + catch(IllegalConfigurationException e) + { + assertTrue(e.getMessage().contains("GROUP keyword not supported")); + } + } + + public void testManagementRuleParsing() throws Exception + { + validateRule(writeACLConfig("ACL ALLOW user1 ALL MANAGEMENT"), + "user1", Operation.ALL, ObjectType.MANAGEMENT, ObjectProperties.EMPTY); + + validateRule(writeACLConfig("ACL ALLOW user1 ACCESS MANAGEMENT"), + "user1", Operation.ACCESS, ObjectType.MANAGEMENT, ObjectProperties.EMPTY); + } + + private void validateRule(final PlainConfiguration config, String username, Operation operation, ObjectType objectType, ObjectProperties objectProperties) + { + final RuleSet rs = config.getConfiguration(); + assertEquals(1, rs.getRuleCount()); + + final Map<Integer, Rule> rules = rs.getAllRules(); + 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.getAction().getOperation()); + assertEquals("Rule has unexpected operation", objectType, rule.getAction().getObjectType()); + assertEquals("Rule has unexpected object properties", objectProperties, rule.getAction().getProperties()); + } } diff --git a/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/RuleTest.java b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/RuleTest.java new file mode 100644 index 0000000000..2ae7759679 --- /dev/null +++ b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/RuleTest.java @@ -0,0 +1,53 @@ +/* + * 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 static org.mockito.Mockito.*; + +import org.apache.qpid.server.security.access.Permission; + +import junit.framework.TestCase; + +public class RuleTest extends TestCase +{ + public void testEqualsAndHashCode() + { + AclAction aclAction = mock(AclAction.class); + String identity = "identity"; + Permission allow = Permission.ALLOW; + + Rule rule = new Rule(identity, aclAction, allow); + Rule equalRule = new Rule(identity, aclAction, allow); + + assertTrue(rule.equals(rule)); + assertTrue(rule.equals(equalRule)); + assertTrue(equalRule.equals(rule)); + + assertTrue(rule.hashCode() == equalRule.hashCode()); + + assertFalse("Different identity should cause rules to be unequal", + rule.equals(new Rule("identity2", aclAction, allow))); + + assertFalse("Different action should cause rules to be unequal", + rule.equals(new Rule(identity, mock(AclAction.class), allow))); + + assertFalse("Different permission should cause rules to be unequal", + rule.equals(new Rule(identity, aclAction, Permission.DENY))); + } +} diff --git a/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/firewall/HostnameFirewallRuleTest.java b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/firewall/HostnameFirewallRuleTest.java new file mode 100644 index 0000000000..be82cb294a --- /dev/null +++ b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/firewall/HostnameFirewallRuleTest.java @@ -0,0 +1,99 @@ +/* + * 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.firewall; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.net.InetAddress; + +import org.apache.qpid.server.security.access.firewall.HostnameFirewallRule; + +import junit.framework.TestCase; + +public class HostnameFirewallRuleTest extends TestCase +{ + private InetAddress _addressNotInRule; + + private HostnameFirewallRule _HostnameFirewallRule; + + @Override + protected void setUp() throws Exception + { + _addressNotInRule = InetAddress.getByName("127.0.0.1"); + } + + public void testSingleHostname() throws Exception + { + String hostnameInRule = "hostnameInRule"; + InetAddress addressWithMatchingHostname = mock(InetAddress.class); + when(addressWithMatchingHostname.getCanonicalHostName()).thenReturn(hostnameInRule); + + _HostnameFirewallRule = new HostnameFirewallRule(hostnameInRule); + + assertFalse(_HostnameFirewallRule.matches(_addressNotInRule)); + assertTrue(_HostnameFirewallRule.matches(addressWithMatchingHostname)); + } + + public void testSingleHostnameWilcard() throws Exception + { + String hostnameInRule = ".*FOO.*"; + InetAddress addressWithMatchingHostname = mock(InetAddress.class); + when(addressWithMatchingHostname.getCanonicalHostName()).thenReturn("xxFOOxx"); + + _HostnameFirewallRule = new HostnameFirewallRule(hostnameInRule); + + assertFalse(_HostnameFirewallRule.matches(_addressNotInRule)); + assertTrue(_HostnameFirewallRule.matches(addressWithMatchingHostname)); + } + + public void testMultipleHostnames() throws Exception + { + String[] hostnamesInRule = new String[] {"hostnameInRule1", "hostnameInRule2"}; + + _HostnameFirewallRule = new HostnameFirewallRule(hostnamesInRule); + + assertFalse(_HostnameFirewallRule.matches(_addressNotInRule)); + for (String hostnameInRule : hostnamesInRule) + { + InetAddress addressWithMatchingHostname = mock(InetAddress.class); + when(addressWithMatchingHostname.getCanonicalHostName()).thenReturn(hostnameInRule); + + assertTrue(_HostnameFirewallRule.matches(addressWithMatchingHostname)); + } + } + + public void testEqualsAndHashCode() + { + String hostname1 = "hostname1"; + String hostname2 = "hostname2"; + + HostnameFirewallRule rule = new HostnameFirewallRule(hostname1, hostname2); + HostnameFirewallRule equalRule = new HostnameFirewallRule(hostname1, hostname2); + + assertTrue(rule.equals(rule)); + assertTrue(rule.equals(equalRule)); + assertTrue(equalRule.equals(rule)); + + assertTrue(rule.hashCode() == equalRule.hashCode()); + + assertFalse("Different hostnames should cause rules to be unequal", + rule.equals(new HostnameFirewallRule(hostname1, "different-hostname"))); + } +} diff --git a/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/firewall/NetworkFirewallRuleTest.java b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/firewall/NetworkFirewallRuleTest.java new file mode 100644 index 0000000000..e521039db2 --- /dev/null +++ b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/firewall/NetworkFirewallRuleTest.java @@ -0,0 +1,115 @@ +/* + * 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.firewall; + +import java.net.InetAddress; + +import org.apache.qpid.server.security.access.firewall.NetworkFirewallRule; + +import junit.framework.TestCase; + +public class NetworkFirewallRuleTest extends TestCase +{ + private static final String LOCALHOST_IP = "127.0.0.1"; + private static final String OTHER_IP_1 = "192.168.23.1"; + private static final String OTHER_IP_2 = "192.168.23.2"; + + private InetAddress _addressNotInRule; + + private NetworkFirewallRule _networkFirewallRule; + + @Override + protected void setUp() throws Exception + { + _addressNotInRule = InetAddress.getByName(LOCALHOST_IP); + } + + public void testIpRule() throws Exception + { + String ipAddressInRule = OTHER_IP_1; + + _networkFirewallRule = new NetworkFirewallRule(ipAddressInRule); + + assertFalse(_networkFirewallRule.matches(_addressNotInRule)); + assertTrue(_networkFirewallRule.matches(InetAddress.getByName(ipAddressInRule))); + } + + public void testNetMask() throws Exception + { + String ipAddressInRule = "192.168.23.0/24"; + _networkFirewallRule = new NetworkFirewallRule(ipAddressInRule); + + assertFalse(_networkFirewallRule.matches(InetAddress.getByName("192.168.24.1"))); + assertTrue(_networkFirewallRule.matches(InetAddress.getByName("192.168.23.0"))); + assertTrue(_networkFirewallRule.matches(InetAddress.getByName("192.168.23.255"))); + } + + public void testWildcard() throws Exception + { + // Test xxx.xxx.* + + assertFalse(new NetworkFirewallRule("192.168.*") + .matches(InetAddress.getByName("192.169.1.0"))); + + assertTrue(new NetworkFirewallRule("192.168.*") + .matches(InetAddress.getByName("192.168.1.0"))); + + assertTrue(new NetworkFirewallRule("192.168.*") + .matches(InetAddress.getByName("192.168.255.255"))); + + // Test xxx.xxx.xxx.* + + assertFalse(new NetworkFirewallRule("192.168.1.*") + .matches(InetAddress.getByName("192.169.2.0"))); + + assertTrue(new NetworkFirewallRule("192.168.1.*") + .matches(InetAddress.getByName("192.168.1.0"))); + + assertTrue(new NetworkFirewallRule("192.168.1.*") + .matches(InetAddress.getByName("192.168.1.255"))); + } + + public void testMultipleNetworks() throws Exception + { + String[] ipAddressesInRule = new String[] {OTHER_IP_1, OTHER_IP_2}; + + _networkFirewallRule = new NetworkFirewallRule(ipAddressesInRule); + + assertFalse(_networkFirewallRule.matches(_addressNotInRule)); + for (String ipAddressInRule : ipAddressesInRule) + { + assertTrue(_networkFirewallRule.matches(InetAddress.getByName(ipAddressInRule))); + } + } + + public void testEqualsAndHashCode() + { + NetworkFirewallRule rule = new NetworkFirewallRule(LOCALHOST_IP, OTHER_IP_1); + NetworkFirewallRule equalRule = new NetworkFirewallRule(LOCALHOST_IP, OTHER_IP_1); + + assertTrue(rule.equals(rule)); + assertTrue(rule.equals(equalRule)); + assertTrue(equalRule.equals(rule)); + + assertTrue(rule.hashCode() == equalRule.hashCode()); + + assertFalse("Different networks should cause rules to be unequal", + rule.equals(new NetworkFirewallRule(LOCALHOST_IP, OTHER_IP_2))); + } +} diff --git a/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactoryTest.java b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactoryTest.java new file mode 100644 index 0000000000..ca1f19098f --- /dev/null +++ b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactoryTest.java @@ -0,0 +1,69 @@ +package org.apache.qpid.server.security.access.plugins; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Pattern; + +import org.apache.qpid.server.configuration.IllegalConfigurationException; +import org.apache.qpid.server.security.AccessControl; +import org.apache.qpid.test.utils.QpidTestCase; +import org.apache.qpid.test.utils.TestFileUtils; + +public class DefaultAccessControlFactoryTest extends QpidTestCase +{ + public void testCreateInstanceWhenAclFileIsNotPresent() + { + DefaultAccessControlFactory factory = new DefaultAccessControlFactory(); + Map<String, Object> attributes = new HashMap<String, Object>(); + AccessControl acl = factory.createInstance(attributes); + assertNull("ACL was created without a configuration file", acl); + } + + public void testCreateInstanceWhenAclFileIsSpecified() + { + File aclFile = TestFileUtils.createTempFile(this, ".acl", "ACL ALLOW all all"); + DefaultAccessControlFactory factory = new DefaultAccessControlFactory(); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(DefaultAccessControlFactory.ATTRIBUTE_ACL_FILE, aclFile.getAbsolutePath()); + AccessControl acl = factory.createInstance(attributes); + + assertNotNull("ACL was not created from acl file: " + aclFile.getAbsolutePath(), acl); + } + + public void testCreateInstanceWhenAclFileIsSpecifiedButDoesNotExist() + { + File aclFile = new File(TMP_FOLDER, "my-non-existing-acl-" + System.currentTimeMillis()); + assertFalse("ACL file " + aclFile.getAbsolutePath() + " actually exists but should not", aclFile.exists()); + DefaultAccessControlFactory factory = new DefaultAccessControlFactory(); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(DefaultAccessControlFactory.ATTRIBUTE_ACL_FILE, aclFile.getAbsolutePath()); + try + { + factory.createInstance(attributes); + fail("It should not be possible to create ACL from non existing file"); + } + catch (IllegalConfigurationException e) + { + assertTrue("Unexpected exception message", Pattern.matches("ACL file '.*' is not found", e.getMessage())); + } + } + + public void testCreateInstanceWhenAclFileIsSpecifiedAsNonString() + { + DefaultAccessControlFactory factory = new DefaultAccessControlFactory(); + Map<String, Object> attributes = new HashMap<String, Object>(); + Integer aclFile = new Integer(0); + attributes.put(DefaultAccessControlFactory.ATTRIBUTE_ACL_FILE, aclFile); + try + { + factory.createInstance(attributes); + fail("It should not be possible to create ACL from Integer"); + } + catch (IllegalConfigurationException e) + { + assertEquals("Unexpected exception message", "Expected '" + DefaultAccessControlFactory.ATTRIBUTE_ACL_FILE + + "' attribute value of type String but was " + Integer.class + ": " + aclFile, e.getMessage()); + } + } +} diff --git a/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/AccessControlTest.java b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlTest.java index 5db02d10ce..a8406308c0 100644 --- a/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/AccessControlTest.java +++ b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlTest.java @@ -20,12 +20,16 @@ */ package org.apache.qpid.server.security.access.plugins; -import java.util.Arrays; +import static org.mockito.Mockito.*; + +import java.net.InetAddress; +import java.net.InetSocketAddress; + +import javax.security.auth.Subject; import junit.framework.TestCase; import org.apache.commons.configuration.ConfigurationException; -import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; import org.apache.qpid.server.logging.UnitTestMessageLogger; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.actors.TestLogActor; @@ -37,20 +41,19 @@ import org.apache.qpid.server.security.access.Operation; import org.apache.qpid.server.security.access.Permission; import org.apache.qpid.server.security.access.config.Rule; import org.apache.qpid.server.security.access.config.RuleSet; -import org.apache.qpid.server.security.auth.sasl.TestPrincipalUtils; +import org.apache.qpid.server.security.auth.TestPrincipalUtils; /** - * Unit test for ACL V2 plugin. - * - * This unit test tests the AccessControl class and it collaboration with {@link RuleSet}, - * {@link SecurityManager} and {@link CurrentActor}. The ruleset is configured programmatically, - * rather than from an external file. - * + * In these tests, the ruleset is configured programmatically rather than from an external file. + * * @see RuleSetTest */ -public class AccessControlTest extends TestCase +public class DefaultAccessControlTest extends TestCase { - private AccessControl _plugin = null; // Class under test + private static final String ALLOWED_GROUP = "allowed_group"; + private static final String DENIED_GROUP = "denied_group"; + + private DefaultAccessControl _plugin = null; // Class under test private final UnitTestMessageLogger messageLogger = new UnitTestMessageLogger(); private void setUpGroupAccessControl() throws ConfigurationException @@ -60,7 +63,7 @@ public class AccessControlTest extends TestCase private void configureAccessControl(final RuleSet rs) throws ConfigurationException { - _plugin = (AccessControl) AccessControl.FACTORY.newInstance(createConfiguration(rs)); + _plugin = new DefaultAccessControl(rs); SecurityManager.setThreadSubject(null); CurrentActor.set(new TestLogActor(messageLogger)); } @@ -68,14 +71,12 @@ public class AccessControlTest extends TestCase private RuleSet createGroupRuleSet() { final RuleSet rs = new RuleSet(); - rs.addGroup("aclGroup1", Arrays.asList(new String[] {"member1", "Member2"})); // Rule expressed with username rs.grant(0, "user1", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - // Rule expressed with a acl group - rs.grant(1, "aclGroup1", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - // Rule expressed with an external group - rs.grant(2, "extGroup1", Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + // Rules expressed with groups + rs.grant(1, ALLOWED_GROUP, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + rs.grant(2, DENIED_GROUP, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); // Catch all rule rs.grant(3, Rule.ALL, Permission.DENY_LOG, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); @@ -117,31 +118,23 @@ public class AccessControlTest extends TestCase * Tests that an allow rule expressed with an <b>ACL groupname</b> allows an operation performed by a thread running * by a user who belongs to the same group.. */ - public void testAclGroupMembershipAllowsOperation() throws ConfigurationException + public void testGroupMembershipAllowsOperation() throws ConfigurationException { setUpGroupAccessControl(); - SecurityManager.setThreadSubject(TestPrincipalUtils.createTestSubject("member1")); - Result result = _plugin.authorise(Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - assertEquals(Result.ALLOWED, result); - - SecurityManager.setThreadSubject(TestPrincipalUtils.createTestSubject("Member2")); - - result = _plugin.authorise(Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - assertEquals(Result.ALLOWED, result); + authoriseAndAssertResult(Result.ALLOWED, "member of allowed group", ALLOWED_GROUP); + authoriseAndAssertResult(Result.DENIED, "member of denied group", DENIED_GROUP); + authoriseAndAssertResult(Result.ALLOWED, "another member of allowed group", ALLOWED_GROUP); } /** - * Tests that a deny rule expressed with an <b>External groupname</b> denies an operation performed by a thread running + * Tests that a deny rule expressed with a <b>groupname</b> denies an operation performed by a thread running * by a user who belongs to the same group. */ - public void testExternalGroupMembershipDeniesOperation() throws ConfigurationException + public void testGroupMembershipDeniesOperation() throws ConfigurationException { setUpGroupAccessControl(); - SecurityManager.setThreadSubject(TestPrincipalUtils.createTestSubject("user3", "extGroup1")); - - final Result result = _plugin.authorise(Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - assertEquals(Result.DENIED, result); + authoriseAndAssertResult(Result.DENIED, "user3", DENIED_GROUP); } /** @@ -203,6 +196,46 @@ public class AccessControlTest extends TestCase assertEquals(Result.DEFER, result); } + public void testAccess() throws Exception + { + Subject subject = TestPrincipalUtils.createTestSubject("user1"); + SecurityManager.setThreadSubject(subject); + + RuleSet mockRuleSet = mock(RuleSet.class); + + InetAddress inetAddress = InetAddress.getLocalHost(); + InetSocketAddress inetSocketAddress = new InetSocketAddress(inetAddress, 1); + + DefaultAccessControl accessControl = new DefaultAccessControl(mockRuleSet); + + accessControl.access(ObjectType.VIRTUALHOST, inetSocketAddress); + + verify(mockRuleSet).check(subject, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY, inetAddress); + } + + public void testAccessIsDeniedIfRuleThrowsException() throws Exception + { + Subject subject = TestPrincipalUtils.createTestSubject("user1"); + SecurityManager.setThreadSubject(subject); + + InetAddress inetAddress = InetAddress.getLocalHost(); + InetSocketAddress inetSocketAddress = new InetSocketAddress(inetAddress, 1); + + RuleSet mockRuleSet = mock(RuleSet.class); + when(mockRuleSet.check( + subject, + Operation.ACCESS, + ObjectType.VIRTUALHOST, + ObjectProperties.EMPTY, + inetAddress)).thenThrow(new RuntimeException()); + + DefaultAccessControl accessControl = new DefaultAccessControl(mockRuleSet); + Result result = accessControl.access(ObjectType.VIRTUALHOST, inetSocketAddress); + + assertEquals(Result.DENIED, result); + } + + /** * Tests that a grant access method rule allows any access operation to be performed on a specified component */ @@ -325,31 +358,11 @@ public class AccessControlTest extends TestCase assertEquals(Result.DEFER, result); } - /** - * Creates a configuration plugin for the {@link AccessControl} plugin. - */ - private ConfigurationPlugin createConfiguration(final RuleSet rs) + private void authoriseAndAssertResult(Result expectedResult, String userName, String... groups) { - final ConfigurationPlugin cp = new ConfigurationPlugin() - { - @SuppressWarnings("unchecked") - public AccessControlConfiguration getConfiguration(final String plugin) - { - return new AccessControlConfiguration() - { - public RuleSet getRuleSet() - { - return rs; - } - }; - } - - public String[] getElementsProcessed() - { - throw new UnsupportedOperationException(); - } - }; - - return cp; + SecurityManager.setThreadSubject(TestPrincipalUtils.createTestSubject(userName, groups)); + + Result result = _plugin.authorise(Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + assertEquals(expectedResult, result); } } diff --git a/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java index f7cc60543d..181d693614 100644 --- a/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java +++ b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java @@ -22,7 +22,6 @@ package org.apache.qpid.server.security.access.plugins; import java.security.Principal; -import java.util.Arrays; import javax.security.auth.Subject; @@ -34,8 +33,7 @@ import org.apache.qpid.server.security.access.Operation; import org.apache.qpid.server.security.access.Permission; import org.apache.qpid.server.security.access.config.Rule; import org.apache.qpid.server.security.access.config.RuleSet; -import org.apache.qpid.server.security.auth.sasl.TestPrincipalUtils; -import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.security.auth.TestPrincipalUtils; import org.apache.qpid.test.utils.QpidTestCase; /** @@ -46,10 +44,7 @@ import org.apache.qpid.test.utils.QpidTestCase; * access control mechanism is validated by checking whether operations would be authorised by calling the * {@link RuleSet#check(Principal, Operation, ObjectType, ObjectProperties)} method. * - * It ensure that permissions can be granted correctly on users directly, ACL groups (that is those - * groups declared directly in the ACL itself), and External groups (that is a group from an External - * Authentication Provider, such as an LDAP). - + * It ensure that permissions can be granted correctly on users directly and on groups. */ public class RuleSetTest extends QpidTestCase { @@ -316,63 +311,36 @@ public class RuleSetTest extends QpidTestCase assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("userb"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); } - /** - * Tests support for ACL groups (i.e. inline groups declared in the ACL file itself). - */ - public void testAclGroupsSupported() + public void testGroupsSupported() { - assertTrue(_ruleSet.addGroup("aclgroup", Arrays.asList(new String[] {"usera", "userb"}))); - - _ruleSet.grant(1, "aclgroup", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - assertEquals(1, _ruleSet.getRuleCount()); - - assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("usera"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); - assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("userb"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); - assertEquals(Result.DEFER, _ruleSet.check(TestPrincipalUtils.createTestSubject("userc"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); - } - - /** - * Tests support for nested ACL groups. - */ - public void testNestedAclGroupsSupported() - { - assertTrue(_ruleSet.addGroup("aclgroup1", Arrays.asList(new String[] {"userb"}))); - assertTrue(_ruleSet.addGroup("aclgroup2", Arrays.asList(new String[] {"usera", "aclgroup1"}))); - - _ruleSet.grant(1, "aclgroup2", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - assertEquals(1, _ruleSet.getRuleCount()); + String allowGroup = "allowGroup"; + String deniedGroup = "deniedGroup"; - assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("usera"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); - assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("userb"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); - } + _ruleSet.grant(1, allowGroup, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + _ruleSet.grant(2, deniedGroup, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - /** - * Tests support for nested External groups (i.e. those groups coming from an external source such as an LDAP). - */ - public void testExternalGroupsSupported() - { - _ruleSet.grant(1, "extgroup1", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - _ruleSet.grant(2, "extgroup2", Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); assertEquals(2, _ruleSet.getRuleCount()); - assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("usera", "extgroup1"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); - assertEquals(Result.DENIED, _ruleSet.check(TestPrincipalUtils.createTestSubject("userb", "extgroup2"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); + assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("usera", allowGroup),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); + assertEquals(Result.DENIED, _ruleSet.check(TestPrincipalUtils.createTestSubject("userb", deniedGroup),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); + assertEquals(Result.DEFER, _ruleSet.check(TestPrincipalUtils.createTestSubject("user", "group not mentioned in acl"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); } /** * Rule order in the ACL determines the outcome of the check. This test ensures that a user who is - * granted explicit permission on an object, is granted that access even although late a group + * granted explicit permission on an object, is granted that access even though a group * to which the user belongs is later denied the permission. */ public void testAllowDeterminedByRuleOrder() { - assertTrue(_ruleSet.addGroup("aclgroup", Arrays.asList(new String[] {"usera"}))); + String group = "group"; + String user = "user"; - _ruleSet.grant(1, "usera", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - _ruleSet.grant(2, "aclgroup", Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + _ruleSet.grant(1, user, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + _ruleSet.grant(2, group, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); assertEquals(2, _ruleSet.getRuleCount()); - assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("usera"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); + assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject(user, group),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); } /** @@ -381,13 +349,33 @@ public class RuleSetTest extends QpidTestCase */ public void testDenyDeterminedByRuleOrder() { - assertTrue(_ruleSet.addGroup("aclgroup", Arrays.asList(new String[] {"usera"}))); + String group = "aclgroup"; + String user = "usera"; - _ruleSet.grant(1, "aclgroup", Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - _ruleSet.grant(2, "usera", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + _ruleSet.grant(1, group, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + _ruleSet.grant(2, user, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); assertEquals(2, _ruleSet.getRuleCount()); - assertEquals(Result.DENIED, _ruleSet.check(TestPrincipalUtils.createTestSubject("usera"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); + assertEquals(Result.DENIED, _ruleSet.check(TestPrincipalUtils.createTestSubject(user, group),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); + } + + public void testUserInMultipleGroups() + { + String allowedGroup = "group1"; + String deniedGroup = "group2"; + + _ruleSet.grant(1, allowedGroup, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + _ruleSet.grant(2, deniedGroup, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + + Subject subjectInBothGroups = TestPrincipalUtils.createTestSubject("user", allowedGroup, deniedGroup); + Subject subjectInDeniedGroupAndOneOther = TestPrincipalUtils.createTestSubject("user", deniedGroup, "some other group"); + Subject subjectInAllowedGroupAndOneOther = TestPrincipalUtils.createTestSubject("user", allowedGroup, "some other group"); + + assertEquals(Result.ALLOWED, _ruleSet.check(subjectInBothGroups,Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); + + assertEquals(Result.DENIED, _ruleSet.check(subjectInDeniedGroupAndOneOther,Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); + + assertEquals(Result.ALLOWED, _ruleSet.check(subjectInAllowedGroupAndOneOther,Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); } } |