diff --git a/watcher/openstack/common/policy.py b/watcher/openstack/common/policy.py index db6d47e6c..150f4e6fd 100644 --- a/watcher/openstack/common/policy.py +++ b/watcher/openstack/common/policy.py @@ -77,18 +77,20 @@ 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 watcher.openstack.common import fileutils from watcher.openstack.common.gettextutils import _, _LE, _LW -from watcher.openstack.common import jsonutils -from watcher.openstack.common import log as logging policy_opts = [ @@ -113,6 +115,10 @@ 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): @@ -189,16 +195,20 @@ class Enforcer(object): :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): + default_rule=None, use_conf=True, overwrite=True): self.rules = Rules(rules, default_rule) self.default_rule = default_rule or CONF.policy_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. @@ -240,7 +250,8 @@ class Enforcer(object): if not self.policy_path: self.policy_path = self._get_policy_path(self.policy_file) - self._load_policy_file(self.policy_path, force_reload) + 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) @@ -261,10 +272,11 @@ class Enforcer(object): def _load_policy_file(self, path, force_reload, overwrite=True): reloaded, data = fileutils.read_cached_file( path, force_reload=force_reload) - if reloaded or not self.rules: + if reloaded or not self.rules or not overwrite: rules = Rules.load_json(data, self.default_rule) - self.set_rules(rules, overwrite) - LOG.debug("Rules successfully reloaded") + 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. @@ -812,7 +824,7 @@ def _parse_text_rule(rule): return state.result except ValueError: # Couldn't parse the rule - LOG.exception(_LE("Failed to understand rule %r") % rule) + LOG.exception(_LE("Failed to understand rule %s") % rule) # Fail closed return FalseCheck() @@ -883,7 +895,17 @@ class HttpCheck(Check): """ url = ('http:' + self.match) % target - data = {'target': jsonutils.dumps(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) @@ -916,7 +938,10 @@ class GenericCheck(Check): leftval = ast.literal_eval(self.kind) except ValueError: try: - leftval = creds[self.kind] + 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)