use graduated oslo.policy
- Add oslo.policy to requirements.txt - Remove in-tree copy of oslo.policy Change-Id: Ia6fef939cfe0df33476ccee365934a5d2878f90d Partial-Bug: #1458945
This commit is contained in:
parent
3c67982f7c
commit
a4b2ade479
@ -10,7 +10,6 @@ namespace = nova.network.neutronv2
|
|||||||
namespace = nova.scheduler
|
namespace = nova.scheduler
|
||||||
namespace = nova.virt
|
namespace = nova.virt
|
||||||
namespace = nova.openstack.common.memorycache
|
namespace = nova.openstack.common.memorycache
|
||||||
namespace = nova.openstack.common.policy
|
|
||||||
namespace = oslo.log
|
namespace = oslo.log
|
||||||
namespace = oslo.messaging
|
namespace = oslo.messaging
|
||||||
namespace = oslo.policy
|
namespace = oslo.policy
|
||||||
|
@ -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 | <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
|
|
||||||
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)
|
|
@ -17,12 +17,14 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
from oslo_policy import policy
|
||||||
from oslo_utils import excutils
|
from oslo_utils import excutils
|
||||||
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.openstack.common import policy
|
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
_ENFORCER = None
|
_ENFORCER = None
|
||||||
|
|
||||||
@ -48,7 +50,8 @@ def init(policy_file=None, rules=None, default_rule=None, use_conf=True):
|
|||||||
|
|
||||||
global _ENFORCER
|
global _ENFORCER
|
||||||
if not _ENFORCER:
|
if not _ENFORCER:
|
||||||
_ENFORCER = policy.Enforcer(policy_file=policy_file,
|
_ENFORCER = policy.Enforcer(CONF,
|
||||||
|
policy_file=policy_file,
|
||||||
rules=rules,
|
rules=rules,
|
||||||
default_rule=default_rule,
|
default_rule=default_rule,
|
||||||
use_conf=use_conf)
|
use_conf=use_conf)
|
||||||
|
@ -29,6 +29,7 @@ import iso8601
|
|||||||
import mock
|
import mock
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
from oslo_policy import policy as oslo_policy
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
|
|
||||||
@ -54,7 +55,6 @@ from nova.network import model
|
|||||||
from nova.network.neutronv2 import api as neutronapi
|
from nova.network.neutronv2 import api as neutronapi
|
||||||
from nova import objects
|
from nova import objects
|
||||||
from nova.objects import base as obj_base
|
from nova.objects import base as obj_base
|
||||||
from nova.openstack.common import policy as common_policy
|
|
||||||
from nova import policy
|
from nova import policy
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.unit.api.openstack.compute import (
|
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):
|
def test_delete_security_group_policy_not_allowed(self):
|
||||||
rules = {'compute_extension:security_groups':
|
rules = {'compute_extension:security_groups':
|
||||||
common_policy.parse_rule('project_id:%(project_id)s')}
|
'project_id:%(project_id)s'}
|
||||||
policy.set_rules(rules)
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
|
|
||||||
with mock.patch.object(self.cloud.security_group_api,
|
with mock.patch.object(self.cloud.security_group_api,
|
||||||
'get') as get:
|
'get') as get:
|
||||||
@ -536,8 +536,8 @@ class CloudTestCase(test.TestCase):
|
|||||||
|
|
||||||
def test_authorize_security_group_ingress_policy_not_allowed(self):
|
def test_authorize_security_group_ingress_policy_not_allowed(self):
|
||||||
rules = {'compute_extension:security_groups':
|
rules = {'compute_extension:security_groups':
|
||||||
common_policy.parse_rule('project_id:%(project_id)s')}
|
'project_id:%(project_id)s'}
|
||||||
policy.set_rules(rules)
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
|
|
||||||
with mock.patch.object(self.cloud.security_group_api,
|
with mock.patch.object(self.cloud.security_group_api,
|
||||||
'get') as get:
|
'get') as get:
|
||||||
@ -669,8 +669,8 @@ class CloudTestCase(test.TestCase):
|
|||||||
|
|
||||||
def test_revoke_security_group_ingress_policy_not_allowed(self):
|
def test_revoke_security_group_ingress_policy_not_allowed(self):
|
||||||
rules = {'compute_extension:security_groups':
|
rules = {'compute_extension:security_groups':
|
||||||
common_policy.parse_rule('project_id:%(project_id)s')}
|
'project_id:%(project_id)s'}
|
||||||
policy.set_rules(rules)
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
|
|
||||||
with mock.patch.object(self.cloud.security_group_api,
|
with mock.patch.object(self.cloud.security_group_api,
|
||||||
'get') as get:
|
'get') as get:
|
||||||
@ -2354,10 +2354,9 @@ class CloudTestCase(test.TestCase):
|
|||||||
'max_count': 1, }
|
'max_count': 1, }
|
||||||
instance_id = self._run_instance(**kwargs)
|
instance_id = self._run_instance(**kwargs)
|
||||||
rules = {
|
rules = {
|
||||||
"compute:start":
|
"compute:start": "project_id:non_fake",
|
||||||
common_policy.parse_rule("project_id:non_fake"),
|
|
||||||
}
|
}
|
||||||
policy.set_rules(rules)
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
self.cloud.start_instances,
|
self.cloud.start_instances,
|
||||||
self.context, [instance_id])
|
self.context, [instance_id])
|
||||||
@ -2395,10 +2394,9 @@ class CloudTestCase(test.TestCase):
|
|||||||
'max_count': 1, }
|
'max_count': 1, }
|
||||||
instance_id = self._run_instance(**kwargs)
|
instance_id = self._run_instance(**kwargs)
|
||||||
rules = {
|
rules = {
|
||||||
"compute:stop":
|
"compute:stop": "project_id:non_fake"
|
||||||
common_policy.parse_rule("project_id:non_fake")
|
|
||||||
}
|
}
|
||||||
policy.set_rules(rules)
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
self.cloud.stop_instances,
|
self.cloud.stop_instances,
|
||||||
self.context, [instance_id])
|
self.context, [instance_id])
|
||||||
|
@ -24,6 +24,7 @@ import uuid
|
|||||||
import iso8601
|
import iso8601
|
||||||
import mock
|
import mock
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
from oslo_policy import policy as oslo_policy
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
from six.moves import range
|
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.network.neutronv2 import api as neutron_api
|
||||||
from nova import objects
|
from nova import objects
|
||||||
from nova.objects import instance as instance_obj
|
from nova.objects import instance as instance_obj
|
||||||
from nova.openstack.common import policy as common_policy
|
|
||||||
from nova import policy
|
from nova import policy
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.unit.api.openstack import fakes
|
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)
|
self.stubs.Set(compute_api.API, 'get_all', fake_get_all)
|
||||||
|
|
||||||
rules = {
|
rules = {
|
||||||
"compute:get_all_tenants":
|
"compute:get_all_tenants": "project_id:fake",
|
||||||
common_policy.parse_rule("project_id:fake"),
|
"compute:get_all": "project_id:fake",
|
||||||
"compute:get_all":
|
|
||||||
common_policy.parse_rule("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')
|
req = fakes.HTTPRequest.blank('/fake/servers?all_tenants=1')
|
||||||
servers = self.controller.index(req)['servers']
|
servers = self.controller.index(req)['servers']
|
||||||
@ -814,13 +812,11 @@ class ServersControllerTest(ControllerTest):
|
|||||||
return [fakes.stub_instance(100)]
|
return [fakes.stub_instance(100)]
|
||||||
|
|
||||||
rules = {
|
rules = {
|
||||||
"compute:get_all_tenants":
|
"compute:get_all_tenants": "project_id:non_fake",
|
||||||
common_policy.parse_rule("project_id:non_fake"),
|
"compute:get_all": "project_id:fake",
|
||||||
"compute:get_all":
|
|
||||||
common_policy.parse_rule("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)
|
self.stubs.Set(compute_api.API, 'get_all', fake_get_all)
|
||||||
|
|
||||||
req = fakes.HTTPRequest.blank('/fake/servers?all_tenants=1')
|
req = fakes.HTTPRequest.blank('/fake/servers?all_tenants=1')
|
||||||
@ -1480,8 +1476,8 @@ class ServersControllerUpdateTest(ControllerTest):
|
|||||||
req, FAKE_UUID, body)
|
req, FAKE_UUID, body)
|
||||||
|
|
||||||
def test_update_server_policy_fail(self):
|
def test_update_server_policy_fail(self):
|
||||||
rule = {'compute:update': common_policy.parse_rule('role:admin')}
|
rule = {'compute:update': 'role:admin'}
|
||||||
policy.set_rules(rule)
|
policy.set_rules(oslo_policy.Rules.from_dict(rule))
|
||||||
body = {'server': {'name': 'server_test'}}
|
body = {'server': {'name': 'server_test'}}
|
||||||
req = self._get_request(body, {'name': 'server_test'})
|
req = self._get_request(body, {'name': 'server_test'})
|
||||||
self.assertRaises(exception.PolicyNotAuthorized,
|
self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
@ -1790,9 +1786,8 @@ class ServerStatusTest(test.TestCase):
|
|||||||
return self.controller.show(request, FAKE_UUID)
|
return self.controller.show(request, FAKE_UUID)
|
||||||
|
|
||||||
def _req_with_policy_fail(self, policy_rule_name):
|
def _req_with_policy_fail(self, policy_rule_name):
|
||||||
rule = {'compute:%s' % policy_rule_name:
|
rule = {'compute:%s' % policy_rule_name: 'role:admin'}
|
||||||
common_policy.parse_rule('role:admin')}
|
policy.set_rules(oslo_policy.Rules.from_dict(rule))
|
||||||
policy.set_rules(rule)
|
|
||||||
return fakes.HTTPRequest.blank('/fake/servers/1234/action')
|
return fakes.HTTPRequest.blank('/fake/servers/1234/action')
|
||||||
|
|
||||||
def test_active(self):
|
def test_active(self):
|
||||||
|
@ -18,6 +18,8 @@ import mock
|
|||||||
from mox3 import mox
|
from mox3 import mox
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
|
from oslo_policy import policy as oslo_policy
|
||||||
|
|
||||||
from nova.api.openstack.compute import certificates \
|
from nova.api.openstack.compute import certificates \
|
||||||
as certificates_v21
|
as certificates_v21
|
||||||
from nova.api.openstack.compute.legacy_v2.contrib import certificates \
|
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.cert import rpcapi
|
||||||
from nova import context
|
from nova import context
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.openstack.common import policy as common_policy
|
|
||||||
from nova import policy
|
from nova import policy
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.unit.api.openstack import fakes
|
from nova.tests.unit.api.openstack import fakes
|
||||||
@ -65,10 +66,9 @@ class CertificatesTestV21(test.NoDBTestCase):
|
|||||||
|
|
||||||
def test_certificates_show_policy_failed(self):
|
def test_certificates_show_policy_failed(self):
|
||||||
rules = {
|
rules = {
|
||||||
self.certificate_show_extension:
|
self.certificate_show_extension: "!"
|
||||||
common_policy.parse_rule("!")
|
|
||||||
}
|
}
|
||||||
policy.set_rules(rules)
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
self.controller.show, self.req, 'root')
|
self.controller.show, self.req, 'root')
|
||||||
self.assertIn(self.certificate_show_extension,
|
self.assertIn(self.certificate_show_extension,
|
||||||
@ -95,10 +95,9 @@ class CertificatesTestV21(test.NoDBTestCase):
|
|||||||
|
|
||||||
def test_certificates_create_policy_failed(self):
|
def test_certificates_create_policy_failed(self):
|
||||||
rules = {
|
rules = {
|
||||||
self.certificate_create_extension:
|
self.certificate_create_extension: "!"
|
||||||
common_policy.parse_rule("!")
|
|
||||||
}
|
}
|
||||||
policy.set_rules(rules)
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
self.controller.create, self.req)
|
self.controller.create, self.req)
|
||||||
self.assertIn(self.certificate_create_extension,
|
self.assertIn(self.certificate_create_extension,
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import uuid as stdlib_uuid
|
import uuid as stdlib_uuid
|
||||||
|
|
||||||
|
from oslo_policy import policy as oslo_policy
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
@ -26,7 +27,6 @@ from nova.compute import vm_states
|
|||||||
from nova import console
|
from nova import console
|
||||||
from nova import db
|
from nova import db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.openstack.common import policy as common_policy
|
|
||||||
from nova import policy
|
from nova import policy
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.unit.api.openstack import fakes
|
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):
|
def _test_fail_policy(self, rule, action, data=None):
|
||||||
rules = {
|
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')
|
req = fakes.HTTPRequest.blank(self.url + '/20')
|
||||||
|
|
||||||
if data is not None:
|
if data is not None:
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
import copy
|
import copy
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
from oslo_policy import policy as oslo_policy
|
||||||
import six
|
import six
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
@ -27,7 +28,6 @@ from nova import db
|
|||||||
from nova.db.sqlalchemy import models
|
from nova.db.sqlalchemy import models
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import objects
|
from nova import objects
|
||||||
from nova.openstack.common import policy as common_policy
|
|
||||||
from nova import policy
|
from nova import policy
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.unit.api.openstack import fakes
|
from nova.tests.unit.api.openstack import fakes
|
||||||
@ -81,10 +81,10 @@ class InstanceActionsPolicyTestV21(test.NoDBTestCase):
|
|||||||
return fakes.HTTPRequest.blank(fake_url)
|
return fakes.HTTPRequest.blank(fake_url)
|
||||||
|
|
||||||
def _set_policy_rules(self):
|
def _set_policy_rules(self):
|
||||||
rules = {'compute:get': common_policy.parse_rule(''),
|
rules = {'compute:get': '',
|
||||||
'os_compute_api:os-instance-actions':
|
'os_compute_api:os-instance-actions':
|
||||||
common_policy.parse_rule('project_id:%(project_id)s')}
|
'project_id:%(project_id)s'}
|
||||||
policy.set_rules(rules)
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
|
|
||||||
def test_list_actions_restricted_by_project(self):
|
def test_list_actions_restricted_by_project(self):
|
||||||
self._set_policy_rules()
|
self._set_policy_rules()
|
||||||
@ -121,10 +121,10 @@ class InstanceActionsPolicyTestV2(InstanceActionsPolicyTestV21):
|
|||||||
instance_actions = instance_actions_v2
|
instance_actions = instance_actions_v2
|
||||||
|
|
||||||
def _set_policy_rules(self):
|
def _set_policy_rules(self):
|
||||||
rules = {'compute:get': common_policy.parse_rule(''),
|
rules = {'compute:get': '',
|
||||||
'compute_extension:instance_actions':
|
'compute_extension:instance_actions':
|
||||||
common_policy.parse_rule('project_id:%(project_id)s')}
|
'project_id:%(project_id)s'}
|
||||||
policy.set_rules(rules)
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
|
|
||||||
|
|
||||||
class InstanceActionsTestV21(test.NoDBTestCase):
|
class InstanceActionsTestV21(test.NoDBTestCase):
|
||||||
@ -153,12 +153,10 @@ class InstanceActionsTestV21(test.NoDBTestCase):
|
|||||||
use_admin_context=use_admin_context)
|
use_admin_context=use_admin_context)
|
||||||
|
|
||||||
def _set_policy_rules(self):
|
def _set_policy_rules(self):
|
||||||
rules = {'compute:get': common_policy.parse_rule(''),
|
rules = {'compute:get': '',
|
||||||
'os_compute_api:os-instance-actions':
|
'os_compute_api:os-instance-actions': '',
|
||||||
common_policy.parse_rule(''),
|
'os_compute_api:os-instance-actions:events': 'is_admin:True'}
|
||||||
'os_compute_api:os-instance-actions:events':
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
common_policy.parse_rule('is_admin:True')}
|
|
||||||
policy.set_rules(rules)
|
|
||||||
|
|
||||||
def test_list_actions(self):
|
def test_list_actions(self):
|
||||||
def fake_get_actions(context, uuid):
|
def fake_get_actions(context, uuid):
|
||||||
@ -250,9 +248,7 @@ class InstanceActionsTestV2(InstanceActionsTestV21):
|
|||||||
instance_actions = instance_actions_v2
|
instance_actions = instance_actions_v2
|
||||||
|
|
||||||
def _set_policy_rules(self):
|
def _set_policy_rules(self):
|
||||||
rules = {'compute:get': common_policy.parse_rule(''),
|
rules = {'compute:get': '',
|
||||||
'compute_extension:instance_actions':
|
'compute_extension:instance_actions': '',
|
||||||
common_policy.parse_rule(''),
|
'compute_extension:instance_actions:events': 'is_admin:True'}
|
||||||
'compute_extension:instance_actions:events':
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
common_policy.parse_rule('is_admin:True')}
|
|
||||||
policy.set_rules(rules)
|
|
||||||
|
@ -17,6 +17,8 @@ import mock
|
|||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
import webob
|
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 import keypairs as keypairs_v21
|
||||||
from nova.api.openstack.compute.legacy_v2.contrib import keypairs \
|
from nova.api.openstack.compute.legacy_v2.contrib import keypairs \
|
||||||
as keypairs_v2
|
as keypairs_v2
|
||||||
@ -25,7 +27,6 @@ from nova.compute import api as compute_api
|
|||||||
from nova import db
|
from nova import db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import objects
|
from nova import objects
|
||||||
from nova.openstack.common import policy as common_policy
|
|
||||||
from nova import policy
|
from nova import policy
|
||||||
from nova import quota
|
from nova import quota
|
||||||
from nova import test
|
from nova import test
|
||||||
@ -369,64 +370,56 @@ class KeypairPolicyTestV21(test.TestCase):
|
|||||||
self.req = fakes.HTTPRequest.blank('')
|
self.req = fakes.HTTPRequest.blank('')
|
||||||
|
|
||||||
def test_keypair_list_fail_policy(self):
|
def test_keypair_list_fail_policy(self):
|
||||||
rules = {self.policy_path + ':index':
|
rules = {self.policy_path + ':index': 'role:admin'}
|
||||||
common_policy.parse_rule('role:admin')}
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
policy.set_rules(rules)
|
|
||||||
self.assertRaises(exception.Forbidden,
|
self.assertRaises(exception.Forbidden,
|
||||||
self.KeyPairController.index,
|
self.KeyPairController.index,
|
||||||
self.req)
|
self.req)
|
||||||
|
|
||||||
def test_keypair_list_pass_policy(self):
|
def test_keypair_list_pass_policy(self):
|
||||||
rules = {self.policy_path + ':index':
|
rules = {self.policy_path + ':index': ''}
|
||||||
common_policy.parse_rule('')}
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
policy.set_rules(rules)
|
|
||||||
res = self.KeyPairController.index(self.req)
|
res = self.KeyPairController.index(self.req)
|
||||||
self.assertIn('keypairs', res)
|
self.assertIn('keypairs', res)
|
||||||
|
|
||||||
def test_keypair_show_fail_policy(self):
|
def test_keypair_show_fail_policy(self):
|
||||||
rules = {self.policy_path + ':show':
|
rules = {self.policy_path + ':show': 'role:admin'}
|
||||||
common_policy.parse_rule('role:admin')}
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
policy.set_rules(rules)
|
|
||||||
self.assertRaises(exception.Forbidden,
|
self.assertRaises(exception.Forbidden,
|
||||||
self.KeyPairController.show,
|
self.KeyPairController.show,
|
||||||
self.req, 'FAKE')
|
self.req, 'FAKE')
|
||||||
|
|
||||||
def test_keypair_show_pass_policy(self):
|
def test_keypair_show_pass_policy(self):
|
||||||
rules = {self.policy_path + ':show':
|
rules = {self.policy_path + ':show': ''}
|
||||||
common_policy.parse_rule('')}
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
policy.set_rules(rules)
|
|
||||||
res = self.KeyPairController.show(self.req, 'FAKE')
|
res = self.KeyPairController.show(self.req, 'FAKE')
|
||||||
self.assertIn('keypair', res)
|
self.assertIn('keypair', res)
|
||||||
|
|
||||||
def test_keypair_create_fail_policy(self):
|
def test_keypair_create_fail_policy(self):
|
||||||
body = {'keypair': {'name': 'create_test'}}
|
body = {'keypair': {'name': 'create_test'}}
|
||||||
rules = {self.policy_path + ':create':
|
rules = {self.policy_path + ':create': 'role:admin'}
|
||||||
common_policy.parse_rule('role:admin')}
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
policy.set_rules(rules)
|
|
||||||
self.assertRaises(exception.Forbidden,
|
self.assertRaises(exception.Forbidden,
|
||||||
self.KeyPairController.create,
|
self.KeyPairController.create,
|
||||||
self.req, body=body)
|
self.req, body=body)
|
||||||
|
|
||||||
def test_keypair_create_pass_policy(self):
|
def test_keypair_create_pass_policy(self):
|
||||||
body = {'keypair': {'name': 'create_test'}}
|
body = {'keypair': {'name': 'create_test'}}
|
||||||
rules = {self.policy_path + ':create':
|
rules = {self.policy_path + ':create': ''}
|
||||||
common_policy.parse_rule('')}
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
policy.set_rules(rules)
|
|
||||||
res = self.KeyPairController.create(self.req, body=body)
|
res = self.KeyPairController.create(self.req, body=body)
|
||||||
self.assertIn('keypair', res)
|
self.assertIn('keypair', res)
|
||||||
|
|
||||||
def test_keypair_delete_fail_policy(self):
|
def test_keypair_delete_fail_policy(self):
|
||||||
rules = {self.policy_path + ':delete':
|
rules = {self.policy_path + ':delete': 'role:admin'}
|
||||||
common_policy.parse_rule('role:admin')}
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
policy.set_rules(rules)
|
|
||||||
self.assertRaises(exception.Forbidden,
|
self.assertRaises(exception.Forbidden,
|
||||||
self.KeyPairController.delete,
|
self.KeyPairController.delete,
|
||||||
self.req, 'FAKE')
|
self.req, 'FAKE')
|
||||||
|
|
||||||
def test_keypair_delete_pass_policy(self):
|
def test_keypair_delete_pass_policy(self):
|
||||||
rules = {self.policy_path + ':delete':
|
rules = {self.policy_path + ':delete': ''}
|
||||||
common_policy.parse_rule('')}
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
policy.set_rules(rules)
|
|
||||||
self.KeyPairController.delete(self.req, 'FAKE')
|
self.KeyPairController.delete(self.req, 'FAKE')
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ import uuid
|
|||||||
import mock
|
import mock
|
||||||
from mox3 import mox
|
from mox3 import mox
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
from oslo_policy import policy as oslo_policy
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
@ -32,7 +33,6 @@ from nova import db
|
|||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.image import glance
|
from nova.image import glance
|
||||||
from nova import objects
|
from nova import objects
|
||||||
from nova.openstack.common import policy as common_policy
|
|
||||||
from nova import policy
|
from nova import policy
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.unit.api.openstack import fakes
|
from nova.tests.unit.api.openstack import fakes
|
||||||
@ -1316,12 +1316,10 @@ class ServerActionsControllerTestV2(ServerActionsControllerTestV21):
|
|||||||
}
|
}
|
||||||
rule_name = "compute:snapshot_volume_backed"
|
rule_name = "compute:snapshot_volume_backed"
|
||||||
rules = {
|
rules = {
|
||||||
rule_name:
|
rule_name: "project_id:no_id",
|
||||||
common_policy.parse_rule("project_id:no_id"),
|
"compute:get": ""
|
||||||
"compute:get":
|
|
||||||
common_policy.parse_rule("")
|
|
||||||
}
|
}
|
||||||
policy.set_rules(rules)
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
with mock.patch.object(compute_api.API, 'is_volume_backed_instance',
|
with mock.patch.object(compute_api.API, 'is_volume_backed_instance',
|
||||||
return_value=True):
|
return_value=True):
|
||||||
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
@ -1342,12 +1340,10 @@ class ServerActionsControllerTestV2(ServerActionsControllerTestV21):
|
|||||||
}
|
}
|
||||||
rule_name = "compute:snapshot_volume_backed"
|
rule_name = "compute:snapshot_volume_backed"
|
||||||
rules = {
|
rules = {
|
||||||
rule_name:
|
rule_name: "role:no_role",
|
||||||
common_policy.parse_rule("role:no_role"),
|
"compute:get": ""
|
||||||
"compute:get":
|
|
||||||
common_policy.parse_rule("")
|
|
||||||
}
|
}
|
||||||
policy.set_rules(rules)
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
with mock.patch.object(compute_api.API, 'is_volume_backed_instance',
|
with mock.patch.object(compute_api.API, 'is_volume_backed_instance',
|
||||||
return_value=True):
|
return_value=True):
|
||||||
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
|
@ -16,6 +16,8 @@ from mox3 import mox
|
|||||||
import six
|
import six
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
|
from oslo_policy import policy as oslo_policy
|
||||||
|
|
||||||
from nova.api.openstack.compute import extension_info
|
from nova.api.openstack.compute import extension_info
|
||||||
from nova.api.openstack.compute.legacy_v2.contrib import server_start_stop \
|
from nova.api.openstack.compute.legacy_v2.contrib import server_start_stop \
|
||||||
as server_v2
|
as server_v2
|
||||||
@ -24,7 +26,6 @@ from nova.api.openstack.compute import servers \
|
|||||||
from nova.compute import api as compute_api
|
from nova.compute import api as compute_api
|
||||||
from nova import db
|
from nova import db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.openstack.common import policy as common_policy
|
|
||||||
from nova import policy
|
from nova import policy
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.unit.api.openstack import fakes
|
from nova.tests.unit.api.openstack import fakes
|
||||||
@ -79,10 +80,9 @@ class ServerStartStopTestV21(test.TestCase):
|
|||||||
|
|
||||||
def test_start_policy_failed(self):
|
def test_start_policy_failed(self):
|
||||||
rules = {
|
rules = {
|
||||||
self.start_policy:
|
self.start_policy: "project_id:non_fake"
|
||||||
common_policy.parse_rule("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)
|
self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get)
|
||||||
body = dict(start="")
|
body = dict(start="")
|
||||||
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
@ -123,10 +123,9 @@ class ServerStartStopTestV21(test.TestCase):
|
|||||||
|
|
||||||
def test_stop_policy_failed(self):
|
def test_stop_policy_failed(self):
|
||||||
rules = {
|
rules = {
|
||||||
self.stop_policy:
|
self.stop_policy: "project_id:non_fake"
|
||||||
common_policy.parse_rule("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)
|
self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get)
|
||||||
body = dict(stop="")
|
body = dict(stop="")
|
||||||
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
|
@ -25,6 +25,7 @@ import iso8601
|
|||||||
import mock
|
import mock
|
||||||
from mox3 import mox
|
from mox3 import mox
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
from oslo_policy import policy as oslo_policy
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
from six.moves import range
|
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.network.neutronv2 import api as neutron_api
|
||||||
from nova import objects
|
from nova import objects
|
||||||
from nova.objects import instance as instance_obj
|
from nova.objects import instance as instance_obj
|
||||||
from nova.openstack.common import policy as common_policy
|
|
||||||
from nova import policy
|
from nova import policy
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.unit.api.openstack import fakes
|
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)
|
self.stubs.Set(compute_api.API, 'get_all', fake_get_all)
|
||||||
|
|
||||||
rules = {
|
rules = {
|
||||||
"os_compute_api:servers:index":
|
"os_compute_api:servers:index": "project_id:fake",
|
||||||
common_policy.parse_rule("project_id:fake"),
|
"os_compute_api:servers:index:get_all_tenants": "project_id:fake"
|
||||||
"os_compute_api:servers:index:get_all_tenants":
|
|
||||||
common_policy.parse_rule("project_id:fake")
|
|
||||||
}
|
}
|
||||||
policy.set_rules(rules)
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
|
|
||||||
req = self.req('/fake/servers?all_tenants=1')
|
req = self.req('/fake/servers?all_tenants=1')
|
||||||
servers = self.controller.index(req)['servers']
|
servers = self.controller.index(req)['servers']
|
||||||
@ -852,12 +850,11 @@ class ServersControllerTest(ControllerTest):
|
|||||||
|
|
||||||
rules = {
|
rules = {
|
||||||
"os_compute_api:servers:index:get_all_tenants":
|
"os_compute_api:servers:index:get_all_tenants":
|
||||||
common_policy.parse_rule("project_id:non_fake"),
|
"project_id:non_fake",
|
||||||
"os_compute_api:servers:get_all":
|
"os_compute_api:servers:get_all": "project_id:fake",
|
||||||
common_policy.parse_rule("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)
|
self.stubs.Set(compute_api.API, 'get_all', fake_get_all)
|
||||||
|
|
||||||
req = self.req('/fake/servers?all_tenants=1')
|
req = self.req('/fake/servers?all_tenants=1')
|
||||||
@ -1717,10 +1714,9 @@ class ServersControllerRebuildInstanceTest(ControllerTest):
|
|||||||
|
|
||||||
def test_start_policy_failed(self):
|
def test_start_policy_failed(self):
|
||||||
rules = {
|
rules = {
|
||||||
"os_compute_api:servers:start":
|
"os_compute_api:servers:start": "project_id:non_fake"
|
||||||
common_policy.parse_rule("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)
|
req = fakes.HTTPRequestV21.blank('/fake/servers/%s/action' % FAKE_UUID)
|
||||||
body = dict(start="")
|
body = dict(start="")
|
||||||
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
@ -1761,10 +1757,9 @@ class ServersControllerRebuildInstanceTest(ControllerTest):
|
|||||||
|
|
||||||
def test_stop_policy_failed(self):
|
def test_stop_policy_failed(self):
|
||||||
rules = {
|
rules = {
|
||||||
"os_compute_api:servers:stop":
|
"os_compute_api:servers:stop": "project_id:non_fake"
|
||||||
common_policy.parse_rule("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)
|
req = fakes.HTTPRequestV21.blank('/fake/servers/%s/action' % FAKE_UUID)
|
||||||
body = dict(stop='')
|
body = dict(stop='')
|
||||||
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
@ -1934,8 +1929,8 @@ class ServersControllerUpdateTest(ControllerTest):
|
|||||||
req, FAKE_UUID, body=body)
|
req, FAKE_UUID, body=body)
|
||||||
|
|
||||||
def test_update_server_policy_fail(self):
|
def test_update_server_policy_fail(self):
|
||||||
rule = {'compute:update': common_policy.parse_rule('role:admin')}
|
rule = {'compute:update': 'role:admin'}
|
||||||
policy.set_rules(rule)
|
policy.set_rules(oslo_policy.Rules.from_dict(rule))
|
||||||
body = {'server': {'name': 'server_test'}}
|
body = {'server': {'name': 'server_test'}}
|
||||||
req = self._get_request(body, {'name': 'server_test'})
|
req = self._get_request(body, {'name': 'server_test'})
|
||||||
self.assertRaises(exception.PolicyNotAuthorized,
|
self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
@ -1979,9 +1974,8 @@ class ServerStatusTest(test.TestCase):
|
|||||||
|
|
||||||
self.stubs.Set(self.controller, '_get_server', fake_get_server)
|
self.stubs.Set(self.controller, '_get_server', fake_get_server)
|
||||||
|
|
||||||
rule = {'compute:reboot':
|
rule = {'compute:reboot': 'role:admin'}
|
||||||
common_policy.parse_rule('role:admin')}
|
policy.set_rules(oslo_policy.Rules.from_dict(rule))
|
||||||
policy.set_rules(rule)
|
|
||||||
req = fakes.HTTPRequestV21.blank('/fake/servers/1234/action')
|
req = fakes.HTTPRequestV21.blank('/fake/servers/1234/action')
|
||||||
self.assertRaises(exception.PolicyNotAuthorized,
|
self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
self.controller._action_reboot, req, '1234',
|
self.controller._action_reboot, req, '1234',
|
||||||
@ -2007,9 +2001,8 @@ class ServerStatusTest(test.TestCase):
|
|||||||
|
|
||||||
self.stubs.Set(self.controller, '_get_server', fake_get_server)
|
self.stubs.Set(self.controller, '_get_server', fake_get_server)
|
||||||
|
|
||||||
rule = {'compute:confirm_resize':
|
rule = {'compute:confirm_resize': 'role:admin'}
|
||||||
common_policy.parse_rule('role:admin')}
|
policy.set_rules(oslo_policy.Rules.from_dict(rule))
|
||||||
policy.set_rules(rule)
|
|
||||||
req = fakes.HTTPRequestV21.blank('/fake/servers/1234/action')
|
req = fakes.HTTPRequestV21.blank('/fake/servers/1234/action')
|
||||||
self.assertRaises(exception.PolicyNotAuthorized,
|
self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
self.controller._action_confirm_resize, req, '1234', {})
|
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)
|
self.stubs.Set(self.controller, '_get_server', fake_get_server)
|
||||||
|
|
||||||
rule = {'compute:revert_resize':
|
rule = {'compute:revert_resize': 'role:admin'}
|
||||||
common_policy.parse_rule('role:admin')}
|
policy.set_rules(oslo_policy.Rules.from_dict(rule))
|
||||||
policy.set_rules(rule)
|
|
||||||
req = fakes.HTTPRequestV21.blank('/fake/servers/1234/action')
|
req = fakes.HTTPRequestV21.blank('/fake/servers/1234/action')
|
||||||
self.assertRaises(exception.PolicyNotAuthorized,
|
self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
self.controller._action_revert_resize, req, '1234', {})
|
self.controller._action_revert_resize, req, '1234', {})
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
from oslo_policy import policy as oslo_policy
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
from nova.api.openstack.compute.legacy_v2.contrib import shelve as shelve_v2
|
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.compute import api as compute_api
|
||||||
from nova import db
|
from nova import db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.openstack.common import policy as common_policy
|
|
||||||
from nova import policy
|
from nova import policy
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.unit.api.openstack import fakes
|
from nova.tests.unit.api.openstack import fakes
|
||||||
@ -45,9 +45,8 @@ class ShelvePolicyTestV21(test.NoDBTestCase):
|
|||||||
self.req = fakes.HTTPRequest.blank('')
|
self.req = fakes.HTTPRequest.blank('')
|
||||||
|
|
||||||
def test_shelve_restricted_by_role(self):
|
def test_shelve_restricted_by_role(self):
|
||||||
rules = {'compute_extension:%sshelve' % self.prefix:
|
rules = {'compute_extension:%sshelve' % self.prefix: 'role:admin'}
|
||||||
common_policy.parse_rule('role:admin')}
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
policy.set_rules(rules)
|
|
||||||
|
|
||||||
self.assertRaises(exception.Forbidden, self.controller._shelve,
|
self.assertRaises(exception.Forbidden, self.controller._shelve,
|
||||||
self.req, str(uuid.uuid4()), {})
|
self.req, str(uuid.uuid4()), {})
|
||||||
@ -60,9 +59,8 @@ class ShelvePolicyTestV21(test.NoDBTestCase):
|
|||||||
self.req, str(uuid.uuid4()), {})
|
self.req, str(uuid.uuid4()), {})
|
||||||
|
|
||||||
def test_unshelve_restricted_by_role(self):
|
def test_unshelve_restricted_by_role(self):
|
||||||
rules = {'compute_extension:%sunshelve' % self.prefix:
|
rules = {'compute_extension:%sunshelve' % self.prefix: 'role:admin'}
|
||||||
common_policy.parse_rule('role:admin')}
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
policy.set_rules(rules)
|
|
||||||
|
|
||||||
self.assertRaises(exception.Forbidden, self.controller._unshelve,
|
self.assertRaises(exception.Forbidden, self.controller._unshelve,
|
||||||
self.req, str(uuid.uuid4()), {})
|
self.req, str(uuid.uuid4()), {})
|
||||||
@ -76,8 +74,8 @@ class ShelvePolicyTestV21(test.NoDBTestCase):
|
|||||||
|
|
||||||
def test_shelve_offload_restricted_by_role(self):
|
def test_shelve_offload_restricted_by_role(self):
|
||||||
rules = {'compute_extension:%s%s' % (self.prefix, self.offload):
|
rules = {'compute_extension:%s%s' % (self.prefix, self.offload):
|
||||||
common_policy.parse_rule('role:admin')}
|
'role:admin'}
|
||||||
policy.set_rules(rules)
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
|
|
||||||
self.assertRaises(exception.Forbidden,
|
self.assertRaises(exception.Forbidden,
|
||||||
self.controller._shelve_offload, self.req,
|
self.controller._shelve_offload, self.req,
|
||||||
@ -99,29 +97,26 @@ class ShelvePolicyTestV2(ShelvePolicyTestV21):
|
|||||||
|
|
||||||
# These 3 cases are covered in ShelvePolicyEnforcementV21
|
# These 3 cases are covered in ShelvePolicyEnforcementV21
|
||||||
def test_shelve_allowed(self):
|
def test_shelve_allowed(self):
|
||||||
rules = {'compute:get': common_policy.parse_rule(''),
|
rules = {'compute:get': '',
|
||||||
'compute_extension:%sshelve' % self.prefix:
|
'compute_extension:%sshelve' % self.prefix: ''}
|
||||||
common_policy.parse_rule('')}
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
policy.set_rules(rules)
|
|
||||||
self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get_by_uuid)
|
self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get_by_uuid)
|
||||||
self.assertRaises(exception.Forbidden, self.controller._shelve,
|
self.assertRaises(exception.Forbidden, self.controller._shelve,
|
||||||
self.req, str(uuid.uuid4()), {})
|
self.req, str(uuid.uuid4()), {})
|
||||||
|
|
||||||
def test_unshelve_allowed(self):
|
def test_unshelve_allowed(self):
|
||||||
rules = {'compute:get': common_policy.parse_rule(''),
|
rules = {'compute:get': '',
|
||||||
'compute_extension:%sunshelve' % self.prefix:
|
'compute_extension:%sunshelve' % self.prefix: ''}
|
||||||
common_policy.parse_rule('')}
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
policy.set_rules(rules)
|
|
||||||
|
|
||||||
self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get_by_uuid)
|
self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get_by_uuid)
|
||||||
self.assertRaises(exception.Forbidden, self.controller._unshelve,
|
self.assertRaises(exception.Forbidden, self.controller._unshelve,
|
||||||
self.req, str(uuid.uuid4()), {})
|
self.req, str(uuid.uuid4()), {})
|
||||||
|
|
||||||
def test_shelve_offload_allowed(self):
|
def test_shelve_offload_allowed(self):
|
||||||
rules = {'compute:get': common_policy.parse_rule(''),
|
rules = {'compute:get': '',
|
||||||
'compute_extension:%s%s' % (self.prefix, self.offload):
|
'compute_extension:%s%s' % (self.prefix, self.offload): ''}
|
||||||
common_policy.parse_rule('')}
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
policy.set_rules(rules)
|
|
||||||
|
|
||||||
self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get_by_uuid)
|
self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get_by_uuid)
|
||||||
self.assertRaises(exception.Forbidden,
|
self.assertRaises(exception.Forbidden,
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
from oslo_policy import policy as oslo_policy
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
from six.moves import range
|
from six.moves import range
|
||||||
import webob
|
import webob
|
||||||
@ -29,7 +30,6 @@ from nova import context
|
|||||||
from nova import db
|
from nova import db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import objects
|
from nova import objects
|
||||||
from nova.openstack.common import policy as common_policy
|
|
||||||
from nova import policy
|
from nova import policy
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.unit.api.openstack import fakes
|
from nova.tests.unit.api.openstack import fakes
|
||||||
@ -213,12 +213,10 @@ class SimpleTenantUsageTestV21(test.TestCase):
|
|||||||
req.environ['nova.context'] = self.alt_user_context
|
req.environ['nova.context'] = self.alt_user_context
|
||||||
|
|
||||||
rules = {
|
rules = {
|
||||||
self.policy_rule_prefix + ":show":
|
self.policy_rule_prefix + ":show": [
|
||||||
common_policy.parse_rule([
|
["role:admin"], ["project_id:%(project_id)s"]]
|
||||||
["role:admin"], ["project_id:%(project_id)s"]
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
policy.set_rules(rules)
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.assertRaises(exception.PolicyNotAuthorized,
|
self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
|
@ -19,6 +19,7 @@ import datetime
|
|||||||
import iso8601
|
import iso8601
|
||||||
import mock
|
import mock
|
||||||
from mox3 import mox
|
from mox3 import mox
|
||||||
|
from oslo_policy import policy as oslo_policy
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
@ -40,7 +41,6 @@ from nova import exception
|
|||||||
from nova import objects
|
from nova import objects
|
||||||
from nova.objects import base as obj_base
|
from nova.objects import base as obj_base
|
||||||
from nova.objects import quotas as quotas_obj
|
from nova.objects import quotas as quotas_obj
|
||||||
from nova.openstack.common import policy as common_policy
|
|
||||||
from nova import policy
|
from nova import policy
|
||||||
from nova import quota
|
from nova import quota
|
||||||
from nova import test
|
from nova import test
|
||||||
@ -2924,11 +2924,11 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||||||
def test_skip_policy_check(self, mock_create, mock_get_ins_by_filters,
|
def test_skip_policy_check(self, mock_create, mock_get_ins_by_filters,
|
||||||
mock_get, mock_pause, mock_action, mock_save):
|
mock_get, mock_pause, mock_action, mock_save):
|
||||||
policy.reset()
|
policy.reset()
|
||||||
rules = {'compute:pause': common_policy.parse_rule('!'),
|
rules = {'compute:pause': '!',
|
||||||
'compute:get': common_policy.parse_rule('!'),
|
'compute:get': '!',
|
||||||
'compute:get_all': common_policy.parse_rule('!'),
|
'compute:get_all': '!',
|
||||||
'compute:create': common_policy.parse_rule('!')}
|
'compute:create': '!'}
|
||||||
policy.set_rules(common_policy.Rules(rules))
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
instance = self._create_instance_obj()
|
instance = self._create_instance_obj()
|
||||||
mock_get.return_value = instance
|
mock_get.return_value = instance
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_config import fixture as config_fixture
|
from oslo_config import fixture as config_fixture
|
||||||
|
from oslo_policy import opts as policy_opts
|
||||||
|
|
||||||
from nova import config
|
from nova import config
|
||||||
from nova import ipv6
|
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('num_networks', 'nova.network.manager')
|
||||||
CONF.import_opt('floating_ip_dns_manager', 'nova.network.floating_ips')
|
CONF.import_opt('floating_ip_dns_manager', 'nova.network.floating_ips')
|
||||||
CONF.import_opt('instance_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('compute_driver', 'nova.virt.driver')
|
||||||
CONF.import_opt('api_paste_config', 'nova.wsgi')
|
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('enabled', True, 'osapi_v21')
|
||||||
self.conf.set_default('force_dhcp_release', False)
|
self.conf.set_default('force_dhcp_release', False)
|
||||||
self.conf.set_default('periodic_enable', False)
|
self.conf.set_default('periodic_enable', False)
|
||||||
|
policy_opts.set_defaults(self.conf)
|
||||||
self.addCleanup(utils.cleanup_dns_managers)
|
self.addCleanup(utils.cleanup_dns_managers)
|
||||||
self.addCleanup(ipv6.api.reset_backend)
|
self.addCleanup(ipv6.api.reset_backend)
|
||||||
|
@ -20,6 +20,7 @@ import uuid
|
|||||||
|
|
||||||
import mock
|
import mock
|
||||||
from mox3 import mox
|
from mox3 import mox
|
||||||
|
from oslo_policy import policy as oslo_policy
|
||||||
|
|
||||||
from nova.compute import flavors
|
from nova.compute import flavors
|
||||||
from nova import context
|
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.network import rpcapi as network_rpcapi
|
||||||
from nova import objects
|
from nova import objects
|
||||||
from nova.objects import fields
|
from nova.objects import fields
|
||||||
from nova.openstack.common import policy as common_policy
|
|
||||||
from nova import policy
|
from nova import policy
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.unit import fake_instance
|
from nova.tests.unit import fake_instance
|
||||||
@ -76,8 +76,8 @@ class NetworkPolicyTestCase(test.TestCase):
|
|||||||
|
|
||||||
def test_skip_policy(self):
|
def test_skip_policy(self):
|
||||||
policy.reset()
|
policy.reset()
|
||||||
rules = {'network:get_all': common_policy.parse_rule('!')}
|
rules = {'network:get_all': '!'}
|
||||||
policy.set_rules(common_policy.Rules(rules))
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
api = network.API()
|
api = network.API()
|
||||||
self.assertRaises(exception.PolicyNotAuthorized,
|
self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
api.get_all, self.context)
|
api.get_all, self.context)
|
||||||
|
@ -23,6 +23,7 @@ from mox3 import mox
|
|||||||
from neutronclient.common import exceptions
|
from neutronclient.common import exceptions
|
||||||
from neutronclient.v2_0 import client
|
from neutronclient.v2_0 import client
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
from oslo_policy import policy as oslo_policy
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
import six
|
import six
|
||||||
@ -35,7 +36,6 @@ from nova.network import model
|
|||||||
from nova.network.neutronv2 import api as neutronapi
|
from nova.network.neutronv2 import api as neutronapi
|
||||||
from nova.network.neutronv2 import constants
|
from nova.network.neutronv2 import constants
|
||||||
from nova import objects
|
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 manager as pci_manager
|
||||||
from nova.pci import whitelist as pci_whitelist
|
from nova.pci import whitelist as pci_whitelist
|
||||||
from nova import policy
|
from nova import policy
|
||||||
@ -1914,9 +1914,8 @@ class TestNeutronv2(TestNeutronv2Base):
|
|||||||
self._get_available_networks(prv_nets, pub_nets, req_ids)
|
self._get_available_networks(prv_nets, pub_nets, req_ids)
|
||||||
|
|
||||||
def test_get_available_networks_with_custom_policy(self):
|
def test_get_available_networks_with_custom_policy(self):
|
||||||
rules = {'network:attach_external_network':
|
rules = {'network:attach_external_network': ''}
|
||||||
common_policy.parse_rule('')}
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
policy.set_rules(rules)
|
|
||||||
req_ids = [net['id'] for net in self.nets5]
|
req_ids = [net['id'] for net in self.nets5]
|
||||||
self._get_available_networks(self.nets5, pub_nets=[], req_ids=req_ids)
|
self._get_available_networks(self.nets5, pub_nets=[], req_ids=req_ids)
|
||||||
|
|
||||||
|
@ -16,10 +16,10 @@ import os
|
|||||||
|
|
||||||
import fixtures
|
import fixtures
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
from oslo_policy import policy as oslo_policy
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from nova.openstack.common import policy as common_policy
|
|
||||||
from nova import paths
|
from nova import paths
|
||||||
import nova.policy
|
import nova.policy
|
||||||
from nova.tests.unit import fake_policy
|
from nova.tests.unit import fake_policy
|
||||||
@ -48,15 +48,14 @@ class RealPolicyFixture(fixtures.Fixture):
|
|||||||
# policy_file can be overridden by subclasses
|
# policy_file can be overridden by subclasses
|
||||||
self.policy_file = paths.state_path_def('etc/nova/policy.json')
|
self.policy_file = paths.state_path_def('etc/nova/policy.json')
|
||||||
self._prepare_policy()
|
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.reset()
|
||||||
nova.policy.init()
|
nova.policy.init()
|
||||||
self.addCleanup(nova.policy.reset)
|
self.addCleanup(nova.policy.reset)
|
||||||
|
|
||||||
def set_rules(self, rules):
|
def set_rules(self, rules):
|
||||||
policy = nova.policy._ENFORCER
|
policy = nova.policy._ENFORCER
|
||||||
policy.set_rules({k: common_policy.parse_rule(v)
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
for k, v in rules.items()})
|
|
||||||
|
|
||||||
|
|
||||||
class PolicyFixture(RealPolicyFixture):
|
class PolicyFixture(RealPolicyFixture):
|
||||||
@ -80,7 +79,7 @@ class PolicyFixture(RealPolicyFixture):
|
|||||||
'policy.json')
|
'policy.json')
|
||||||
with open(self.policy_file, 'w') as f:
|
with open(self.policy_file, 'w') as f:
|
||||||
f.write(fake_policy.policy_data)
|
f.write(fake_policy.policy_data)
|
||||||
CONF.set_override('policy_dirs', [])
|
CONF.set_override('policy_dirs', [], group='oslo_policy')
|
||||||
|
|
||||||
|
|
||||||
class RoleBasedPolicyFixture(RealPolicyFixture):
|
class RoleBasedPolicyFixture(RealPolicyFixture):
|
||||||
@ -101,7 +100,7 @@ class RoleBasedPolicyFixture(RealPolicyFixture):
|
|||||||
self.role = role
|
self.role = role
|
||||||
|
|
||||||
def _prepare_policy(self):
|
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
|
# Convert all actions to require specified role
|
||||||
for action, rule in six.iteritems(policy):
|
for action, rule in six.iteritems(policy):
|
||||||
|
@ -16,15 +16,13 @@
|
|||||||
"""Test of Policy Engine For Nova."""
|
"""Test of Policy Engine For Nova."""
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
from six.moves import StringIO
|
|
||||||
|
|
||||||
import mock
|
from oslo_policy import policy as oslo_policy
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
import six.moves.urllib.request as urlrequest
|
import requests_mock
|
||||||
|
|
||||||
from nova import context
|
from nova import context
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.openstack.common import policy as common_policy
|
|
||||||
from nova import policy
|
from nova import policy
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.unit import fake_policy
|
from nova.tests.unit import fake_policy
|
||||||
@ -42,7 +40,7 @@ class PolicyFileTestCase(test.NoDBTestCase):
|
|||||||
with utils.tempdir() as tmpdir:
|
with utils.tempdir() as tmpdir:
|
||||||
tmpfilename = os.path.join(tmpdir, 'policy')
|
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
|
# NOTE(uni): context construction invokes policy check to determin
|
||||||
# is_admin or not. As a side-effect, policy reset is needed here
|
# is_admin or not. As a side-effect, policy reset is needed here
|
||||||
@ -77,8 +75,7 @@ class PolicyTestCase(test.NoDBTestCase):
|
|||||||
}
|
}
|
||||||
policy.reset()
|
policy.reset()
|
||||||
policy.init()
|
policy.init()
|
||||||
policy.set_rules({k: common_policy.parse_rule(v)
|
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||||
for k, v in rules.items()})
|
|
||||||
self.context = context.RequestContext('fake', 'fake', roles=['member'])
|
self.context = context.RequestContext('fake', 'fake', roles=['member'])
|
||||||
self.target = {}
|
self.target = {}
|
||||||
|
|
||||||
@ -102,17 +99,19 @@ class PolicyTestCase(test.NoDBTestCase):
|
|||||||
result = policy.enforce(self.context, action, self.target)
|
result = policy.enforce(self.context, action, self.target)
|
||||||
self.assertEqual(result, True)
|
self.assertEqual(result, True)
|
||||||
|
|
||||||
@mock.patch.object(urlrequest, 'urlopen')
|
@requests_mock.mock()
|
||||||
def test_enforce_http_true(self, mock_urlrequest):
|
def test_enforce_http_true(self, req_mock):
|
||||||
mock_urlrequest.return_value = StringIO("True")
|
req_mock.post('http://www.example.com/',
|
||||||
|
text='True')
|
||||||
action = "example:get_http"
|
action = "example:get_http"
|
||||||
target = {}
|
target = {}
|
||||||
result = policy.enforce(self.context, action, target)
|
result = policy.enforce(self.context, action, target)
|
||||||
self.assertEqual(result, True)
|
self.assertEqual(result, True)
|
||||||
|
|
||||||
@mock.patch.object(urlrequest, 'urlopen')
|
@requests_mock.mock()
|
||||||
def test_enforce_http_false(self, mock_urlrequest):
|
def test_enforce_http_false(self, req_mock):
|
||||||
mock_urlrequest.return_value = StringIO("False")
|
req_mock.post('http://www.example.com/',
|
||||||
|
text='False')
|
||||||
action = "example:get_http"
|
action = "example:get_http"
|
||||||
target = {}
|
target = {}
|
||||||
self.assertRaises(exception.PolicyNotAuthorized, policy.enforce,
|
self.assertRaises(exception.PolicyNotAuthorized, policy.enforce,
|
||||||
@ -163,8 +162,7 @@ class DefaultPolicyTestCase(test.NoDBTestCase):
|
|||||||
|
|
||||||
def _set_rules(self, default_rule):
|
def _set_rules(self, default_rule):
|
||||||
policy.reset()
|
policy.reset()
|
||||||
rules = {k: common_policy.parse_rule(v)
|
rules = oslo_policy.Rules.from_dict(self.rules)
|
||||||
for k, v in self.rules.items()}
|
|
||||||
policy.init(rules=rules, default_rule=default_rule, use_conf=False)
|
policy.init(rules=rules, default_rule=default_rule, use_conf=False)
|
||||||
|
|
||||||
def test_policy_called(self):
|
def test_policy_called(self):
|
||||||
|
@ -13201,7 +13201,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
|
|||||||
_fake_network_info(self.stubs, 1))
|
_fake_network_info(self.stubs, 1))
|
||||||
|
|
||||||
def test_cleanup_resize_same_host(self):
|
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})
|
ins_ref = self._create_instance({'host': CONF.host})
|
||||||
|
|
||||||
def fake_os_path_exists(path):
|
def fake_os_path_exists(path):
|
||||||
@ -13222,7 +13222,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
|
|||||||
_fake_network_info(self.stubs, 1))
|
_fake_network_info(self.stubs, 1))
|
||||||
|
|
||||||
def test_cleanup_resize_not_same_host(self):
|
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
|
host = 'not' + CONF.host
|
||||||
ins_ref = self._create_instance({'host': host})
|
ins_ref = self._create_instance({'host': host})
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ oslo.utils>=2.8.0 # Apache-2.0
|
|||||||
oslo.db>=3.2.0 # Apache-2.0
|
oslo.db>=3.2.0 # Apache-2.0
|
||||||
oslo.rootwrap>=2.0.0 # Apache-2.0
|
oslo.rootwrap>=2.0.0 # Apache-2.0
|
||||||
oslo.messaging!=2.8.0,>2.6.1 # 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.i18n>=1.5.0 # Apache-2.0
|
||||||
oslo.service>=0.12.0 # Apache-2.0
|
oslo.service>=0.12.0 # Apache-2.0
|
||||||
rfc3986>=0.2.0 # Apache-2.0
|
rfc3986>=0.2.0 # Apache-2.0
|
||||||
|
@ -36,7 +36,6 @@ oslo.config.opts =
|
|||||||
nova.scheduler = nova.scheduler.opts:list_opts
|
nova.scheduler = nova.scheduler.opts:list_opts
|
||||||
nova.virt = nova.virt.opts:list_opts
|
nova.virt = nova.virt.opts:list_opts
|
||||||
nova.openstack.common.memorycache = nova.openstack.common.memorycache: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 =
|
nova.compute.monitors.cpu =
|
||||||
virt_driver = nova.compute.monitors.cpu.virt_driver:Monitor
|
virt_driver = nova.compute.monitors.cpu.virt_driver:Monitor
|
||||||
|
Loading…
Reference in New Issue
Block a user