diff --git a/etc/nova/nova-config-generator.conf b/etc/nova/nova-config-generator.conf index 7c820ad483ee..fef4f51394ac 100644 --- a/etc/nova/nova-config-generator.conf +++ b/etc/nova/nova-config-generator.conf @@ -10,7 +10,6 @@ namespace = nova.network.neutronv2 namespace = nova.scheduler namespace = nova.virt namespace = nova.openstack.common.memorycache -namespace = nova.openstack.common.policy namespace = oslo.log namespace = oslo.messaging namespace = oslo.policy diff --git a/nova/openstack/common/policy.py b/nova/openstack/common/policy.py deleted file mode 100644 index 226754dc95b1..000000000000 --- a/nova/openstack/common/policy.py +++ /dev/null @@ -1,962 +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 | :'xpto2035abc' | - | | 'myproject': | - +---------------------------------------------------------------------------+ - | | 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:: - - :%(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:: - - :%(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 -import six.moves.urllib.parse as urlparse -import six.moves.urllib.request as urlrequest - -from nova.openstack.common._i18n import _, _LE -from nova import utils - -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({}) - utils.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 = utils.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 = urlparse.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) diff --git a/nova/policy.py b/nova/policy.py index 33b8364e719e..8cde01607f53 100644 --- a/nova/policy.py +++ b/nova/policy.py @@ -17,12 +17,14 @@ import logging +from oslo_config import cfg +from oslo_policy import policy from oslo_utils import excutils from nova import exception -from nova.openstack.common import policy +CONF = cfg.CONF LOG = logging.getLogger(__name__) _ENFORCER = None @@ -48,7 +50,8 @@ def init(policy_file=None, rules=None, default_rule=None, use_conf=True): global _ENFORCER if not _ENFORCER: - _ENFORCER = policy.Enforcer(policy_file=policy_file, + _ENFORCER = policy.Enforcer(CONF, + policy_file=policy_file, rules=rules, default_rule=default_rule, use_conf=use_conf) diff --git a/nova/tests/unit/api/ec2/test_cloud.py b/nova/tests/unit/api/ec2/test_cloud.py index 74d5c457c78a..a21cb1f37f62 100644 --- a/nova/tests/unit/api/ec2/test_cloud.py +++ b/nova/tests/unit/api/ec2/test_cloud.py @@ -29,6 +29,7 @@ import iso8601 import mock from oslo_config import cfg from oslo_log import log as logging +from oslo_policy import policy as oslo_policy from oslo_utils import timeutils from oslo_utils import uuidutils @@ -54,7 +55,6 @@ from nova.network import model from nova.network.neutronv2 import api as neutronapi from nova import objects from nova.objects import base as obj_base -from nova.openstack.common import policy as common_policy from nova import policy from nova import test from nova.tests.unit.api.openstack.compute import ( @@ -523,8 +523,8 @@ class CloudTestCase(test.TestCase): def test_delete_security_group_policy_not_allowed(self): rules = {'compute_extension:security_groups': - common_policy.parse_rule('project_id:%(project_id)s')} - policy.set_rules(rules) + 'project_id:%(project_id)s'} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) with mock.patch.object(self.cloud.security_group_api, 'get') as get: @@ -536,8 +536,8 @@ class CloudTestCase(test.TestCase): def test_authorize_security_group_ingress_policy_not_allowed(self): rules = {'compute_extension:security_groups': - common_policy.parse_rule('project_id:%(project_id)s')} - policy.set_rules(rules) + 'project_id:%(project_id)s'} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) with mock.patch.object(self.cloud.security_group_api, 'get') as get: @@ -669,8 +669,8 @@ class CloudTestCase(test.TestCase): def test_revoke_security_group_ingress_policy_not_allowed(self): rules = {'compute_extension:security_groups': - common_policy.parse_rule('project_id:%(project_id)s')} - policy.set_rules(rules) + 'project_id:%(project_id)s'} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) with mock.patch.object(self.cloud.security_group_api, 'get') as get: @@ -2354,10 +2354,9 @@ class CloudTestCase(test.TestCase): 'max_count': 1, } instance_id = self._run_instance(**kwargs) rules = { - "compute:start": - common_policy.parse_rule("project_id:non_fake"), + "compute:start": "project_id:non_fake", } - policy.set_rules(rules) + policy.set_rules(oslo_policy.Rules.from_dict(rules)) exc = self.assertRaises(exception.PolicyNotAuthorized, self.cloud.start_instances, self.context, [instance_id]) @@ -2395,10 +2394,9 @@ class CloudTestCase(test.TestCase): 'max_count': 1, } instance_id = self._run_instance(**kwargs) rules = { - "compute:stop": - common_policy.parse_rule("project_id:non_fake") + "compute:stop": "project_id:non_fake" } - policy.set_rules(rules) + policy.set_rules(oslo_policy.Rules.from_dict(rules)) exc = self.assertRaises(exception.PolicyNotAuthorized, self.cloud.stop_instances, self.context, [instance_id]) diff --git a/nova/tests/unit/api/openstack/compute/legacy_v2/test_servers.py b/nova/tests/unit/api/openstack/compute/legacy_v2/test_servers.py index d3b2b1104979..39e70d1bba4f 100644 --- a/nova/tests/unit/api/openstack/compute/legacy_v2/test_servers.py +++ b/nova/tests/unit/api/openstack/compute/legacy_v2/test_servers.py @@ -24,6 +24,7 @@ import uuid import iso8601 import mock from oslo_config import cfg +from oslo_policy import policy as oslo_policy from oslo_serialization import jsonutils from oslo_utils import timeutils from six.moves import range @@ -49,7 +50,6 @@ from nova.network import manager from nova.network.neutronv2 import api as neutron_api from nova import objects from nova.objects import instance as instance_obj -from nova.openstack.common import policy as common_policy from nova import policy from nova import test from nova.tests.unit.api.openstack import fakes @@ -796,13 +796,11 @@ class ServersControllerTest(ControllerTest): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) rules = { - "compute:get_all_tenants": - common_policy.parse_rule("project_id:fake"), - "compute:get_all": - common_policy.parse_rule("project_id:fake"), + "compute:get_all_tenants": "project_id:fake", + "compute:get_all": "project_id:fake", } - policy.set_rules(rules) + policy.set_rules(oslo_policy.Rules.from_dict(rules)) req = fakes.HTTPRequest.blank('/fake/servers?all_tenants=1') servers = self.controller.index(req)['servers'] @@ -814,13 +812,11 @@ class ServersControllerTest(ControllerTest): return [fakes.stub_instance(100)] rules = { - "compute:get_all_tenants": - common_policy.parse_rule("project_id:non_fake"), - "compute:get_all": - common_policy.parse_rule("project_id:fake"), + "compute:get_all_tenants": "project_id:non_fake", + "compute:get_all": "project_id:fake", } - policy.set_rules(rules) + policy.set_rules(oslo_policy.Rules.from_dict(rules)) self.stubs.Set(compute_api.API, 'get_all', fake_get_all) req = fakes.HTTPRequest.blank('/fake/servers?all_tenants=1') @@ -1480,8 +1476,8 @@ class ServersControllerUpdateTest(ControllerTest): req, FAKE_UUID, body) def test_update_server_policy_fail(self): - rule = {'compute:update': common_policy.parse_rule('role:admin')} - policy.set_rules(rule) + rule = {'compute:update': 'role:admin'} + policy.set_rules(oslo_policy.Rules.from_dict(rule)) body = {'server': {'name': 'server_test'}} req = self._get_request(body, {'name': 'server_test'}) self.assertRaises(exception.PolicyNotAuthorized, @@ -1790,9 +1786,8 @@ class ServerStatusTest(test.TestCase): return self.controller.show(request, FAKE_UUID) def _req_with_policy_fail(self, policy_rule_name): - rule = {'compute:%s' % policy_rule_name: - common_policy.parse_rule('role:admin')} - policy.set_rules(rule) + rule = {'compute:%s' % policy_rule_name: 'role:admin'} + policy.set_rules(oslo_policy.Rules.from_dict(rule)) return fakes.HTTPRequest.blank('/fake/servers/1234/action') def test_active(self): diff --git a/nova/tests/unit/api/openstack/compute/test_certificates.py b/nova/tests/unit/api/openstack/compute/test_certificates.py index 01abf4475a7a..ac30a8ec6660 100644 --- a/nova/tests/unit/api/openstack/compute/test_certificates.py +++ b/nova/tests/unit/api/openstack/compute/test_certificates.py @@ -18,6 +18,8 @@ import mock from mox3 import mox from webob import exc +from oslo_policy import policy as oslo_policy + from nova.api.openstack.compute import certificates \ as certificates_v21 from nova.api.openstack.compute.legacy_v2.contrib import certificates \ @@ -25,7 +27,6 @@ from nova.api.openstack.compute.legacy_v2.contrib import certificates \ from nova.cert import rpcapi from nova import context from nova import exception -from nova.openstack.common import policy as common_policy from nova import policy from nova import test from nova.tests.unit.api.openstack import fakes @@ -65,10 +66,9 @@ class CertificatesTestV21(test.NoDBTestCase): def test_certificates_show_policy_failed(self): rules = { - self.certificate_show_extension: - common_policy.parse_rule("!") + self.certificate_show_extension: "!" } - policy.set_rules(rules) + policy.set_rules(oslo_policy.Rules.from_dict(rules)) exc = self.assertRaises(exception.PolicyNotAuthorized, self.controller.show, self.req, 'root') self.assertIn(self.certificate_show_extension, @@ -95,10 +95,9 @@ class CertificatesTestV21(test.NoDBTestCase): def test_certificates_create_policy_failed(self): rules = { - self.certificate_create_extension: - common_policy.parse_rule("!") + self.certificate_create_extension: "!" } - policy.set_rules(rules) + policy.set_rules(oslo_policy.Rules.from_dict(rules)) exc = self.assertRaises(exception.PolicyNotAuthorized, self.controller.create, self.req) self.assertIn(self.certificate_create_extension, diff --git a/nova/tests/unit/api/openstack/compute/test_consoles.py b/nova/tests/unit/api/openstack/compute/test_consoles.py index bf6aa5766120..2efa34a25771 100644 --- a/nova/tests/unit/api/openstack/compute/test_consoles.py +++ b/nova/tests/unit/api/openstack/compute/test_consoles.py @@ -17,6 +17,7 @@ import datetime import uuid as stdlib_uuid +from oslo_policy import policy as oslo_policy from oslo_utils import timeutils import webob @@ -26,7 +27,6 @@ from nova.compute import vm_states from nova import console from nova import db from nova import exception -from nova.openstack.common import policy as common_policy from nova import policy from nova import test from nova.tests.unit.api.openstack import fakes @@ -266,10 +266,10 @@ class ConsolesControllerTestV21(test.NoDBTestCase): def _test_fail_policy(self, rule, action, data=None): rules = { - rule: common_policy.parse_rule("!"), + rule: "!", } - policy.set_rules(rules) + policy.set_rules(oslo_policy.Rules.from_dict(rules)) req = fakes.HTTPRequest.blank(self.url + '/20') if data is not None: diff --git a/nova/tests/unit/api/openstack/compute/test_instance_actions.py b/nova/tests/unit/api/openstack/compute/test_instance_actions.py index 98d4d21bafbd..d9ac0f9f4bed 100644 --- a/nova/tests/unit/api/openstack/compute/test_instance_actions.py +++ b/nova/tests/unit/api/openstack/compute/test_instance_actions.py @@ -16,6 +16,7 @@ import copy import uuid +from oslo_policy import policy as oslo_policy import six from webob import exc @@ -27,7 +28,6 @@ from nova import db from nova.db.sqlalchemy import models from nova import exception from nova import objects -from nova.openstack.common import policy as common_policy from nova import policy from nova import test from nova.tests.unit.api.openstack import fakes @@ -81,10 +81,10 @@ class InstanceActionsPolicyTestV21(test.NoDBTestCase): return fakes.HTTPRequest.blank(fake_url) def _set_policy_rules(self): - rules = {'compute:get': common_policy.parse_rule(''), + rules = {'compute:get': '', 'os_compute_api:os-instance-actions': - common_policy.parse_rule('project_id:%(project_id)s')} - policy.set_rules(rules) + 'project_id:%(project_id)s'} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) def test_list_actions_restricted_by_project(self): self._set_policy_rules() @@ -121,10 +121,10 @@ class InstanceActionsPolicyTestV2(InstanceActionsPolicyTestV21): instance_actions = instance_actions_v2 def _set_policy_rules(self): - rules = {'compute:get': common_policy.parse_rule(''), + rules = {'compute:get': '', 'compute_extension:instance_actions': - common_policy.parse_rule('project_id:%(project_id)s')} - policy.set_rules(rules) + 'project_id:%(project_id)s'} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) class InstanceActionsTestV21(test.NoDBTestCase): @@ -153,12 +153,10 @@ class InstanceActionsTestV21(test.NoDBTestCase): use_admin_context=use_admin_context) def _set_policy_rules(self): - rules = {'compute:get': common_policy.parse_rule(''), - 'os_compute_api:os-instance-actions': - common_policy.parse_rule(''), - 'os_compute_api:os-instance-actions:events': - common_policy.parse_rule('is_admin:True')} - policy.set_rules(rules) + rules = {'compute:get': '', + 'os_compute_api:os-instance-actions': '', + 'os_compute_api:os-instance-actions:events': 'is_admin:True'} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) def test_list_actions(self): def fake_get_actions(context, uuid): @@ -250,9 +248,7 @@ class InstanceActionsTestV2(InstanceActionsTestV21): instance_actions = instance_actions_v2 def _set_policy_rules(self): - rules = {'compute:get': common_policy.parse_rule(''), - 'compute_extension:instance_actions': - common_policy.parse_rule(''), - 'compute_extension:instance_actions:events': - common_policy.parse_rule('is_admin:True')} - policy.set_rules(rules) + rules = {'compute:get': '', + 'compute_extension:instance_actions': '', + 'compute_extension:instance_actions:events': 'is_admin:True'} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) diff --git a/nova/tests/unit/api/openstack/compute/test_keypairs.py b/nova/tests/unit/api/openstack/compute/test_keypairs.py index a5886bed637c..ea438b0ec70f 100644 --- a/nova/tests/unit/api/openstack/compute/test_keypairs.py +++ b/nova/tests/unit/api/openstack/compute/test_keypairs.py @@ -17,6 +17,8 @@ import mock from oslo_serialization import jsonutils import webob +from oslo_policy import policy as oslo_policy + from nova.api.openstack.compute import keypairs as keypairs_v21 from nova.api.openstack.compute.legacy_v2.contrib import keypairs \ as keypairs_v2 @@ -25,7 +27,6 @@ from nova.compute import api as compute_api from nova import db from nova import exception from nova import objects -from nova.openstack.common import policy as common_policy from nova import policy from nova import quota from nova import test @@ -369,64 +370,56 @@ class KeypairPolicyTestV21(test.TestCase): self.req = fakes.HTTPRequest.blank('') def test_keypair_list_fail_policy(self): - rules = {self.policy_path + ':index': - common_policy.parse_rule('role:admin')} - policy.set_rules(rules) + rules = {self.policy_path + ':index': 'role:admin'} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) self.assertRaises(exception.Forbidden, self.KeyPairController.index, self.req) def test_keypair_list_pass_policy(self): - rules = {self.policy_path + ':index': - common_policy.parse_rule('')} - policy.set_rules(rules) + rules = {self.policy_path + ':index': ''} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) res = self.KeyPairController.index(self.req) self.assertIn('keypairs', res) def test_keypair_show_fail_policy(self): - rules = {self.policy_path + ':show': - common_policy.parse_rule('role:admin')} - policy.set_rules(rules) + rules = {self.policy_path + ':show': 'role:admin'} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) self.assertRaises(exception.Forbidden, self.KeyPairController.show, self.req, 'FAKE') def test_keypair_show_pass_policy(self): - rules = {self.policy_path + ':show': - common_policy.parse_rule('')} - policy.set_rules(rules) + rules = {self.policy_path + ':show': ''} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) res = self.KeyPairController.show(self.req, 'FAKE') self.assertIn('keypair', res) def test_keypair_create_fail_policy(self): body = {'keypair': {'name': 'create_test'}} - rules = {self.policy_path + ':create': - common_policy.parse_rule('role:admin')} - policy.set_rules(rules) + rules = {self.policy_path + ':create': 'role:admin'} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) self.assertRaises(exception.Forbidden, self.KeyPairController.create, self.req, body=body) def test_keypair_create_pass_policy(self): body = {'keypair': {'name': 'create_test'}} - rules = {self.policy_path + ':create': - common_policy.parse_rule('')} - policy.set_rules(rules) + rules = {self.policy_path + ':create': ''} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) res = self.KeyPairController.create(self.req, body=body) self.assertIn('keypair', res) def test_keypair_delete_fail_policy(self): - rules = {self.policy_path + ':delete': - common_policy.parse_rule('role:admin')} - policy.set_rules(rules) + rules = {self.policy_path + ':delete': 'role:admin'} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) self.assertRaises(exception.Forbidden, self.KeyPairController.delete, self.req, 'FAKE') def test_keypair_delete_pass_policy(self): - rules = {self.policy_path + ':delete': - common_policy.parse_rule('')} - policy.set_rules(rules) + rules = {self.policy_path + ':delete': ''} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) self.KeyPairController.delete(self.req, 'FAKE') diff --git a/nova/tests/unit/api/openstack/compute/test_server_actions.py b/nova/tests/unit/api/openstack/compute/test_server_actions.py index 322e8ba5cad1..8fb6bedb742a 100644 --- a/nova/tests/unit/api/openstack/compute/test_server_actions.py +++ b/nova/tests/unit/api/openstack/compute/test_server_actions.py @@ -19,6 +19,7 @@ import uuid import mock from mox3 import mox from oslo_config import cfg +from oslo_policy import policy as oslo_policy from oslo_utils import uuidutils import webob @@ -32,7 +33,6 @@ from nova import db from nova import exception from nova.image import glance from nova import objects -from nova.openstack.common import policy as common_policy from nova import policy from nova import test from nova.tests.unit.api.openstack import fakes @@ -1316,12 +1316,10 @@ class ServerActionsControllerTestV2(ServerActionsControllerTestV21): } rule_name = "compute:snapshot_volume_backed" rules = { - rule_name: - common_policy.parse_rule("project_id:no_id"), - "compute:get": - common_policy.parse_rule("") + rule_name: "project_id:no_id", + "compute:get": "" } - policy.set_rules(rules) + policy.set_rules(oslo_policy.Rules.from_dict(rules)) with mock.patch.object(compute_api.API, 'is_volume_backed_instance', return_value=True): exc = self.assertRaises(exception.PolicyNotAuthorized, @@ -1342,12 +1340,10 @@ class ServerActionsControllerTestV2(ServerActionsControllerTestV21): } rule_name = "compute:snapshot_volume_backed" rules = { - rule_name: - common_policy.parse_rule("role:no_role"), - "compute:get": - common_policy.parse_rule("") + rule_name: "role:no_role", + "compute:get": "" } - policy.set_rules(rules) + policy.set_rules(oslo_policy.Rules.from_dict(rules)) with mock.patch.object(compute_api.API, 'is_volume_backed_instance', return_value=True): exc = self.assertRaises(exception.PolicyNotAuthorized, diff --git a/nova/tests/unit/api/openstack/compute/test_server_start_stop.py b/nova/tests/unit/api/openstack/compute/test_server_start_stop.py index 8eb743697360..5427a74e6bbe 100644 --- a/nova/tests/unit/api/openstack/compute/test_server_start_stop.py +++ b/nova/tests/unit/api/openstack/compute/test_server_start_stop.py @@ -16,6 +16,8 @@ from mox3 import mox import six import webob +from oslo_policy import policy as oslo_policy + from nova.api.openstack.compute import extension_info from nova.api.openstack.compute.legacy_v2.contrib import server_start_stop \ as server_v2 @@ -24,7 +26,6 @@ from nova.api.openstack.compute import servers \ from nova.compute import api as compute_api from nova import db from nova import exception -from nova.openstack.common import policy as common_policy from nova import policy from nova import test from nova.tests.unit.api.openstack import fakes @@ -79,10 +80,9 @@ class ServerStartStopTestV21(test.TestCase): def test_start_policy_failed(self): rules = { - self.start_policy: - common_policy.parse_rule("project_id:non_fake") + self.start_policy: "project_id:non_fake" } - policy.set_rules(rules) + policy.set_rules(oslo_policy.Rules.from_dict(rules)) self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get) body = dict(start="") exc = self.assertRaises(exception.PolicyNotAuthorized, @@ -123,10 +123,9 @@ class ServerStartStopTestV21(test.TestCase): def test_stop_policy_failed(self): rules = { - self.stop_policy: - common_policy.parse_rule("project_id:non_fake") + self.stop_policy: "project_id:non_fake" } - policy.set_rules(rules) + policy.set_rules(oslo_policy.Rules.from_dict(rules)) self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get) body = dict(stop="") exc = self.assertRaises(exception.PolicyNotAuthorized, diff --git a/nova/tests/unit/api/openstack/compute/test_serversV21.py b/nova/tests/unit/api/openstack/compute/test_serversV21.py index 1aeef50c734d..efe8afc32c90 100644 --- a/nova/tests/unit/api/openstack/compute/test_serversV21.py +++ b/nova/tests/unit/api/openstack/compute/test_serversV21.py @@ -25,6 +25,7 @@ import iso8601 import mock from mox3 import mox from oslo_config import cfg +from oslo_policy import policy as oslo_policy from oslo_serialization import jsonutils from oslo_utils import timeutils from six.moves import range @@ -58,7 +59,6 @@ from nova.network import manager from nova.network.neutronv2 import api as neutron_api from nova import objects from nova.objects import instance as instance_obj -from nova.openstack.common import policy as common_policy from nova import policy from nova import test from nova.tests.unit.api.openstack import fakes @@ -834,12 +834,10 @@ class ServersControllerTest(ControllerTest): self.stubs.Set(compute_api.API, 'get_all', fake_get_all) rules = { - "os_compute_api:servers:index": - common_policy.parse_rule("project_id:fake"), - "os_compute_api:servers:index:get_all_tenants": - common_policy.parse_rule("project_id:fake") + "os_compute_api:servers:index": "project_id:fake", + "os_compute_api:servers:index:get_all_tenants": "project_id:fake" } - policy.set_rules(rules) + policy.set_rules(oslo_policy.Rules.from_dict(rules)) req = self.req('/fake/servers?all_tenants=1') servers = self.controller.index(req)['servers'] @@ -852,12 +850,11 @@ class ServersControllerTest(ControllerTest): rules = { "os_compute_api:servers:index:get_all_tenants": - common_policy.parse_rule("project_id:non_fake"), - "os_compute_api:servers:get_all": - common_policy.parse_rule("project_id:fake"), + "project_id:non_fake", + "os_compute_api:servers:get_all": "project_id:fake", } - policy.set_rules(rules) + policy.set_rules(oslo_policy.Rules.from_dict(rules)) self.stubs.Set(compute_api.API, 'get_all', fake_get_all) req = self.req('/fake/servers?all_tenants=1') @@ -1717,10 +1714,9 @@ class ServersControllerRebuildInstanceTest(ControllerTest): def test_start_policy_failed(self): rules = { - "os_compute_api:servers:start": - common_policy.parse_rule("project_id:non_fake") + "os_compute_api:servers:start": "project_id:non_fake" } - policy.set_rules(rules) + policy.set_rules(oslo_policy.Rules.from_dict(rules)) req = fakes.HTTPRequestV21.blank('/fake/servers/%s/action' % FAKE_UUID) body = dict(start="") exc = self.assertRaises(exception.PolicyNotAuthorized, @@ -1761,10 +1757,9 @@ class ServersControllerRebuildInstanceTest(ControllerTest): def test_stop_policy_failed(self): rules = { - "os_compute_api:servers:stop": - common_policy.parse_rule("project_id:non_fake") + "os_compute_api:servers:stop": "project_id:non_fake" } - policy.set_rules(rules) + policy.set_rules(oslo_policy.Rules.from_dict(rules)) req = fakes.HTTPRequestV21.blank('/fake/servers/%s/action' % FAKE_UUID) body = dict(stop='') exc = self.assertRaises(exception.PolicyNotAuthorized, @@ -1934,8 +1929,8 @@ class ServersControllerUpdateTest(ControllerTest): req, FAKE_UUID, body=body) def test_update_server_policy_fail(self): - rule = {'compute:update': common_policy.parse_rule('role:admin')} - policy.set_rules(rule) + rule = {'compute:update': 'role:admin'} + policy.set_rules(oslo_policy.Rules.from_dict(rule)) body = {'server': {'name': 'server_test'}} req = self._get_request(body, {'name': 'server_test'}) self.assertRaises(exception.PolicyNotAuthorized, @@ -1979,9 +1974,8 @@ class ServerStatusTest(test.TestCase): self.stubs.Set(self.controller, '_get_server', fake_get_server) - rule = {'compute:reboot': - common_policy.parse_rule('role:admin')} - policy.set_rules(rule) + rule = {'compute:reboot': 'role:admin'} + policy.set_rules(oslo_policy.Rules.from_dict(rule)) req = fakes.HTTPRequestV21.blank('/fake/servers/1234/action') self.assertRaises(exception.PolicyNotAuthorized, self.controller._action_reboot, req, '1234', @@ -2007,9 +2001,8 @@ class ServerStatusTest(test.TestCase): self.stubs.Set(self.controller, '_get_server', fake_get_server) - rule = {'compute:confirm_resize': - common_policy.parse_rule('role:admin')} - policy.set_rules(rule) + rule = {'compute:confirm_resize': 'role:admin'} + policy.set_rules(oslo_policy.Rules.from_dict(rule)) req = fakes.HTTPRequestV21.blank('/fake/servers/1234/action') self.assertRaises(exception.PolicyNotAuthorized, self.controller._action_confirm_resize, req, '1234', {}) @@ -2029,9 +2022,8 @@ class ServerStatusTest(test.TestCase): self.stubs.Set(self.controller, '_get_server', fake_get_server) - rule = {'compute:revert_resize': - common_policy.parse_rule('role:admin')} - policy.set_rules(rule) + rule = {'compute:revert_resize': 'role:admin'} + policy.set_rules(oslo_policy.Rules.from_dict(rule)) req = fakes.HTTPRequestV21.blank('/fake/servers/1234/action') self.assertRaises(exception.PolicyNotAuthorized, self.controller._action_revert_resize, req, '1234', {}) diff --git a/nova/tests/unit/api/openstack/compute/test_shelve.py b/nova/tests/unit/api/openstack/compute/test_shelve.py index 9585067251d9..2c9df3f74aea 100644 --- a/nova/tests/unit/api/openstack/compute/test_shelve.py +++ b/nova/tests/unit/api/openstack/compute/test_shelve.py @@ -14,6 +14,7 @@ import uuid +from oslo_policy import policy as oslo_policy import webob from nova.api.openstack.compute.legacy_v2.contrib import shelve as shelve_v2 @@ -21,7 +22,6 @@ from nova.api.openstack.compute import shelve as shelve_v21 from nova.compute import api as compute_api from nova import db from nova import exception -from nova.openstack.common import policy as common_policy from nova import policy from nova import test from nova.tests.unit.api.openstack import fakes @@ -45,9 +45,8 @@ class ShelvePolicyTestV21(test.NoDBTestCase): self.req = fakes.HTTPRequest.blank('') def test_shelve_restricted_by_role(self): - rules = {'compute_extension:%sshelve' % self.prefix: - common_policy.parse_rule('role:admin')} - policy.set_rules(rules) + rules = {'compute_extension:%sshelve' % self.prefix: 'role:admin'} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) self.assertRaises(exception.Forbidden, self.controller._shelve, self.req, str(uuid.uuid4()), {}) @@ -60,9 +59,8 @@ class ShelvePolicyTestV21(test.NoDBTestCase): self.req, str(uuid.uuid4()), {}) def test_unshelve_restricted_by_role(self): - rules = {'compute_extension:%sunshelve' % self.prefix: - common_policy.parse_rule('role:admin')} - policy.set_rules(rules) + rules = {'compute_extension:%sunshelve' % self.prefix: 'role:admin'} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) self.assertRaises(exception.Forbidden, self.controller._unshelve, self.req, str(uuid.uuid4()), {}) @@ -76,8 +74,8 @@ class ShelvePolicyTestV21(test.NoDBTestCase): def test_shelve_offload_restricted_by_role(self): rules = {'compute_extension:%s%s' % (self.prefix, self.offload): - common_policy.parse_rule('role:admin')} - policy.set_rules(rules) + 'role:admin'} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) self.assertRaises(exception.Forbidden, self.controller._shelve_offload, self.req, @@ -99,29 +97,26 @@ class ShelvePolicyTestV2(ShelvePolicyTestV21): # These 3 cases are covered in ShelvePolicyEnforcementV21 def test_shelve_allowed(self): - rules = {'compute:get': common_policy.parse_rule(''), - 'compute_extension:%sshelve' % self.prefix: - common_policy.parse_rule('')} - policy.set_rules(rules) + rules = {'compute:get': '', + 'compute_extension:%sshelve' % self.prefix: ''} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get_by_uuid) self.assertRaises(exception.Forbidden, self.controller._shelve, self.req, str(uuid.uuid4()), {}) def test_unshelve_allowed(self): - rules = {'compute:get': common_policy.parse_rule(''), - 'compute_extension:%sunshelve' % self.prefix: - common_policy.parse_rule('')} - policy.set_rules(rules) + rules = {'compute:get': '', + 'compute_extension:%sunshelve' % self.prefix: ''} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get_by_uuid) self.assertRaises(exception.Forbidden, self.controller._unshelve, self.req, str(uuid.uuid4()), {}) def test_shelve_offload_allowed(self): - rules = {'compute:get': common_policy.parse_rule(''), - 'compute_extension:%s%s' % (self.prefix, self.offload): - common_policy.parse_rule('')} - policy.set_rules(rules) + rules = {'compute:get': '', + 'compute_extension:%s%s' % (self.prefix, self.offload): ''} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get_by_uuid) self.assertRaises(exception.Forbidden, diff --git a/nova/tests/unit/api/openstack/compute/test_simple_tenant_usage.py b/nova/tests/unit/api/openstack/compute/test_simple_tenant_usage.py index 7ed5ddb428b6..2692374a66b6 100644 --- a/nova/tests/unit/api/openstack/compute/test_simple_tenant_usage.py +++ b/nova/tests/unit/api/openstack/compute/test_simple_tenant_usage.py @@ -16,6 +16,7 @@ import datetime import mock +from oslo_policy import policy as oslo_policy from oslo_utils import timeutils from six.moves import range import webob @@ -29,7 +30,6 @@ from nova import context from nova import db from nova import exception from nova import objects -from nova.openstack.common import policy as common_policy from nova import policy from nova import test from nova.tests.unit.api.openstack import fakes @@ -213,12 +213,10 @@ class SimpleTenantUsageTestV21(test.TestCase): req.environ['nova.context'] = self.alt_user_context rules = { - self.policy_rule_prefix + ":show": - common_policy.parse_rule([ - ["role:admin"], ["project_id:%(project_id)s"] - ]) + self.policy_rule_prefix + ":show": [ + ["role:admin"], ["project_id:%(project_id)s"]] } - policy.set_rules(rules) + policy.set_rules(oslo_policy.Rules.from_dict(rules)) try: self.assertRaises(exception.PolicyNotAuthorized, diff --git a/nova/tests/unit/compute/test_compute_api.py b/nova/tests/unit/compute/test_compute_api.py index ac0c10229850..d06ea7930f4b 100644 --- a/nova/tests/unit/compute/test_compute_api.py +++ b/nova/tests/unit/compute/test_compute_api.py @@ -19,6 +19,7 @@ import datetime import iso8601 import mock from mox3 import mox +from oslo_policy import policy as oslo_policy from oslo_serialization import jsonutils from oslo_utils import timeutils from oslo_utils import uuidutils @@ -40,7 +41,6 @@ from nova import exception from nova import objects from nova.objects import base as obj_base from nova.objects import quotas as quotas_obj -from nova.openstack.common import policy as common_policy from nova import policy from nova import quota from nova import test @@ -2924,11 +2924,11 @@ class _ComputeAPIUnitTestMixIn(object): def test_skip_policy_check(self, mock_create, mock_get_ins_by_filters, mock_get, mock_pause, mock_action, mock_save): policy.reset() - rules = {'compute:pause': common_policy.parse_rule('!'), - 'compute:get': common_policy.parse_rule('!'), - 'compute:get_all': common_policy.parse_rule('!'), - 'compute:create': common_policy.parse_rule('!')} - policy.set_rules(common_policy.Rules(rules)) + rules = {'compute:pause': '!', + 'compute:get': '!', + 'compute:get_all': '!', + 'compute:create': '!'} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) instance = self._create_instance_obj() mock_get.return_value = instance diff --git a/nova/tests/unit/conf_fixture.py b/nova/tests/unit/conf_fixture.py index 3cbd828da3fc..fc66696bbbb9 100644 --- a/nova/tests/unit/conf_fixture.py +++ b/nova/tests/unit/conf_fixture.py @@ -16,6 +16,7 @@ from oslo_config import cfg from oslo_config import fixture as config_fixture +from oslo_policy import opts as policy_opts from nova import config from nova import ipv6 @@ -31,7 +32,6 @@ CONF.import_opt('network_size', 'nova.network.manager') CONF.import_opt('num_networks', 'nova.network.manager') CONF.import_opt('floating_ip_dns_manager', 'nova.network.floating_ips') CONF.import_opt('instance_dns_manager', 'nova.network.floating_ips') -CONF.import_opt('policy_file', 'nova.openstack.common.policy') CONF.import_opt('compute_driver', 'nova.virt.driver') CONF.import_opt('api_paste_config', 'nova.wsgi') @@ -66,5 +66,6 @@ class ConfFixture(config_fixture.Config): self.conf.set_default('enabled', True, 'osapi_v21') self.conf.set_default('force_dhcp_release', False) self.conf.set_default('periodic_enable', False) + policy_opts.set_defaults(self.conf) self.addCleanup(utils.cleanup_dns_managers) self.addCleanup(ipv6.api.reset_backend) diff --git a/nova/tests/unit/network/test_api.py b/nova/tests/unit/network/test_api.py index 2c5cef415f2f..dadc6d7a35e3 100644 --- a/nova/tests/unit/network/test_api.py +++ b/nova/tests/unit/network/test_api.py @@ -20,6 +20,7 @@ import uuid import mock from mox3 import mox +from oslo_policy import policy as oslo_policy from nova.compute import flavors from nova import context @@ -32,7 +33,6 @@ from nova.network import model as network_model from nova.network import rpcapi as network_rpcapi from nova import objects from nova.objects import fields -from nova.openstack.common import policy as common_policy from nova import policy from nova import test from nova.tests.unit import fake_instance @@ -76,8 +76,8 @@ class NetworkPolicyTestCase(test.TestCase): def test_skip_policy(self): policy.reset() - rules = {'network:get_all': common_policy.parse_rule('!')} - policy.set_rules(common_policy.Rules(rules)) + rules = {'network:get_all': '!'} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) api = network.API() self.assertRaises(exception.PolicyNotAuthorized, api.get_all, self.context) diff --git a/nova/tests/unit/network/test_neutronv2.py b/nova/tests/unit/network/test_neutronv2.py index d7cc79aa94c7..1166377ec65a 100644 --- a/nova/tests/unit/network/test_neutronv2.py +++ b/nova/tests/unit/network/test_neutronv2.py @@ -23,6 +23,7 @@ from mox3 import mox from neutronclient.common import exceptions from neutronclient.v2_0 import client from oslo_config import cfg +from oslo_policy import policy as oslo_policy from oslo_serialization import jsonutils from oslo_utils import timeutils import six @@ -35,7 +36,6 @@ from nova.network import model from nova.network.neutronv2 import api as neutronapi from nova.network.neutronv2 import constants from nova import objects -from nova.openstack.common import policy as common_policy from nova.pci import manager as pci_manager from nova.pci import whitelist as pci_whitelist from nova import policy @@ -1914,9 +1914,8 @@ class TestNeutronv2(TestNeutronv2Base): self._get_available_networks(prv_nets, pub_nets, req_ids) def test_get_available_networks_with_custom_policy(self): - rules = {'network:attach_external_network': - common_policy.parse_rule('')} - policy.set_rules(rules) + rules = {'network:attach_external_network': ''} + policy.set_rules(oslo_policy.Rules.from_dict(rules)) req_ids = [net['id'] for net in self.nets5] self._get_available_networks(self.nets5, pub_nets=[], req_ids=req_ids) diff --git a/nova/tests/unit/policy_fixture.py b/nova/tests/unit/policy_fixture.py index 6b815319b4e8..6ba189190c43 100644 --- a/nova/tests/unit/policy_fixture.py +++ b/nova/tests/unit/policy_fixture.py @@ -16,10 +16,10 @@ import os import fixtures from oslo_config import cfg +from oslo_policy import policy as oslo_policy from oslo_serialization import jsonutils import six -from nova.openstack.common import policy as common_policy from nova import paths import nova.policy from nova.tests.unit import fake_policy @@ -48,15 +48,14 @@ class RealPolicyFixture(fixtures.Fixture): # policy_file can be overridden by subclasses self.policy_file = paths.state_path_def('etc/nova/policy.json') self._prepare_policy() - CONF.set_override('policy_file', self.policy_file) + CONF.set_override('policy_file', self.policy_file, group='oslo_policy') nova.policy.reset() nova.policy.init() self.addCleanup(nova.policy.reset) def set_rules(self, rules): policy = nova.policy._ENFORCER - policy.set_rules({k: common_policy.parse_rule(v) - for k, v in rules.items()}) + policy.set_rules(oslo_policy.Rules.from_dict(rules)) class PolicyFixture(RealPolicyFixture): @@ -80,7 +79,7 @@ class PolicyFixture(RealPolicyFixture): 'policy.json') with open(self.policy_file, 'w') as f: f.write(fake_policy.policy_data) - CONF.set_override('policy_dirs', []) + CONF.set_override('policy_dirs', [], group='oslo_policy') class RoleBasedPolicyFixture(RealPolicyFixture): @@ -101,7 +100,7 @@ class RoleBasedPolicyFixture(RealPolicyFixture): self.role = role def _prepare_policy(self): - policy = jsonutils.load(open(CONF.policy_file)) + policy = jsonutils.load(open(CONF.oslo_policy.policy_file)) # Convert all actions to require specified role for action, rule in six.iteritems(policy): diff --git a/nova/tests/unit/test_policy.py b/nova/tests/unit/test_policy.py index f39bfa93ebf6..00f4fbf280bc 100644 --- a/nova/tests/unit/test_policy.py +++ b/nova/tests/unit/test_policy.py @@ -16,15 +16,13 @@ """Test of Policy Engine For Nova.""" import os.path -from six.moves import StringIO -import mock +from oslo_policy import policy as oslo_policy from oslo_serialization import jsonutils -import six.moves.urllib.request as urlrequest +import requests_mock from nova import context from nova import exception -from nova.openstack.common import policy as common_policy from nova import policy from nova import test from nova.tests.unit import fake_policy @@ -42,7 +40,7 @@ class PolicyFileTestCase(test.NoDBTestCase): with utils.tempdir() as tmpdir: tmpfilename = os.path.join(tmpdir, 'policy') - self.flags(policy_file=tmpfilename) + self.flags(policy_file=tmpfilename, group='oslo_policy') # NOTE(uni): context construction invokes policy check to determin # is_admin or not. As a side-effect, policy reset is needed here @@ -77,8 +75,7 @@ class PolicyTestCase(test.NoDBTestCase): } policy.reset() policy.init() - policy.set_rules({k: common_policy.parse_rule(v) - for k, v in rules.items()}) + policy.set_rules(oslo_policy.Rules.from_dict(rules)) self.context = context.RequestContext('fake', 'fake', roles=['member']) self.target = {} @@ -102,17 +99,19 @@ class PolicyTestCase(test.NoDBTestCase): result = policy.enforce(self.context, action, self.target) self.assertEqual(result, True) - @mock.patch.object(urlrequest, 'urlopen') - def test_enforce_http_true(self, mock_urlrequest): - mock_urlrequest.return_value = StringIO("True") + @requests_mock.mock() + def test_enforce_http_true(self, req_mock): + req_mock.post('http://www.example.com/', + text='True') action = "example:get_http" target = {} result = policy.enforce(self.context, action, target) self.assertEqual(result, True) - @mock.patch.object(urlrequest, 'urlopen') - def test_enforce_http_false(self, mock_urlrequest): - mock_urlrequest.return_value = StringIO("False") + @requests_mock.mock() + def test_enforce_http_false(self, req_mock): + req_mock.post('http://www.example.com/', + text='False') action = "example:get_http" target = {} self.assertRaises(exception.PolicyNotAuthorized, policy.enforce, @@ -163,8 +162,7 @@ class DefaultPolicyTestCase(test.NoDBTestCase): def _set_rules(self, default_rule): policy.reset() - rules = {k: common_policy.parse_rule(v) - for k, v in self.rules.items()} + rules = oslo_policy.Rules.from_dict(self.rules) policy.init(rules=rules, default_rule=default_rule, use_conf=False) def test_policy_called(self): diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index 71121775e28f..ef8029bd9509 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -13201,7 +13201,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase): _fake_network_info(self.stubs, 1)) def test_cleanup_resize_same_host(self): - CONF.set_override('policy_dirs', []) + CONF.set_override('policy_dirs', [], group='oslo_policy') ins_ref = self._create_instance({'host': CONF.host}) def fake_os_path_exists(path): @@ -13222,7 +13222,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase): _fake_network_info(self.stubs, 1)) def test_cleanup_resize_not_same_host(self): - CONF.set_override('policy_dirs', []) + CONF.set_override('policy_dirs', [], group='oslo_policy') host = 'not' + CONF.host ins_ref = self._create_instance({'host': host}) diff --git a/requirements.txt b/requirements.txt index 1b9f904135cb..3845923a0357 100644 --- a/requirements.txt +++ b/requirements.txt @@ -44,6 +44,7 @@ oslo.utils>=2.8.0 # Apache-2.0 oslo.db>=3.2.0 # Apache-2.0 oslo.rootwrap>=2.0.0 # Apache-2.0 oslo.messaging!=2.8.0,>2.6.1 # Apache-2.0 +oslo.policy>=0.5.0 # Apache-2.0 oslo.i18n>=1.5.0 # Apache-2.0 oslo.service>=0.12.0 # Apache-2.0 rfc3986>=0.2.0 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg index 72444bbdc5fe..2eea8349457e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -36,7 +36,6 @@ oslo.config.opts = nova.scheduler = nova.scheduler.opts:list_opts nova.virt = nova.virt.opts:list_opts nova.openstack.common.memorycache = nova.openstack.common.memorycache:list_opts - nova.openstack.common.policy = nova.openstack.common.policy:list_opts nova.compute.monitors.cpu = virt_driver = nova.compute.monitors.cpu.virt_driver:Monitor