Use openstack-common's policy module
Reworks glance to use the new policy module in openstack-common. Change-Id: I6012c71a16d83762535e7490d8f5bc5102160fc0
This commit is contained in:
parent
fc758a46e7
commit
c07be927fb
@ -22,8 +22,8 @@ import logging
|
|||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
from glance.common import exception
|
from glance.common import exception
|
||||||
from glance.common import policy
|
|
||||||
from glance.openstack.common import cfg
|
from glance.openstack.common import cfg
|
||||||
|
from glance.openstack.common import policy
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -99,7 +99,5 @@ class Enforcer(object):
|
|||||||
'tenant': context.tenant,
|
'tenant': context.tenant,
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
policy.enforce(match_list, target, credentials,
|
||||||
policy.enforce(match_list, target, credentials)
|
exception.Forbidden, action=action)
|
||||||
except policy.NotAuthorized:
|
|
||||||
raise exception.Forbidden(action=action)
|
|
||||||
|
@ -18,10 +18,12 @@
|
|||||||
"""Common Policy Engine Implementation"""
|
"""Common Policy Engine Implementation"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
|
import urllib
|
||||||
|
import urllib2
|
||||||
|
|
||||||
|
|
||||||
class NotAuthorized(Exception):
|
LOG = logging.getLogger(__name__)
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
_BRAIN = None
|
_BRAIN = None
|
||||||
@ -43,30 +45,45 @@ def reset():
|
|||||||
_BRAIN = None
|
_BRAIN = None
|
||||||
|
|
||||||
|
|
||||||
def enforce(match_list, target_dict, credentials_dict):
|
def enforce(match_list, target_dict, credentials_dict, exc=None,
|
||||||
|
*args, **kwargs):
|
||||||
"""Enforces authorization of some rules against credentials.
|
"""Enforces authorization of some rules against credentials.
|
||||||
|
|
||||||
:param match_list: nested tuples of data to match against
|
:param match_list: nested tuples of data to match against
|
||||||
|
|
||||||
The basic brain supports three types of match lists:
|
The basic brain supports three types of match lists:
|
||||||
|
|
||||||
1) rules
|
1) rules
|
||||||
looks like: ('rule:compute:get_instance',)
|
|
||||||
|
looks like: ``('rule:compute:get_instance',)``
|
||||||
|
|
||||||
Retrieves the named rule from the rules dict and recursively
|
Retrieves the named rule from the rules dict and recursively
|
||||||
checks against the contents of the rule.
|
checks against the contents of the rule.
|
||||||
|
|
||||||
2) roles
|
2) roles
|
||||||
looks like: ('role:compute:admin',)
|
|
||||||
|
looks like: ``('role:compute:admin',)``
|
||||||
|
|
||||||
Matches if the specified role is in credentials_dict['roles'].
|
Matches if the specified role is in credentials_dict['roles'].
|
||||||
|
|
||||||
3) generic
|
3) generic
|
||||||
('tenant_id:%(tenant_id)s',)
|
|
||||||
|
looks like: ``('tenant_id:%(tenant_id)s',)``
|
||||||
|
|
||||||
Substitutes values from the target dict into the match using
|
Substitutes values from the target dict into the match using
|
||||||
the % operator and matches them against the creds dict.
|
the % operator and matches them against the creds dict.
|
||||||
|
|
||||||
Combining rules:
|
Combining rules:
|
||||||
The brain returns True if any of the outer tuple of rules match
|
|
||||||
and also True if all of the inner tuples match. You can use this to
|
The brain returns True if any of the outer tuple of rules
|
||||||
perform simple boolean logic. For example, the following rule would
|
match and also True if all of the inner tuples match. You
|
||||||
return True if the creds contain the role 'admin' OR the if the
|
can use this to perform simple boolean logic. For
|
||||||
tenant_id matches the target dict AND the the creds contains the
|
example, the following rule would return True if the creds
|
||||||
role 'compute_sysadmin':
|
contain the role 'admin' OR the if the tenant_id matches
|
||||||
|
the target dict AND the the creds contains the role
|
||||||
|
'compute_sysadmin':
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
{
|
{
|
||||||
"rule:combined": (
|
"rule:combined": (
|
||||||
@ -75,28 +92,39 @@ def enforce(match_list, target_dict, credentials_dict):
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Note that rule and role are reserved words in the credentials match, so
|
Note that rule and role are reserved words in the credentials match, so
|
||||||
you can't match against properties with those names. Custom brains may
|
you can't match against properties with those names. Custom brains may
|
||||||
also add new reserved words. For example, the HttpBrain adds http as a
|
also add new reserved words. For example, the HttpBrain adds http as a
|
||||||
reserved word.
|
reserved word.
|
||||||
|
|
||||||
:param target_dict: dict of object properties
|
:param target_dict: dict of object properties
|
||||||
|
|
||||||
Target dicts contain as much information as we can about the object being
|
Target dicts contain as much information as we can about the object being
|
||||||
operated on.
|
operated on.
|
||||||
|
|
||||||
:param credentials_dict: dict of actor properties
|
:param credentials_dict: dict of actor properties
|
||||||
|
|
||||||
Credentials dicts contain as much information as we can about the user
|
Credentials dicts contain as much information as we can about the user
|
||||||
performing the action.
|
performing the action.
|
||||||
|
|
||||||
:raises NotAuthorized if the check fails
|
:param exc: exception to raise
|
||||||
|
|
||||||
|
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 exc is not provided, returns
|
||||||
|
False.
|
||||||
|
|
||||||
|
:return: True if the policy allows the action
|
||||||
|
:return: False if the policy does not allow the action and exc is not set
|
||||||
"""
|
"""
|
||||||
global _BRAIN
|
global _BRAIN
|
||||||
if not _BRAIN:
|
if not _BRAIN:
|
||||||
_BRAIN = Brain()
|
_BRAIN = Brain()
|
||||||
if not _BRAIN.check(match_list, target_dict, credentials_dict):
|
if not _BRAIN.check(match_list, target_dict, credentials_dict):
|
||||||
raise NotAuthorized()
|
if exc:
|
||||||
|
raise exc(*args, **kwargs)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class Brain(object):
|
class Brain(object):
|
||||||
@ -115,7 +143,12 @@ class Brain(object):
|
|||||||
self.rules[key] = match
|
self.rules[key] = match
|
||||||
|
|
||||||
def _check(self, match, target_dict, cred_dict):
|
def _check(self, match, target_dict, cred_dict):
|
||||||
|
try:
|
||||||
match_kind, match_value = match.split(':', 1)
|
match_kind, match_value = match.split(':', 1)
|
||||||
|
except Exception:
|
||||||
|
LOG.exception(_("Failed to understand rule %(match)r") % locals())
|
||||||
|
# If the rule is invalid, fail closed
|
||||||
|
return False
|
||||||
try:
|
try:
|
||||||
f = getattr(self, '_check_%s' % match_kind)
|
f = getattr(self, '_check_%s' % match_kind)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -162,7 +195,7 @@ class Brain(object):
|
|||||||
|
|
||||||
def _check_role(self, match, target_dict, cred_dict):
|
def _check_role(self, match, target_dict, cred_dict):
|
||||||
"""Check that there is a matching role in the cred dict."""
|
"""Check that there is a matching role in the cred dict."""
|
||||||
return match in cred_dict['roles']
|
return match.lower() in [x.lower() for x in cred_dict['roles']]
|
||||||
|
|
||||||
def _check_generic(self, match, target_dict, cred_dict):
|
def _check_generic(self, match, target_dict, cred_dict):
|
||||||
"""Check an individual match.
|
"""Check an individual match.
|
||||||
@ -180,3 +213,26 @@ class Brain(object):
|
|||||||
if key in cred_dict:
|
if key in cred_dict:
|
||||||
return value == cred_dict[key]
|
return value == cred_dict[key]
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class HttpBrain(Brain):
|
||||||
|
"""A brain that can check external urls for policy.
|
||||||
|
|
||||||
|
Posts json blobs for target and credentials.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _check_http(self, match, target_dict, cred_dict):
|
||||||
|
"""Check http: rules by calling to a remote server.
|
||||||
|
|
||||||
|
This example implementation simply verifies that the response is
|
||||||
|
exactly 'True'. A custom brain using response codes could easily
|
||||||
|
be implemented.
|
||||||
|
|
||||||
|
"""
|
||||||
|
url = match % target_dict
|
||||||
|
data = {'target': json.dumps(target_dict),
|
||||||
|
'credentials': json.dumps(cred_dict)}
|
||||||
|
post_data = urllib.urlencode(data)
|
||||||
|
f = urllib2.urlopen(url, post_data)
|
||||||
|
return f.read() == "True"
|
@ -1,7 +1,7 @@
|
|||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
|
|
||||||
# The list of modules to copy from openstack-common
|
# The list of modules to copy from openstack-common
|
||||||
modules=cfg,importutils,iniparser,setup,timeutils
|
modules=cfg,importutils,iniparser,policy,setup,timeutils
|
||||||
|
|
||||||
# The base module to hold the copy of openstack.common
|
# The base module to hold the copy of openstack.common
|
||||||
base=glance
|
base=glance
|
||||||
|
Loading…
Reference in New Issue
Block a user