summaryrefslogtreecommitdiff
path: root/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java')
-rw-r--r--qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java323
1 files changed, 323 insertions, 0 deletions
diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java
new file mode 100644
index 0000000000..9f2168a31c
--- /dev/null
+++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java
@@ -0,0 +1,323 @@
+/*
+ *
+ * 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.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.StreamTokenizer;
+import java.util.HashMap;
+import java.util.Iterator;
+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.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
+{
+ 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";
+
+ private StreamTokenizer _st;
+
+ public PlainConfiguration(File file)
+ {
+ super(file);
+ }
+
+ @Override
+ public RuleSet load() throws ConfigurationException
+ {
+ RuleSet ruleSet = super.load();
+
+ try
+ {
+ _st = new StreamTokenizer(new BufferedReader(new FileReader(_file)));
+ _st.resetSyntax(); // setup the tokenizer
+
+ _st.commentChar(COMMENT); // single line comments
+ _st.eolIsSignificant(true); // return EOL as a token
+ _st.lowerCaseMode(true); // case insensitive tokens
+ _st.ordinaryChar('='); // equals is a token
+ _st.ordinaryChar(CONTINUATION); // continuation character (when followed by EOL)
+ _st.quoteChar('"'); // double quote
+ _st.quoteChar('\''); // single quote
+ _st.whitespaceChars('\u0000', '\u0020'); // whitespace (to be ignored) TODO properly
+ _st.wordChars('a', 'z'); // unquoted token characters [a-z]
+ _st.wordChars('A', 'Z'); // [A-Z]
+ _st.wordChars('0', '9'); // [0-9]
+ _st.wordChars('_', '_'); // underscore
+ _st.wordChars('-', '-'); // dash
+ _st.wordChars('.', '.'); // dot
+ _st.wordChars('*', '*'); // star
+ _st.wordChars('@', '@'); // at
+ _st.wordChars(':', ':'); // colon
+
+ // parse the acl file lines
+ Stack<String> stack = new Stack<String>();
+ int current;
+ do {
+ current = _st.nextToken();
+ switch (current)
+ {
+ case StreamTokenizer.TT_EOF:
+ case StreamTokenizer.TT_EOL:
+ if (stack.isEmpty())
+ {
+ 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()));
+ }
+
+ // 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);
+ first = stack.firstElement();
+ stack.removeElementAt(0);
+ }
+
+ if (StringUtils.equalsIgnoreCase(ACL, first))
+ {
+ parseAcl(number, stack);
+ }
+ else if (number == null)
+ {
+ if (StringUtils.equalsIgnoreCase(GROUP, first))
+ {
+ parseGroup(stack);
+ }
+ else if (StringUtils.equalsIgnoreCase(CONFIG, first))
+ {
+ parseConfig(stack);
+ }
+ else
+ {
+ throw new ConfigurationException(String.format(UNRECOGNISED_INITIAL_MSG, first, getLine()));
+ }
+ }
+ else
+ {
+ throw new ConfigurationException(String.format(NUMBER_NOT_ALLOWED_MSG, first, getLine()));
+ }
+
+ // reset stack, start next line
+ stack.clear();
+ break;
+ case StreamTokenizer.TT_NUMBER:
+ stack.push(Integer.toString(Double.valueOf(_st.nval).intValue()));
+ break;
+ case StreamTokenizer.TT_WORD:
+ stack.push(_st.sval); // token
+ break;
+ default:
+ if (_st.ttype == CONTINUATION)
+ {
+ int next = _st.nextToken();
+ if (next == StreamTokenizer.TT_EOL)
+ {
+ 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));
+ }
+ else if (_st.ttype == '\'' || _st.ttype == '"')
+ {
+ stack.push(_st.sval); // quoted token
+ }
+ else
+ {
+ stack.push(Character.toString((char) _st.ttype)); // single character
+ }
+ }
+ } while (current != StreamTokenizer.TT_EOF);
+
+ if (!stack.isEmpty())
+ {
+ throw new ConfigurationException(String.format(PREMATURE_EOF_MSG, getLine()));
+ }
+ }
+ catch (IllegalArgumentException iae)
+ {
+ throw new ConfigurationException(String.format(PARSE_TOKEN_FAILED_MSG, getLine()), iae);
+ }
+ catch (FileNotFoundException fnfe)
+ {
+ throw new ConfigurationException(String.format(CONFIG_NOT_FOUND_MSG, getFile().getName()), fnfe);
+ }
+ catch (IOException ioe)
+ {
+ throw new ConfigurationException(String.format(CANNOT_LOAD_MSG, getFile().getName()), ioe);
+ }
+
+ return ruleSet;
+ }
+
+ private void parseGroup(List<String> args) throws ConfigurationException
+ {
+ if (args.size() < 2)
+ {
+ throw new ConfigurationException(String.format(NOT_ENOUGH_GROUP_MSG, getLine()));
+ }
+
+ getConfiguration().addGroup(args.get(0), args.subList(1, args.size()));
+ }
+
+ private void parseAcl(Integer number, List<String> args) throws ConfigurationException
+ {
+ if (args.size() < 3)
+ {
+ throw new ConfigurationException(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()));
+ }
+
+ if (args.size() == 3)
+ {
+ getConfiguration().grant(number, identity, permission, operation);
+ }
+ else
+ {
+ ObjectType object = ObjectType.parse(args.get(3));
+ ObjectProperties properties = toObjectProperties(args.subList(4, args.size()));
+
+ getConfiguration().grant(number, identity, permission, operation, object, properties);
+ }
+ }
+
+ private void parseConfig(List<String> args) throws ConfigurationException
+ {
+ if (args.size() < 3)
+ {
+ throw new ConfigurationException(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
+ {
+ ObjectProperties properties = new ObjectProperties();
+ 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()));
+ }
+ if (!"=".equals(i.next()))
+ {
+ throw new ConfigurationException(String.format(PROPERTY_NO_EQUALS_MSG, getLine()));
+ }
+ if (!i.hasNext())
+ {
+ throw new ConfigurationException(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);
+ }
+ return properties;
+ }
+
+ /** Converts a {@link List} of "name", "=", "value" tokens into a {@link Map}. */
+ protected Map<String, Boolean> toPluginProperties(List<String> args) throws ConfigurationException
+ {
+ Map<String, Boolean> properties = new HashMap<String, Boolean>();
+ Iterator<String> i = args.iterator();
+ while (i.hasNext())
+ {
+ String key = i.next().toLowerCase();
+ if (!i.hasNext())
+ {
+ throw new ConfigurationException(String.format(PROPERTY_KEY_ONLY_MSG, getLine()));
+ }
+ if (!"=".equals(i.next()))
+ {
+ throw new ConfigurationException(String.format(PROPERTY_NO_EQUALS_MSG, getLine()));
+ }
+ if (!i.hasNext())
+ {
+ throw new ConfigurationException(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;
+ }
+}