jh629g 23b9d62dc5 Persist User Domain for role validation in Heat
Heat must have the domain of the user
in order to validate the user for role
assignment. Regardless of if roles are being
changed or assigned, heat always requires
and validates this data when heat receives
a project template. This work persists the user
domain when calls are made to CMS so that
RDS can add this data to project heat templates.

Change-Id: I5a8e72241e68dac730c3522d820a17d926fa3be8
2020-08-03 14:49:15 +00:00

187 lines
7.1 KiB
Python
Executable File

"""Policy Engine For ORM."""
import logging
from . import qolicy
from orm.common.client.keystone.keystone_utils import tokens
from orm.common.orm_common.utils import api_error_utils as err_utils
from orm.common.orm_common.utils import dictator
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')
keystone_ep = keystone_ep if keystone_ep else (
request.headers.get('Keystone-Endpoint'))
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']
request.headers['Keystone-Endpoint'] = user.auth_url
request.headers['User-Domain'] = user.domain
keystone_ep = user.auth_url
except Exception as ex:
user = None
request.headers['X-RANGER-Client'] = 'NA'
logger.exception(
"policy - Failed to get_token, using endpoint={}".format(
keystone_ep))
raise err_utils.get_error('N/A', status_code=401)
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(str(e)))
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:
logger.error('Fail to validate request. due to {}.'.format(str(e)))
is_permitted = False
logger.info('Authorize...end')
if not is_permitted:
raise err_utils.get_error('N/A', status_code=401)