]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Switch to oslo.policy 0.3.0
authorIvan Kolodyazhny <e0ne@e0ne.info>
Tue, 9 Jun 2015 11:17:30 +0000 (14:17 +0300)
committerIvan Kolodyazhny <e0ne@e0ne.info>
Tue, 16 Jun 2015 19:03:09 +0000 (22:03 +0300)
Use graduated oslo.policy instead of sync with oslo-incubator code.

Co-Authored-By: Ivan Kolodyazhny <e0ne@e0ne.info>
Change-Id: Ibb295830ac6abde9317c2549f75c1780ac454125
Closes-Bug: #1421863
Partial-Bug: #1458945

cinder/openstack/common/policy.py [deleted file]
cinder/policy.py
cinder/test.py
cinder/tests/unit/conf_fixture.py
openstack-common.conf
requirements.txt
tools/config/oslo.config.generator.rc

diff --git a/cinder/openstack/common/policy.py b/cinder/openstack/common/policy.py
deleted file mode 100644 (file)
index 47517fa..0000000
+++ /dev/null
@@ -1,963 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2012 OpenStack Foundation.
-# All Rights Reserved.
-#
-#    Licensed 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.
-
-"""
-Common Policy Engine Implementation
-
-Policies can be expressed in one of two forms: A list of lists, or a
-string written in the new policy language.
-
-In the list-of-lists representation, each check inside the innermost
-list is combined as with an "and" conjunction--for that check to pass,
-all the specified checks must pass.  These innermost lists are then
-combined as with an "or" conjunction. As an example, take the following
-rule, expressed in the list-of-lists representation::
-
-    [["role:admin"], ["project_id:%(project_id)s", "role:projectadmin"]]
-
-This is the original way of expressing policies, but there now exists a
-new way: the policy language.
-
-In the policy language, each check is specified the same way as in the
-list-of-lists representation: a simple "a:b" pair that is matched to
-the correct class to perform that check::
-
- +===========================================================================+
- |            TYPE                |                SYNTAX                    |
- +===========================================================================+
- |User's Role                     |              role:admin                  |
- +---------------------------------------------------------------------------+
- |Rules already defined on policy |          rule:admin_required             |
- +---------------------------------------------------------------------------+
- |Against URL's¹                  |         http://my-url.org/check          |
- +---------------------------------------------------------------------------+
- |User attributes²                |    project_id:%(target.project.id)s      |
- +---------------------------------------------------------------------------+
- |Strings                         |        <variable>:'xpto2035abc'          |
- |                                |         'myproject':<variable>           |
- +---------------------------------------------------------------------------+
- |                                |         project_id:xpto2035abc           |
- |Literals                        |         domain_id:20                     |
- |                                |         True:%(user.enabled)s            |
- +===========================================================================+
-
-¹URL checking must return 'True' to be valid
-²User attributes (obtained through the token): user_id, domain_id or project_id
-
-Conjunction operators are available, allowing for more expressiveness
-in crafting policies. So, in the policy language, the previous check in
-list-of-lists becomes::
-
-    role:admin or (project_id:%(project_id)s and role:projectadmin)
-
-The policy language also has the "not" operator, allowing a richer
-policy rule::
-
-    project_id:%(project_id)s and not role:dunce
-
-Attributes sent along with API calls can be used by the policy engine
-(on the right side of the expression), by using the following syntax::
-
-    <some_value>:%(user.id)s
-
-Contextual attributes of objects identified by their IDs are loaded
-from the database. They are also available to the policy engine and
-can be checked through the `target` keyword::
-
-    <some_value>:%(target.role.name)s
-
-Finally, two special policy checks should be mentioned; the policy
-check "@" will always accept an access, and the policy check "!" will
-always reject an access.  (Note that if a rule is either the empty
-list ("[]") or the empty string, this is equivalent to the "@" policy
-check.)  Of these, the "!" policy check is probably the most useful,
-as it allows particular rules to be explicitly disabled.
-"""
-
-import abc
-import ast
-import copy
-import logging
-import os
-import re
-
-from oslo_config import cfg
-from oslo_serialization import jsonutils
-import six
-from six.moves import urllib
-import six.moves.urllib.request as urlrequest
-
-from cinder.openstack.common import fileutils
-from cinder.openstack.common._i18n import _, _LE
-
-
-policy_opts = [
-    cfg.StrOpt('policy_file',
-               default='policy.json',
-               help=_('The JSON file that defines policies.')),
-    cfg.StrOpt('policy_default_rule',
-               default='default',
-               help=_('Default rule. Enforced when a requested rule is not '
-                      'found.')),
-    cfg.MultiStrOpt('policy_dirs',
-                    default=['policy.d'],
-                    help=_('Directories where policy configuration files are '
-                           'stored. They can be relative to any directory '
-                           'in the search path defined by the config_dir '
-                           'option, or absolute paths. The file defined by '
-                           'policy_file must exist for these directories to '
-                           'be searched.  Missing or empty directories are '
-                           'ignored.')),
-]
-
-CONF = cfg.CONF
-CONF.register_opts(policy_opts)
-
-LOG = logging.getLogger(__name__)
-
-_checks = {}
-
-
-def list_opts():
-    """Entry point for oslo-config-generator."""
-    return [(None, copy.deepcopy(policy_opts))]
-
-
-class PolicyNotAuthorized(Exception):
-
-    def __init__(self, rule):
-        msg = _("Policy doesn't allow %s to be performed.") % rule
-        super(PolicyNotAuthorized, self).__init__(msg)
-
-
-class Rules(dict):
-    """A store for rules. Handles the default_rule setting directly."""
-
-    @classmethod
-    def load_json(cls, data, default_rule=None):
-        """Allow loading of JSON rule data."""
-
-        # Suck in the JSON data and parse the rules
-        rules = dict((k, parse_rule(v)) for k, v in
-                     jsonutils.loads(data).items())
-
-        return cls(rules, default_rule)
-
-    def __init__(self, rules=None, default_rule=None):
-        """Initialize the Rules store."""
-
-        super(Rules, self).__init__(rules or {})
-        self.default_rule = default_rule
-
-    def __missing__(self, key):
-        """Implements the default rule handling."""
-
-        if isinstance(self.default_rule, dict):
-            raise KeyError(key)
-
-        # If the default rule isn't actually defined, do something
-        # reasonably intelligent
-        if not self.default_rule:
-            raise KeyError(key)
-
-        if isinstance(self.default_rule, BaseCheck):
-            return self.default_rule
-
-        # We need to check this or we can get infinite recursion
-        if self.default_rule not in self:
-            raise KeyError(key)
-
-        elif isinstance(self.default_rule, six.string_types):
-            return self[self.default_rule]
-
-    def __str__(self):
-        """Dumps a string representation of the rules."""
-
-        # Start by building the canonical strings for the rules
-        out_rules = {}
-        for key, value in self.items():
-            # Use empty string for singleton TrueCheck instances
-            if isinstance(value, TrueCheck):
-                out_rules[key] = ''
-            else:
-                out_rules[key] = str(value)
-
-        # Dump a pretty-printed JSON representation
-        return jsonutils.dumps(out_rules, indent=4)
-
-
-class Enforcer(object):
-    """Responsible for loading and enforcing rules.
-
-    :param policy_file: Custom policy file to use, if none is
-                        specified, `CONF.policy_file` will be
-                        used.
-    :param rules: Default dictionary / Rules to use. It will be
-                  considered just in the first instantiation. If
-                  `load_rules(True)`, `clear()` or `set_rules(True)`
-                  is called this will be overwritten.
-    :param default_rule: Default rule to use, CONF.default_rule will
-                         be used if none is specified.
-    :param use_conf: Whether to load rules from cache or config file.
-    :param overwrite: Whether to overwrite existing rules when reload rules
-                      from config file.
-    """
-
-    def __init__(self, policy_file=None, rules=None,
-                 default_rule=None, use_conf=True, overwrite=True):
-        self.default_rule = default_rule or CONF.policy_default_rule
-        self.rules = Rules(rules, self.default_rule)
-
-        self.policy_path = None
-        self.policy_file = policy_file or CONF.policy_file
-        self.use_conf = use_conf
-        self.overwrite = overwrite
-
-    def set_rules(self, rules, overwrite=True, use_conf=False):
-        """Create a new Rules object based on the provided dict of rules.
-
-        :param rules: New rules to use. It should be an instance of dict.
-        :param overwrite: Whether to overwrite current rules or update them
-                          with the new rules.
-        :param use_conf: Whether to reload rules from cache or config file.
-        """
-
-        if not isinstance(rules, dict):
-            raise TypeError(_("Rules must be an instance of dict or Rules, "
-                            "got %s instead") % type(rules))
-        self.use_conf = use_conf
-        if overwrite:
-            self.rules = Rules(rules, self.default_rule)
-        else:
-            self.rules.update(rules)
-
-    def clear(self):
-        """Clears Enforcer rules, policy's cache and policy's path."""
-        self.set_rules({})
-        fileutils.delete_cached_file(self.policy_path)
-        self.default_rule = None
-        self.policy_path = None
-
-    def load_rules(self, force_reload=False):
-        """Loads policy_path's rules.
-
-        Policy file is cached and will be reloaded if modified.
-
-        :param force_reload: Whether to reload rules from config file.
-        """
-
-        if force_reload:
-            self.use_conf = force_reload
-
-        if self.use_conf:
-            if not self.policy_path:
-                self.policy_path = self._get_policy_path(self.policy_file)
-
-            self._load_policy_file(self.policy_path, force_reload,
-                                   overwrite=self.overwrite)
-            for path in CONF.policy_dirs:
-                try:
-                    path = self._get_policy_path(path)
-                except cfg.ConfigFilesNotFoundError:
-                    continue
-                self._walk_through_policy_directory(path,
-                                                    self._load_policy_file,
-                                                    force_reload, False)
-
-    @staticmethod
-    def _walk_through_policy_directory(path, func, *args):
-        # We do not iterate over sub-directories.
-        policy_files = next(os.walk(path))[2]
-        policy_files.sort()
-        for policy_file in [p for p in policy_files if not p.startswith('.')]:
-            func(os.path.join(path, policy_file), *args)
-
-    def _load_policy_file(self, path, force_reload, overwrite=True):
-            reloaded, data = fileutils.read_cached_file(
-                path, force_reload=force_reload)
-            if reloaded or not self.rules or not overwrite:
-                rules = Rules.load_json(data, self.default_rule)
-                self.set_rules(rules, overwrite=overwrite, use_conf=True)
-                LOG.debug("Reloaded policy file: %(path)s",
-                          {'path': path})
-
-    def _get_policy_path(self, path):
-        """Locate the policy json data file/path.
-
-        :param path: It's value can be a full path or related path. When
-                     full path specified, this function just returns the full
-                     path. When related path specified, this function will
-                     search configuration directories to find one that exists.
-
-        :returns: The policy path
-
-        :raises: ConfigFilesNotFoundError if the file/path couldn't
-                 be located.
-        """
-        policy_path = CONF.find_file(path)
-
-        if policy_path:
-            return policy_path
-
-        raise cfg.ConfigFilesNotFoundError((path,))
-
-    def enforce(self, rule, target, creds, do_raise=False,
-                exc=None, *args, **kwargs):
-        """Checks authorization of a rule against the target and credentials.
-
-        :param rule: A string or BaseCheck instance specifying the rule
-                    to evaluate.
-        :param target: As much information about the object being operated
-                    on as possible, as a dictionary.
-        :param creds: As much information about the user performing the
-                    action as possible, as a dictionary.
-        :param do_raise: Whether to raise an exception or not if check
-                        fails.
-        :param exc: Class of the exception to raise if the check fails.
-                    Any remaining arguments passed to enforce() (both
-                    positional and keyword arguments) will be passed to
-                    the exception class. If not specified, PolicyNotAuthorized
-                    will be used.
-
-        :return: Returns False if the policy does not allow the action and
-                exc is not provided; otherwise, returns a value that
-                evaluates to True.  Note: for rules using the "case"
-                expression, this True value will be the specified string
-                from the expression.
-        """
-
-        self.load_rules()
-
-        # Allow the rule to be a Check tree
-        if isinstance(rule, BaseCheck):
-            result = rule(target, creds, self)
-        elif not self.rules:
-            # No rules to reference means we're going to fail closed
-            result = False
-        else:
-            try:
-                # Evaluate the rule
-                result = self.rules[rule](target, creds, self)
-            except KeyError:
-                LOG.debug("Rule [%s] doesn't exist" % rule)
-                # If the rule doesn't exist, fail closed
-                result = False
-
-        # If it is False, raise the exception if requested
-        if do_raise and not result:
-            if exc:
-                raise exc(*args, **kwargs)
-
-            raise PolicyNotAuthorized(rule)
-
-        return result
-
-
-@six.add_metaclass(abc.ABCMeta)
-class BaseCheck(object):
-    """Abstract base class for Check classes."""
-
-    @abc.abstractmethod
-    def __str__(self):
-        """String representation of the Check tree rooted at this node."""
-
-        pass
-
-    @abc.abstractmethod
-    def __call__(self, target, cred, enforcer):
-        """Triggers if instance of the class is called.
-
-        Performs the check. Returns False to reject the access or a
-        true value (not necessary True) to accept the access.
-        """
-
-        pass
-
-
-class FalseCheck(BaseCheck):
-    """A policy check that always returns False (disallow)."""
-
-    def __str__(self):
-        """Return a string representation of this check."""
-
-        return "!"
-
-    def __call__(self, target, cred, enforcer):
-        """Check the policy."""
-
-        return False
-
-
-class TrueCheck(BaseCheck):
-    """A policy check that always returns True (allow)."""
-
-    def __str__(self):
-        """Return a string representation of this check."""
-
-        return "@"
-
-    def __call__(self, target, cred, enforcer):
-        """Check the policy."""
-
-        return True
-
-
-class Check(BaseCheck):
-    """A base class to allow for user-defined policy checks."""
-
-    def __init__(self, kind, match):
-        """Initiates Check instance.
-
-        :param kind: The kind of the check, i.e., the field before the
-                     ':'.
-        :param match: The match of the check, i.e., the field after
-                      the ':'.
-        """
-
-        self.kind = kind
-        self.match = match
-
-    def __str__(self):
-        """Return a string representation of this check."""
-
-        return "%s:%s" % (self.kind, self.match)
-
-
-class NotCheck(BaseCheck):
-    """Implements the "not" logical operator.
-
-    A policy check that inverts the result of another policy check.
-    """
-
-    def __init__(self, rule):
-        """Initialize the 'not' check.
-
-        :param rule: The rule to negate.  Must be a Check.
-        """
-
-        self.rule = rule
-
-    def __str__(self):
-        """Return a string representation of this check."""
-
-        return "not %s" % self.rule
-
-    def __call__(self, target, cred, enforcer):
-        """Check the policy.
-
-        Returns the logical inverse of the wrapped check.
-        """
-
-        return not self.rule(target, cred, enforcer)
-
-
-class AndCheck(BaseCheck):
-    """Implements the "and" logical operator.
-
-    A policy check that requires that a list of other checks all return True.
-    """
-
-    def __init__(self, rules):
-        """Initialize the 'and' check.
-
-        :param rules: A list of rules that will be tested.
-        """
-
-        self.rules = rules
-
-    def __str__(self):
-        """Return a string representation of this check."""
-
-        return "(%s)" % ' and '.join(str(r) for r in self.rules)
-
-    def __call__(self, target, cred, enforcer):
-        """Check the policy.
-
-        Requires that all rules accept in order to return True.
-        """
-
-        for rule in self.rules:
-            if not rule(target, cred, enforcer):
-                return False
-
-        return True
-
-    def add_check(self, rule):
-        """Adds rule to be tested.
-
-        Allows addition of another rule to the list of rules that will
-        be tested.  Returns the AndCheck object for convenience.
-        """
-
-        self.rules.append(rule)
-        return self
-
-
-class OrCheck(BaseCheck):
-    """Implements the "or" operator.
-
-    A policy check that requires that at least one of a list of other
-    checks returns True.
-    """
-
-    def __init__(self, rules):
-        """Initialize the 'or' check.
-
-        :param rules: A list of rules that will be tested.
-        """
-
-        self.rules = rules
-
-    def __str__(self):
-        """Return a string representation of this check."""
-
-        return "(%s)" % ' or '.join(str(r) for r in self.rules)
-
-    def __call__(self, target, cred, enforcer):
-        """Check the policy.
-
-        Requires that at least one rule accept in order to return True.
-        """
-
-        for rule in self.rules:
-            if rule(target, cred, enforcer):
-                return True
-        return False
-
-    def add_check(self, rule):
-        """Adds rule to be tested.
-
-        Allows addition of another rule to the list of rules that will
-        be tested.  Returns the OrCheck object for convenience.
-        """
-
-        self.rules.append(rule)
-        return self
-
-
-def _parse_check(rule):
-    """Parse a single base check rule into an appropriate Check object."""
-
-    # Handle the special checks
-    if rule == '!':
-        return FalseCheck()
-    elif rule == '@':
-        return TrueCheck()
-
-    try:
-        kind, match = rule.split(':', 1)
-    except Exception:
-        LOG.exception(_LE("Failed to understand rule %s") % rule)
-        # If the rule is invalid, we'll fail closed
-        return FalseCheck()
-
-    # Find what implements the check
-    if kind in _checks:
-        return _checks[kind](kind, match)
-    elif None in _checks:
-        return _checks[None](kind, match)
-    else:
-        LOG.error(_LE("No handler for matches of kind %s") % kind)
-        return FalseCheck()
-
-
-def _parse_list_rule(rule):
-    """Translates the old list-of-lists syntax into a tree of Check objects.
-
-    Provided for backwards compatibility.
-    """
-
-    # Empty rule defaults to True
-    if not rule:
-        return TrueCheck()
-
-    # Outer list is joined by "or"; inner list by "and"
-    or_list = []
-    for inner_rule in rule:
-        # Elide empty inner lists
-        if not inner_rule:
-            continue
-
-        # Handle bare strings
-        if isinstance(inner_rule, six.string_types):
-            inner_rule = [inner_rule]
-
-        # Parse the inner rules into Check objects
-        and_list = [_parse_check(r) for r in inner_rule]
-
-        # Append the appropriate check to the or_list
-        if len(and_list) == 1:
-            or_list.append(and_list[0])
-        else:
-            or_list.append(AndCheck(and_list))
-
-    # If we have only one check, omit the "or"
-    if not or_list:
-        return FalseCheck()
-    elif len(or_list) == 1:
-        return or_list[0]
-
-    return OrCheck(or_list)
-
-
-# Used for tokenizing the policy language
-_tokenize_re = re.compile(r'\s+')
-
-
-def _parse_tokenize(rule):
-    """Tokenizer for the policy language.
-
-    Most of the single-character tokens are specified in the
-    _tokenize_re; however, parentheses need to be handled specially,
-    because they can appear inside a check string.  Thankfully, those
-    parentheses that appear inside a check string can never occur at
-    the very beginning or end ("%(variable)s" is the correct syntax).
-    """
-
-    for tok in _tokenize_re.split(rule):
-        # Skip empty tokens
-        if not tok or tok.isspace():
-            continue
-
-        # Handle leading parens on the token
-        clean = tok.lstrip('(')
-        for i in range(len(tok) - len(clean)):
-            yield '(', '('
-
-        # If it was only parentheses, continue
-        if not clean:
-            continue
-        else:
-            tok = clean
-
-        # Handle trailing parens on the token
-        clean = tok.rstrip(')')
-        trail = len(tok) - len(clean)
-
-        # Yield the cleaned token
-        lowered = clean.lower()
-        if lowered in ('and', 'or', 'not'):
-            # Special tokens
-            yield lowered, clean
-        elif clean:
-            # Not a special token, but not composed solely of ')'
-            if len(tok) >= 2 and ((tok[0], tok[-1]) in
-                                  [('"', '"'), ("'", "'")]):
-                # It's a quoted string
-                yield 'string', tok[1:-1]
-            else:
-                yield 'check', _parse_check(clean)
-
-        # Yield the trailing parens
-        for i in range(trail):
-            yield ')', ')'
-
-
-class ParseStateMeta(type):
-    """Metaclass for the ParseState class.
-
-    Facilitates identifying reduction methods.
-    """
-
-    def __new__(mcs, name, bases, cls_dict):
-        """Create the class.
-
-        Injects the 'reducers' list, a list of tuples matching token sequences
-        to the names of the corresponding reduction methods.
-        """
-
-        reducers = []
-
-        for key, value in cls_dict.items():
-            if not hasattr(value, 'reducers'):
-                continue
-            for reduction in value.reducers:
-                reducers.append((reduction, key))
-
-        cls_dict['reducers'] = reducers
-
-        return super(ParseStateMeta, mcs).__new__(mcs, name, bases, cls_dict)
-
-
-def reducer(*tokens):
-    """Decorator for reduction methods.
-
-    Arguments are a sequence of tokens, in order, which should trigger running
-    this reduction method.
-    """
-
-    def decorator(func):
-        # Make sure we have a list of reducer sequences
-        if not hasattr(func, 'reducers'):
-            func.reducers = []
-
-        # Add the tokens to the list of reducer sequences
-        func.reducers.append(list(tokens))
-
-        return func
-
-    return decorator
-
-
-@six.add_metaclass(ParseStateMeta)
-class ParseState(object):
-    """Implement the core of parsing the policy language.
-
-    Uses a greedy reduction algorithm to reduce a sequence of tokens into
-    a single terminal, the value of which will be the root of the Check tree.
-
-    Note: error reporting is rather lacking.  The best we can get with
-    this parser formulation is an overall "parse failed" error.
-    Fortunately, the policy language is simple enough that this
-    shouldn't be that big a problem.
-    """
-
-    def __init__(self):
-        """Initialize the ParseState."""
-
-        self.tokens = []
-        self.values = []
-
-    def reduce(self):
-        """Perform a greedy reduction of the token stream.
-
-        If a reducer method matches, it will be executed, then the
-        reduce() method will be called recursively to search for any more
-        possible reductions.
-        """
-
-        for reduction, methname in self.reducers:
-            if (len(self.tokens) >= len(reduction) and
-                    self.tokens[-len(reduction):] == reduction):
-                # Get the reduction method
-                meth = getattr(self, methname)
-
-                # Reduce the token stream
-                results = meth(*self.values[-len(reduction):])
-
-                # Update the tokens and values
-                self.tokens[-len(reduction):] = [r[0] for r in results]
-                self.values[-len(reduction):] = [r[1] for r in results]
-
-                # Check for any more reductions
-                return self.reduce()
-
-    def shift(self, tok, value):
-        """Adds one more token to the state.  Calls reduce()."""
-
-        self.tokens.append(tok)
-        self.values.append(value)
-
-        # Do a greedy reduce...
-        self.reduce()
-
-    @property
-    def result(self):
-        """Obtain the final result of the parse.
-
-        Raises ValueError if the parse failed to reduce to a single result.
-        """
-
-        if len(self.values) != 1:
-            raise ValueError("Could not parse rule")
-        return self.values[0]
-
-    @reducer('(', 'check', ')')
-    @reducer('(', 'and_expr', ')')
-    @reducer('(', 'or_expr', ')')
-    def _wrap_check(self, _p1, check, _p2):
-        """Turn parenthesized expressions into a 'check' token."""
-
-        return [('check', check)]
-
-    @reducer('check', 'and', 'check')
-    def _make_and_expr(self, check1, _and, check2):
-        """Create an 'and_expr'.
-
-        Join two checks by the 'and' operator.
-        """
-
-        return [('and_expr', AndCheck([check1, check2]))]
-
-    @reducer('and_expr', 'and', 'check')
-    def _extend_and_expr(self, and_expr, _and, check):
-        """Extend an 'and_expr' by adding one more check."""
-
-        return [('and_expr', and_expr.add_check(check))]
-
-    @reducer('check', 'or', 'check')
-    def _make_or_expr(self, check1, _or, check2):
-        """Create an 'or_expr'.
-
-        Join two checks by the 'or' operator.
-        """
-
-        return [('or_expr', OrCheck([check1, check2]))]
-
-    @reducer('or_expr', 'or', 'check')
-    def _extend_or_expr(self, or_expr, _or, check):
-        """Extend an 'or_expr' by adding one more check."""
-
-        return [('or_expr', or_expr.add_check(check))]
-
-    @reducer('not', 'check')
-    def _make_not_expr(self, _not, check):
-        """Invert the result of another check."""
-
-        return [('check', NotCheck(check))]
-
-
-def _parse_text_rule(rule):
-    """Parses policy to the tree.
-
-    Translates a policy written in the policy language into a tree of
-    Check objects.
-    """
-
-    # Empty rule means always accept
-    if not rule:
-        return TrueCheck()
-
-    # Parse the token stream
-    state = ParseState()
-    for tok, value in _parse_tokenize(rule):
-        state.shift(tok, value)
-
-    try:
-        return state.result
-    except ValueError:
-        # Couldn't parse the rule
-        LOG.exception(_LE("Failed to understand rule %s") % rule)
-
-        # Fail closed
-        return FalseCheck()
-
-
-def parse_rule(rule):
-    """Parses a policy rule into a tree of Check objects."""
-
-    # If the rule is a string, it's in the policy language
-    if isinstance(rule, six.string_types):
-        return _parse_text_rule(rule)
-    return _parse_list_rule(rule)
-
-
-def register(name, func=None):
-    """Register a function or Check class as a policy check.
-
-    :param name: Gives the name of the check type, e.g., 'rule',
-                 'role', etc.  If name is None, a default check type
-                 will be registered.
-    :param func: If given, provides the function or class to register.
-                 If not given, returns a function taking one argument
-                 to specify the function or class to register,
-                 allowing use as a decorator.
-    """
-
-    # Perform the actual decoration by registering the function or
-    # class.  Returns the function or class for compliance with the
-    # decorator interface.
-    def decorator(func):
-        _checks[name] = func
-        return func
-
-    # If the function or class is given, do the registration
-    if func:
-        return decorator(func)
-
-    return decorator
-
-
-@register("rule")
-class RuleCheck(Check):
-    def __call__(self, target, creds, enforcer):
-        """Recursively checks credentials based on the defined rules."""
-
-        try:
-            return enforcer.rules[self.match](target, creds, enforcer)
-        except KeyError:
-            # We don't have any matching rule; fail closed
-            return False
-
-
-@register("role")
-class RoleCheck(Check):
-    def __call__(self, target, creds, enforcer):
-        """Check that there is a matching role in the cred dict."""
-
-        return self.match.lower() in [x.lower() for x in creds['roles']]
-
-
-@register('http')
-class HttpCheck(Check):
-    def __call__(self, target, creds, enforcer):
-        """Check http: rules by calling to a remote server.
-
-        This example implementation simply verifies that the response
-        is exactly 'True'.
-        """
-
-        url = ('http:' + self.match) % target
-
-        # Convert instances of object() in target temporarily to
-        # empty dict to avoid circular reference detection
-        # errors in jsonutils.dumps().
-        temp_target = copy.deepcopy(target)
-        for key in target.keys():
-            element = target.get(key)
-            if type(element) is object:
-                temp_target[key] = {}
-
-        data = {'target': jsonutils.dumps(temp_target),
-                'credentials': jsonutils.dumps(creds)}
-        post_data = urllib.parse.urlencode(data)
-        f = urlrequest.urlopen(url, post_data)
-        return f.read() == "True"
-
-
-@register(None)
-class GenericCheck(Check):
-    def __call__(self, target, creds, enforcer):
-        """Check an individual match.
-
-        Matches look like:
-
-            tenant:%(tenant_id)s
-            role:compute:admin
-            True:%(user.enabled)s
-            'Member':%(role.name)s
-        """
-
-        try:
-            match = self.match % target
-        except KeyError:
-            # While doing GenericCheck if key not
-            # present in Target return false
-            return False
-
-        try:
-            # Try to interpret self.kind as a literal
-            leftval = ast.literal_eval(self.kind)
-        except ValueError:
-            try:
-                kind_parts = self.kind.split('.')
-                leftval = creds
-                for kind_part in kind_parts:
-                    leftval = leftval[kind_part]
-            except KeyError:
-                return False
-        return match == six.text_type(leftval)
index 586a1bce267af37afb4507d45a857bff60a50f44..02dc263f2b218447c887d3e654db932dd4b5cea0 100644 (file)
 
 
 from oslo_config import cfg
+from oslo_policy import opts as policy_opts
+from oslo_policy import policy
 
 from cinder import exception
-from cinder.openstack.common import policy
 
 CONF = cfg.CONF
+policy_opts.set_defaults(cfg.CONF, 'policy.json')
 
 _ENFORCER = None
 
@@ -29,7 +31,7 @@ _ENFORCER = None
 def init():
     global _ENFORCER
     if not _ENFORCER:
-        _ENFORCER = policy.Enforcer()
+        _ENFORCER = policy.Enforcer(CONF)
 
 
 def enforce_action(context, action):
index e78237ff047f909a99c2d27aa3e50f2adbe4fa2a..f5db6bdecd5845b473b46ace423d0e95a20e4ef5 100644 (file)
@@ -221,7 +221,8 @@ class TestCase(testtools.TestCase):
                                          '..',
                                      )
                                  ),
-                                 'cinder/tests/unit/policy.json'))
+                                 'cinder/tests/unit/policy.json'),
+                             group='oslo_policy')
 
     def _common_cleanup(self):
         """Runs after each test method to tear down test environment."""
index 2685b15f7f3305b5b64377c17abceff82b24df04..9f6f8152f7ccb4c42f6d6cde4028f15c2e9c8294 100644 (file)
@@ -25,7 +25,7 @@ CONF.import_opt('backup_enable_progress_timer',
                 'cinder.backup.drivers.nfs')
 CONF.import_opt('backup_swift_enable_progress_timer',
                 'cinder.backup.drivers.swift')
-CONF.import_opt('policy_file', 'cinder.policy')
+CONF.import_opt('policy_file', 'cinder.policy', group='oslo_policy')
 CONF.import_opt('volume_driver', 'cinder.volume.manager')
 CONF.import_opt('xiv_ds8k_proxy',
                 'cinder.volume.drivers.ibm.xiv_ds8k')
@@ -44,7 +44,8 @@ def set_defaults(conf):
     conf.set_default('rpc_backend', 'cinder.openstack.common.rpc.impl_fake')
     conf.set_default('connection', 'sqlite://', group='database')
     conf.set_default('sqlite_synchronous', False, group='database')
-    conf.set_default('policy_file', 'cinder.tests.unit/policy.json')
+    conf.set_default('policy_file', 'cinder.tests.unit/policy.json',
+                     group='oslo_policy')
     conf.set_default(
         'xiv_ds8k_proxy',
         'cinder.tests.unit.test_ibm_xiv_ds8k.XIVDS8KFakeProxyDriver')
@@ -54,7 +55,7 @@ def set_defaults(conf):
                      'cinder.scheduler.filter_scheduler.FilterScheduler')
     conf.set_default('state_path', os.path.abspath(
         os.path.join(os.path.dirname(__file__), '..', '..', '..')))
-    conf.set_default('policy_dirs', [])
+    conf.set_default('policy_dirs', [], group='oslo_policy')
     conf.set_default('auth_strategy', 'noauth')
     conf.set_default('backup_enable_progress_timer', False)
     conf.set_default('backup_swift_enable_progress_timer', False)
index 0714af116a69e97f2e0489c1a860a495c91301c6..13c9f93e98a8270b5b9feb7174bd9572b2a5ff67 100644 (file)
@@ -9,7 +9,6 @@ module=imageutils
 module=install_venv_common
 module=loopingcall
 module=periodic_task
-module=policy
 module=scheduler
 module=scheduler.filters
 module=scheduler.weights
index 7a64b54bbb749f132f46dbc3ddc6c673cda66a7d..15cef4831162ad056d0ca91ded1518c11785e97a 100644 (file)
@@ -19,6 +19,7 @@ oslo.db>=1.10.0  # Apache-2.0
 oslo.log>=1.2.0  # Apache-2.0
 oslo.messaging>=1.8.0  # Apache-2.0
 oslo.middleware>=1.2.0,!=2.0.0                  # Apache-2.0
+oslo.policy>=0.5.0  # Apache-2.0
 oslo.rootwrap>=2.0.0  # Apache-2.0
 oslo.serialization>=1.4.0               # Apache-2.0
 oslo.utils>=1.4.0                       # Apache-2.0
index f51783a3ac485a0800d990642875f190100ce5f9..d01f82efc722fe371a8102b1f4592b7db603073e 100644 (file)
@@ -1,2 +1,2 @@
 export CINDER_CONFIG_GENERATOR_EXTRA_MODULES="keystonemiddleware.auth_token"
-export CINDER_CONFIG_GENERATOR_EXTRA_LIBRARIES="oslo_concurrency oslo.messaging oslo_db oslo.db.concurrency"
+export CINDER_CONFIG_GENERATOR_EXTRA_LIBRARIES="oslo_concurrency oslo.messaging oslo_db oslo.db.concurrency oslo_policy"