/* * 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.io.FileNotFoundException; import java.io.FileWriter; import java.io.PrintWriter; import java.util.Map; import junit.framework.TestCase; import org.apache.commons.configuration.ConfigurationException; 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; 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.Rule; import org.apache.qpid.server.security.access.config.RuleSet; /** * These tests check that the ACL file parsing works correctly. * * For each message that can be returned in a {@link ConfigurationException}, an ACL file is created that should trigger this * particular message. */ public class PlainConfigurationTest extends TestCase { private PlainConfiguration writeACLConfig(String...aclData) throws Exception { File acl = File.createTempFile(getClass().getName() + getName(), "acl"); acl.deleteOnExit(); // Write ACL file PrintWriter aclWriter = new PrintWriter(new FileWriter(acl)); for (String line : aclData) { aclWriter.println(line); } aclWriter.close(); // Load ruleset PlainConfiguration configFile = new PlainConfiguration(acl); configFile.load(); return configFile; } public void testMissingACLConfig() throws Exception { try { // Load ruleset ConfigurationFile configFile = new PlainConfiguration(new File("doesnotexist")); configFile.load(); fail("fail"); } catch (ConfigurationException ce) { assertEquals(String.format(PlainConfiguration.CONFIG_NOT_FOUND_MSG, "doesnotexist"), ce.getMessage()); assertTrue(ce.getCause() instanceof FileNotFoundException); } } public void testACLFileSyntaxContinuation() throws Exception { try { writeACLConfig("ACL ALLOW ALL \\ ALL"); fail("fail"); } catch (ConfigurationException ce) { assertEquals(String.format(PlainConfiguration.PREMATURE_CONTINUATION_MSG, 1), ce.getMessage()); } } public void testACLFileSyntaxTokens() throws Exception { try { writeACLConfig("ACL unparsed ALL ALL"); fail("fail"); } catch (ConfigurationException ce) { assertEquals(String.format(PlainConfiguration.PARSE_TOKEN_FAILED_MSG, 1), ce.getMessage()); assertTrue(ce.getCause() instanceof IllegalArgumentException); assertEquals("Not a valid permission: unparsed", ce.getCause().getMessage()); } } 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 { writeACLConfig("ACL ALLOW"); fail("fail"); } catch (ConfigurationException ce) { assertEquals(String.format(PlainConfiguration.NOT_ENOUGH_ACL_MSG, 1), ce.getMessage()); } } public void testACLFileSyntaxNotEnoughConfig() throws Exception { try { writeACLConfig("CONFIG"); fail("fail"); } catch (ConfigurationException ce) { assertEquals(String.format(PlainConfiguration.NOT_ENOUGH_TOKENS_MSG, 1), ce.getMessage()); } } public void testACLFileSyntaxNotEnough() throws Exception { try { writeACLConfig("INVALID"); fail("fail"); } catch (ConfigurationException ce) { assertEquals(String.format(PlainConfiguration.NOT_ENOUGH_TOKENS_MSG, 1), ce.getMessage()); } } public void testACLFileSyntaxPropertyKeyOnly() throws Exception { try { writeACLConfig("ACL ALLOW adk CREATE QUEUE name"); fail("fail"); } catch (ConfigurationException ce) { assertEquals(String.format(PlainConfiguration.PROPERTY_KEY_ONLY_MSG, 1), ce.getMessage()); } } public void testACLFileSyntaxPropertyNoEquals() throws Exception { try { writeACLConfig("ACL ALLOW adk CREATE QUEUE name test"); fail("fail"); } catch (ConfigurationException ce) { assertEquals(String.format(PlainConfiguration.PROPERTY_NO_EQUALS_MSG, 1), ce.getMessage()); } } public void testACLFileSyntaxPropertyNoValue() throws Exception { try { writeACLConfig("ACL ALLOW adk CREATE QUEUE name ="); fail("fail"); } catch (ConfigurationException ce) { assertEquals(String.format(PlainConfiguration.PROPERTY_NO_VALUE_MSG, 1), ce.getMessage()); } } /** * Tests interpretation of an acl rule with no object properties. * */ public void testValidRule() throws Exception { final PlainConfiguration config = writeACLConfig("ACL DENY-LOG user1 ACCESS VIRTUALHOST"); final RuleSet rs = config.getConfiguration(); assertEquals(1, rs.getRuleCount()); final Map rules = rs.getAllRules(); assertEquals(1, rules.size()); final Rule rule = rules.get(0); assertEquals("Rule has unexpected identity", "user1", rule.getIdentity()); assertEquals("Rule has unexpected operation", Operation.ACCESS, rule.getAction().getOperation()); assertEquals("Rule has unexpected operation", ObjectType.VIRTUALHOST, rule.getAction().getObjectType()); assertEquals("Rule has unexpected object properties", ObjectProperties.EMPTY, rule.getAction().getProperties()); } /** * Tests interpretation of an acl rule with object properties quoted in single quotes. */ public void testValidRuleWithSingleQuotedProperty() throws Exception { final PlainConfiguration config = writeACLConfig("ACL ALLOW all CREATE EXCHANGE name = \'value\'"); final RuleSet rs = config.getConfiguration(); assertEquals(1, rs.getRuleCount()); final Map rules = rs.getAllRules(); assertEquals(1, rules.size()); final Rule rule = rules.get(0); assertEquals("Rule has unexpected identity", "all", rule.getIdentity()); assertEquals("Rule has unexpected operation", Operation.CREATE, rule.getAction().getOperation()); assertEquals("Rule has unexpected operation", ObjectType.EXCHANGE, rule.getAction().getObjectType()); final ObjectProperties expectedProperties = new ObjectProperties(); expectedProperties.setName("value"); assertEquals("Rule has unexpected object properties", expectedProperties, rule.getAction().getProperties()); } /** * Tests interpretation of an acl rule with object properties quoted in double quotes. */ public void testValidRuleWithDoubleQuotedProperty() throws Exception { final PlainConfiguration config = writeACLConfig("ACL ALLOW all CREATE EXCHANGE name = \"value\""); final RuleSet rs = config.getConfiguration(); assertEquals(1, rs.getRuleCount()); final Map rules = rs.getAllRules(); assertEquals(1, rules.size()); final Rule rule = rules.get(0); assertEquals("Rule has unexpected identity", "all", rule.getIdentity()); assertEquals("Rule has unexpected operation", Operation.CREATE, rule.getAction().getOperation()); assertEquals("Rule has unexpected operation", ObjectType.EXCHANGE, rule.getAction().getObjectType()); final ObjectProperties expectedProperties = new ObjectProperties(); expectedProperties.setName("value"); assertEquals("Rule has unexpected object properties", expectedProperties, rule.getAction().getProperties()); } /** * Tests interpretation of an acl rule with many object properties. */ public void testValidRuleWithManyProperties() throws Exception { final PlainConfiguration config = writeACLConfig("ACL ALLOW admin DELETE QUEUE name=name1 owner = owner1"); final RuleSet rs = config.getConfiguration(); assertEquals(1, rs.getRuleCount()); final Map rules = rs.getAllRules(); assertEquals(1, rules.size()); final Rule rule = rules.get(0); assertEquals("Rule has unexpected identity", "admin", rule.getIdentity()); assertEquals("Rule has unexpected operation", Operation.DELETE, rule.getAction().getOperation()); assertEquals("Rule has unexpected operation", ObjectType.QUEUE, rule.getAction().getObjectType()); final ObjectProperties expectedProperties = new ObjectProperties(); expectedProperties.setName("name1"); expectedProperties.put(Property.OWNER, "owner1"); assertEquals("Rule has unexpected operation", expectedProperties, rule.getAction().getProperties()); } /** * Tests interpretation of an acl rule with object properties containing wildcards. Values containing * hashes must be quoted otherwise they are interpreted as comments. */ public void testValidRuleWithWildcardProperties() throws Exception { final PlainConfiguration config = writeACLConfig("ACL ALLOW all CREATE EXCHANGE routingKey = \'news.#\'", "ACL ALLOW all CREATE EXCHANGE routingKey = \'news.co.#\'", "ACL ALLOW all CREATE EXCHANGE routingKey = *.co.medellin"); final RuleSet rs = config.getConfiguration(); assertEquals(3, rs.getRuleCount()); final Map rules = rs.getAllRules(); assertEquals(3, rules.size()); final Rule rule1 = rules.get(0); assertEquals("Rule has unexpected identity", "all", rule1.getIdentity()); assertEquals("Rule has unexpected operation", Operation.CREATE, rule1.getAction().getOperation()); assertEquals("Rule has unexpected operation", ObjectType.EXCHANGE, rule1.getAction().getObjectType()); final ObjectProperties expectedProperties1 = new ObjectProperties(); expectedProperties1.put(Property.ROUTING_KEY,"news.#"); assertEquals("Rule has unexpected object properties", expectedProperties1, rule1.getAction().getProperties()); final Rule rule2 = rules.get(10); final ObjectProperties expectedProperties2 = new ObjectProperties(); expectedProperties2.put(Property.ROUTING_KEY,"news.co.#"); assertEquals("Rule has unexpected object properties", expectedProperties2, rule2.getAction().getProperties()); final Rule rule3 = rules.get(20); final ObjectProperties expectedProperties3 = new ObjectProperties(); expectedProperties3.put(Property.ROUTING_KEY,"*.co.medellin"); assertEquals("Rule has unexpected object properties", expectedProperties3, rule3.getAction().getProperties()); } /** * Tests that rules are case insignificant. */ public void testMixedCaseRuleInterpretation() throws Exception { final PlainConfiguration config = writeACLConfig("AcL deny-LOG User1 BiND Exchange Name=AmQ.dIrect"); final RuleSet rs = config.getConfiguration(); assertEquals(1, rs.getRuleCount()); final Map rules = rs.getAllRules(); assertEquals(1, rules.size()); final Rule rule = rules.get(0); assertEquals("Rule has unexpected identity", "User1", rule.getIdentity()); assertEquals("Rule has unexpected operation", Operation.BIND, rule.getAction().getOperation()); assertEquals("Rule has unexpected operation", ObjectType.EXCHANGE, rule.getAction().getObjectType()); final ObjectProperties expectedProperties = new ObjectProperties("AmQ.dIrect"); assertEquals("Rule has unexpected object properties", expectedProperties, rule.getAction().getProperties()); } /** * Tests whitespace is supported. Note that currently the Java implementation permits comments to * be introduced anywhere in the ACL, whereas the C++ supports only whitespace at the beginning of * of line. */ public void testCommentsSuppported() throws Exception { final PlainConfiguration config = writeACLConfig("#Comment", "ACL DENY-LOG user1 ACCESS VIRTUALHOST # another comment", " # final comment with leading whitespace"); final RuleSet rs = config.getConfiguration(); assertEquals(1, rs.getRuleCount()); final Map rules = rs.getAllRules(); assertEquals(1, rules.size()); final Rule rule = rules.get(0); assertEquals("Rule has unexpected identity", "user1", rule.getIdentity()); assertEquals("Rule has unexpected operation", Operation.ACCESS, rule.getAction().getOperation()); assertEquals("Rule has unexpected operation", ObjectType.VIRTUALHOST, rule.getAction().getObjectType()); assertEquals("Rule has unexpected object properties", ObjectProperties.EMPTY, rule.getAction().getProperties()); } /** * Tests interpretation of an acl rule using mixtures of tabs/spaces as token separators. * */ public void testWhitespace() throws Exception { final PlainConfiguration config = writeACLConfig("ACL\tDENY-LOG\t\t user1\t \tACCESS VIRTUALHOST"); final RuleSet rs = config.getConfiguration(); assertEquals(1, rs.getRuleCount()); final Map rules = rs.getAllRules(); assertEquals(1, rules.size()); final Rule rule = rules.get(0); assertEquals("Rule has unexpected identity", "user1", rule.getIdentity()); assertEquals("Rule has unexpected operation", Operation.ACCESS, rule.getAction().getOperation()); assertEquals("Rule has unexpected operation", ObjectType.VIRTUALHOST, rule.getAction().getObjectType()); assertEquals("Rule has unexpected object properties", ObjectProperties.EMPTY, rule.getAction().getProperties()); } /** * Tests interpretation of an acl utilising line continuation. */ public void testLineContination() throws Exception { final PlainConfiguration config = writeACLConfig("ACL DENY-LOG user1 \\", "ACCESS VIRTUALHOST"); final RuleSet rs = config.getConfiguration(); assertEquals(1, rs.getRuleCount()); final Map rules = rs.getAllRules(); assertEquals(1, rules.size()); final Rule rule = rules.get(0); assertEquals("Rule has unexpected identity", "user1", rule.getIdentity()); assertEquals("Rule has unexpected operation", Operation.ACCESS, rule.getAction().getOperation()); assertEquals("Rule has unexpected operation", ObjectType.VIRTUALHOST, rule.getAction().getObjectType()); assertEquals("Rule has unexpected object properties", ObjectProperties.EMPTY, rule.getAction().getProperties()); } }