org.apache.qpid.server.security.access.config.PlainConfiguration.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.qpid.server.security.access.config.PlainConfiguration.java

Source

/*
 *
 * 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.lang.StringUtils;
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 ACL = "acl";
    public static final String CONFIG = "config";

    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() {
        RuleSet ruleSet = super.load();

        File file = getFile();
        FileReader fileReader = null;

        try {
            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
            _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 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);
                        first = stack.firstElement();
                        stack.removeElementAt(0);
                    }

                    if (StringUtils.equalsIgnoreCase(ACL, first)) {
                        parseAcl(number, stack);
                    } else if (number == null) {
                        if (StringUtils.equalsIgnoreCase("GROUP", first)) {
                            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)) {
                            parseConfig(stack);
                        } else {
                            throw new IllegalConfigurationException(
                                    String.format(UNRECOGNISED_INITIAL_MSG, first, getLine()));
                        }
                    } else {
                        throw new IllegalConfigurationException(
                                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 IllegalConfigurationException(
                                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 IllegalConfigurationException(String.format(PREMATURE_EOF_MSG, getLine()));
            }
        } catch (IllegalArgumentException iae) {
            throw new IllegalConfigurationException(String.format(PARSE_TOKEN_FAILED_MSG, getLine()), iae);
        } catch (FileNotFoundException fnfe) {
            throw new IllegalConfigurationException(String.format(CONFIG_NOT_FOUND_MSG, file.getName()), fnfe);
        } catch (IOException ioe) {
            throw new IllegalConfigurationException(String.format(CANNOT_LOAD_MSG, file.getName()), ioe);
        } finally {
            if (fileReader != null) {
                try {
                    fileReader.close();
                } catch (IOException e) {
                    throw new IllegalConfigurationException(String.format(CANNOT_CLOSE_MSG, file.getName()), e);
                }
            }
        }

        return ruleSet;
    }

    private void parseAcl(Integer number, List<String> args) {
        if (args.size() < 3) {
            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 IllegalConfigurationException(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));
            AclRulePredicates predicates = toRulePredicates(args.subList(4, args.size()));

            getConfiguration().grant(number, identity, permission, operation, object, predicates);
        }
    }

    private void parseConfig(List<String> args) {
        if (args.size() < 3) {
            throw new IllegalConfigurationException(String.format(NOT_ENOUGH_CONFIG_MSG, getLine()));
        }

        Map<String, Boolean> properties = toPluginProperties(args);

        getConfiguration().configure(properties);
    }

    private AclRulePredicates toRulePredicates(List<String> args) {
        AclRulePredicates predicates = new AclRulePredicates();
        Iterator<String> i = args.iterator();
        while (i.hasNext()) {
            String key = i.next();
            if (!i.hasNext()) {
                throw new IllegalConfigurationException(String.format(PROPERTY_KEY_ONLY_MSG, getLine()));
            }
            if (!"=".equals(i.next())) {
                throw new IllegalConfigurationException(String.format(PROPERTY_NO_EQUALS_MSG, getLine()));
            }
            if (!i.hasNext()) {
                throw new IllegalConfigurationException(String.format(PROPERTY_NO_VALUE_MSG, getLine()));
            }
            String value = i.next();

            predicates.parse(key, value);
        }
        return predicates;
    }

    /** Converts a {@link List} of "name", "=", "value" tokens into a {@link Map}. */
    protected Map<String, Boolean> toPluginProperties(List<String> args) {
        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 IllegalConfigurationException(String.format(PROPERTY_KEY_ONLY_MSG, getLine()));
            }
            if (!"=".equals(i.next())) {
                throw new IllegalConfigurationException(String.format(PROPERTY_NO_EQUALS_MSG, getLine()));
            }
            if (!i.hasNext()) {
                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;
    }
}