
Fixes existing pep8 errors and reenables the corresponding tox rules Change-Id: I4168a90c40173e4c35c9d75030cd592ace657508
183 lines
6.8 KiB
Python
Executable File
183 lines
6.8 KiB
Python
Executable File
"""Policy Engine For ORM."""
|
|
|
|
import logging
|
|
|
|
from keystone_utils import tokens
|
|
from orm_common.utils import api_error_utils as err_utils
|
|
from orm_common.utils import dictator
|
|
import qolicy
|
|
from wsme.exc import ClientSideError
|
|
|
|
logger = logging.getLogger(__name__)
|
|
_ENFORCER = None
|
|
_POLICY_FILE = None
|
|
_TOKEN_CONF = None
|
|
|
|
|
|
def reset():
|
|
global _ENFORCER
|
|
if _ENFORCER:
|
|
_ENFORCER.clear()
|
|
_ENFORCER = None
|
|
|
|
global _POLICY_FILE
|
|
if _POLICY_FILE:
|
|
_POLICY_FILE = None
|
|
|
|
|
|
class EnforcerError(Exception):
|
|
"""An exception that receives *args and **kwargs, necessary for
|
|
Enforcer.enforce().
|
|
"""
|
|
def __init__(self, *args, **kwargs):
|
|
super(EnforcerError, self).__init__()
|
|
|
|
|
|
def _get_rules_from_file(path):
|
|
logger.debug('Reading policy file: {}'.format(path))
|
|
|
|
return qolicy.Rules.load_json(open(path, 'r').read(), 'default')
|
|
|
|
|
|
def init(policy_file, token_conf, default_rule=None):
|
|
"""Init an Enforcer class.
|
|
|
|
:param policy_file: Custom policy file to use.
|
|
:param default_rule: Default rule to use
|
|
:param token_conf: The Keystone utils token configuration
|
|
"""
|
|
logger.info('Initializing policy enforcer...')
|
|
|
|
global _ENFORCER
|
|
global _POLICY_FILE
|
|
global _TOKEN_CONF
|
|
if not _ENFORCER:
|
|
loaded_rules = _get_rules_from_file(policy_file)
|
|
_POLICY_FILE = policy_file
|
|
_TOKEN_CONF = token_conf
|
|
_ENFORCER = qolicy.Enforcer(None,
|
|
policy_file=None,
|
|
rules=loaded_rules,
|
|
default_rule=default_rule,
|
|
use_conf=False)
|
|
|
|
|
|
def reset_rules(overwrite=True, use_conf=False):
|
|
"""Reset rules 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 config file.
|
|
"""
|
|
if not _POLICY_FILE:
|
|
message = 'Policy file not set (did you call policy.init?)'
|
|
logger.error(message)
|
|
raise ValueError(message)
|
|
_ENFORCER.set_rules(_get_rules_from_file(_POLICY_FILE),
|
|
overwrite, use_conf)
|
|
|
|
|
|
def enforce(action, token, user, lcp_id=None, keystone_ep=None,
|
|
do_raise=True):
|
|
"""Verifies that the action is valid on the target in this context.
|
|
|
|
:param action: string representing the action to be checked
|
|
this should be colon separated for clarity.
|
|
i.e. ``compute:create_instance``,
|
|
``compute:attach_volume``,
|
|
``volume:attach_volume``
|
|
:param token: The token to validate
|
|
:param lcp_id: The ID of the LCP associated with the Keystone instance
|
|
with which the token was created
|
|
:param keystone_ep: The Keystone endpoint, in case we already have it
|
|
:param do_raise: if True (the default), raises Unauthorized (401);
|
|
if False, returns False
|
|
|
|
:raises EnforcerError if verification fails and do_raise is True.
|
|
|
|
:return: returns a non-False value (not necessarily "True") if
|
|
authorized, and the exact value False if not authorized and
|
|
do_raise is False.
|
|
"""
|
|
logger.debug('Enforcing policy - action: {}, token: {}, lcp_id: {}, '
|
|
'keystone_ep: {}'.format(action, token, lcp_id, keystone_ep))
|
|
# Re-read the rules, in case the policy file has changed
|
|
reset_rules()
|
|
|
|
# May raise EnforcerError, we'll let it propagate
|
|
result = _ENFORCER.enforce(action, {}, user, do_raise=do_raise,
|
|
exc=EnforcerError, action=action)
|
|
|
|
return result
|
|
|
|
|
|
def _is_authorization_enabled(app_conf):
|
|
return app_conf.authentication.enabled
|
|
|
|
|
|
def authorize(action, request, app_conf, keystone_ep=None):
|
|
"""Authorize a request.
|
|
|
|
:param action: The requested action, in the policy.json syntax
|
|
:param request: Pecan request object
|
|
:param app_conf: Application configuration
|
|
:param keystone_ep: Keystone endpoint, in case we already have it
|
|
|
|
:raises Unauthorized (401) in case anything fails in the authorization
|
|
process
|
|
"""
|
|
logger.info('Authorize...start')
|
|
|
|
token_to_validate = request.headers.get('X-Auth-Token')
|
|
lcp_id = request.headers.get('X-Auth-Region')
|
|
try:
|
|
if _is_authorization_enabled(app_conf):
|
|
try:
|
|
# Set the service name for Nagios codes
|
|
dictator.soft_set('service_name', app_conf.server.name.upper())
|
|
|
|
user = tokens.get_token_user(token_to_validate, _TOKEN_CONF,
|
|
lcp_id, keystone_ep)
|
|
request.headers['X-RANGER-Client'] = user.user['name']
|
|
request.headers['X-RANGER-Owner'] = user.tenant['id']
|
|
except Exception:
|
|
user = None
|
|
request.headers['X-RANGER-Client'] = 'NA'
|
|
logger.exception(
|
|
"policy - Failed to get_token_user, using user={}".format(
|
|
user))
|
|
|
|
if token_to_validate is not None and lcp_id is not None and str(token_to_validate).strip() != '' and str(lcp_id).strip() != '':
|
|
logger.debug('Authorization: enforcing policy on token=[{}], lcp_id=[{}]'.format(token_to_validate, lcp_id))
|
|
enforce(action, token_to_validate, user, lcp_id, keystone_ep)
|
|
is_permitted = True
|
|
logger.debug('Authorization: policy check passed')
|
|
else:
|
|
logger.debug('Token=[{}] and/or Region=[{}] are empty/none.'.format(token_to_validate, lcp_id))
|
|
logger.info(
|
|
'INFO|CON{}AUTH004|One or more of the authentication headers are missing'.format(
|
|
dictator.get('service_name', 'ORM')))
|
|
# Enforce anyway, in case the policy for this is to always
|
|
# allow any user to perform this operation
|
|
enforce(action, token_to_validate, user)
|
|
is_permitted = True
|
|
else:
|
|
logger.debug('The authentication service is disabled. No authentication is needed.')
|
|
is_permitted = True
|
|
except ClientSideError as e:
|
|
logger.error('Fail to validate request. due to {}.'.format(e.message))
|
|
raise err_utils.get_error('N/A', status_code=e.code)
|
|
except EnforcerError:
|
|
logger.error('The token is unauthorized according to the policy')
|
|
is_permitted = False
|
|
except Exception as e:
|
|
msg = 'Fail to validate request. due to {}.'.format(e.message)
|
|
logger.error(msg)
|
|
logger.exception(e)
|
|
is_permitted = False
|
|
|
|
logger.info('Authorize...end')
|
|
if not is_permitted:
|
|
raise err_utils.get_error('N/A', status_code=401)
|