deckhand/deckhand/policy.py
Felipe Monteiro d2d2312af9 DECKHAND-66: Document substitution implementation
This PS implements documentation substitution and
the rendered-documents endpoint. Each time the
rendered-documents is queried, the documents for
the reqeust revision_id dynamically undergo
secret substitution.

All functional tests related to secret substitution
have been unskipped.

Deckhand currently does not real testing for
verifying that secret substitution works
for encrypted documents. This will only happen
when integration testing is added to Deckhand to
test its interaction with Keystone and Barbican.

Included in this PS:
  - basic implementation for secret substitution
  - introduction of jsonpath_ng for searching for and
    updating jsonpaths in documents
  - rendered-documents endpoint
  - unit tests
  - all relevant functional tests unskipped
  - additional bucket controller tests include RBAC
    tests and framework testing RBAC via unit tests

Change-Id: I86f269a5b616b518e5f742a4005891412226fe2a
2017-10-13 15:16:27 -04:00

132 lines
4.5 KiB
Python

# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import functools
import six
import falcon
from oslo_config import cfg
from oslo_log import log as logging
from oslo_policy import policy
from deckhand import errors
from deckhand import policies
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
_ENFORCER = None
def reset():
global _ENFORCER
if _ENFORCER:
_ENFORCER.clear()
_ENFORCER = None
def init(policy_file=None, rules=None, default_rule=None, use_conf=True):
"""Init an Enforcer class.
:param policy_file: Custom policy file to use, if none is specified,
``CONF.policy_file`` will be used.
:param rules: Default dictionary / Rules to use. It will be
considered just in the first instantiation.
: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 config file.
"""
global _ENFORCER
if not _ENFORCER:
_ENFORCER = policy.Enforcer(CONF,
policy_file=policy_file,
rules=rules,
default_rule=default_rule,
use_conf=use_conf)
register_rules(_ENFORCER)
def _do_enforce_rbac(action, context, do_raise=True):
init()
credentials = context.to_policy_values()
target = {'project_id': context.project_id,
'user_id': context.user_id}
exc = errors.PolicyNotAuthorized
try:
# `oslo.policy` supports both enforce and authorize. authorize is
# stricter because it'll raise an exception if the policy action is
# not found in the list of registered rules. This means that attempting
# to enforce anything not found in ``deckhand.policies`` will error out
# with a 'Policy not registered' message.
return _ENFORCER.authorize(
action, target, context.to_dict(), do_raise=do_raise,
exc=exc, action=action)
except policy.PolicyNotRegistered as e:
LOG.exception('Policy not registered.')
raise falcon.HTTPForbidden(description=six.text_type(e))
except Exception as e:
LOG.debug(
'Policy check for %(action)s failed with credentials '
'%(credentials)s',
{'action': action, 'credentials': credentials})
raise falcon.HTTPForbidden(description=six.text_type(e))
def authorize(action):
"""Verifies whether a policy action can be performed given the credentials
found in the falcon request context.
:param action: The policy action to enforce.
:returns: ``True`` if policy enforcement succeeded, else ``False``.
:raises: falcon.HTTPForbidden if policy enforcement failed or if the policy
action isn't registered under ``deckhand.policies``.
"""
def decorator(func):
@functools.wraps(func)
def handler(*args, **kwargs):
# args[1] is always the falcon Request object.
context = args[1].context
_do_enforce_rbac(action, context)
return func(*args, **kwargs)
return handler
return decorator
def conditional_authorize(action, context, do_raise=True):
"""Conditionally authorize a policy action.
:param action: The policy action to enforce.
:param context: The falcon request context object.
:param do_raise: Whether to raise the exception if policy enforcement
fails. ``True`` by default.
:raises: falcon.HTTPForbidden if policy enforcement failed or if the policy
action isn't registered under ``deckhand.policies``.
Example::
# If any requested documents' metadata.storagePolicy == 'cleartext'.
if cleartext_documents:
policy.conditional_authorize('deckhand:create_cleartext_documents',
req.context)
"""
return _do_enforce_rbac(action, context, do_raise=do_raise)
def register_rules(enforcer):
enforcer.register_defaults(policies.list_rules())