]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Sync policy module from oslo-incubator
authorJay S. Bryant <jsbryant@us.ibm.com>
Mon, 16 Feb 2015 23:05:01 +0000 (17:05 -0600)
committerJay S. Bryant <jsbryant@us.ibm.com>
Tue, 17 Feb 2015 17:14:23 +0000 (11:14 -0600)
The policy module has not had a sync since back in
November.  There have been a number of changes that
should be pulled into Kilo.

Current HEAD in OSLO:
---------------------
commit 9bf01f9d98487cb13e3d95ad2a580fe8fc6f2479
Date:  Fri Feb 13 14:18:58 2015 -0500
Switch from oslo.config to oslo_config

Changes merged with this patch:
---------------------
2aacb111 Change oslo.config to oslo_config
2fbf5065 Remove oslo.log code and clean up versionutils API
262279b1 switch to oslo_serialization
07e9b32a Improving docstrings for policy API
e67f5cd0 Merge "Don't log missing policy.d as a warning"
99d991ce Merge "Fixed a problem with neutron http policy check"
b19af080 Don't log missing policy.d as a warning
2324c775 Add rule overwrite flag to Enforcer class
6166a960 Fixed a problem with neutron http policy check

Closes-bug: 1288178
Change-Id: I6987029b9c15f3d35fa591014859f5f96c98f3a3

cinder/openstack/common/policy.py

index cf45ffa1df3dbf374ba12c6bf28169b1f91fcdf1..7349ec949eaf0663bce8d42ff9ea4445a0d83693 100644 (file)
@@ -1,3 +1,5 @@
+# -*- coding: utf-8 -*-
+#
 # Copyright (c) 2012 OpenStack Foundation.
 # All Rights Reserved.
 #
@@ -22,22 +24,43 @@ 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.  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 code to perform that check.  However, conjunction
-operators are available, allowing for more expressiveness in crafting
-policies.
-
-As an example, take the following rule, expressed in the list-of-lists
-representation::
+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"]]
 
-In the policy language, this becomes::
+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)
 
@@ -46,26 +69,16 @@ policy rule::
 
     project_id:%(project_id)s and not role:dunce
 
-It is possible to perform policy checks on the following user
-attributes (obtained through the token): user_id, domain_id or
-project_id::
-
-    domain_id:<some_value>
-
 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
+    <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
-
-All these attributes (related to users, API calls, and context) can be
-checked against each other or against constants, be it literals (True,
-<a_number>) or strings.
+    <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
@@ -78,6 +91,7 @@ as it allows particular rules to be explicitly disabled.
 import abc
 import ast
 import copy
+import logging
 import os
 import re
 
@@ -88,8 +102,7 @@ import six.moves.urllib.parse as urlparse
 import six.moves.urllib.request as urlrequest
 
 from cinder.openstack.common import fileutils
-from cinder.openstack.common._i18n import _, _LE, _LW
-from cinder.openstack.common import log as logging
+from cinder.openstack.common._i18n import _, _LE, _LI
 
 
 policy_opts = [
@@ -199,16 +212,19 @@ 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.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.
@@ -240,7 +256,7 @@ class Enforcer(object):
 
         Policy file is cached and will be reloaded if modified.
 
-        :param force_reload: Whether to overwrite current rules.
+        :param force_reload: Whether to reload rules from config file.
         """
 
         if force_reload:
@@ -250,12 +266,13 @@ 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)
                 except cfg.ConfigFilesNotFoundError:
-                    LOG.warn(_LW("Can not find policy directory: %s"), path)
+                    LOG.info(_LI("Can not find policy directory: %s"), path)
                     continue
                 self._walk_through_policy_directory(path,
                                                     self._load_policy_file,
@@ -272,9 +289,9 @@ 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)
+                self.set_rules(rules, overwrite=overwrite, use_conf=True)
                 LOG.debug("Rules successfully reloaded")
 
     def _get_policy_path(self, path):
@@ -894,7 +911,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)