LOG = logging.getLogger(__name__)
_PATH_CACHE = dict()
_ARRAY_RE = re.compile(r'.*\[\d+\].*')
def to_camel_case(s):
def to_snake_case(name):
def _normalize_jsonpath(jsonpath):
def _jsonpath_parse_cache(jsonpath):
def jsonpath_parse(data, jsonpath, match_all=False):
def _populate_data_with_attributes(jsonpath, data):
def jsonpath_replace(data, value, jsonpath, pattern=None):
    def _do_replace():
def multisort(data, sort_by=None, order_by=None):
def deepfilter(dct, **filters):
    def _transform_filter_bool(filter_val): DOCUMENT_SANITY_CHECK_FAILURE = 'D001'
DOCUMENT_POST_RENDERING_FAILURE = 'D002'
class ValidationMessage(object):
    def __init__(self,
                 message='Document validation error.',
                 error=True,
                 name='Deckhand validation error',
                 level='Error',
                 doc_schema='',
                 doc_name='',
                 doc_layer='',
                 diagnostic=''):
    def format_message(self): LOG = logging.getLogger(__name__)
class BaseResource(object):
class DeckhandRequest(falcon.Request):
    def on_options(self, req, resp):
    def from_yaml(self, req, expect_list=True, allow_empty=False):
    def project_id(self):
    def user_id(self):
    def roles(self):
    def __repr__(self): class ViewBuilder(object):
    def _gen_url(self, revision):
def sanitize_params(allowed_params):
    def decorator(func):
        def wrapper(self, req, *func_args, **func_kwargs):
            def _enforce_query_filter_type(key, val):
            def _convert_to_dict(sanitized_params, filter_key, filter_val): class ViewBuilder(common.ViewBuilder):
    def list(self, documents): LOG = logging.getLogger(__name__)
CONF = cfg.CONF
_FACADE = None
_LOCK = threading.Lock()
                _FACADE = session.EngineFacade.from_config(
                    CONF, sqlite_fk=True)
def _create_facade_lazily():
def get_engine():
def get_session(autocommit=True, expire_on_commit=False):
def drop_db():
def setup_db(connection_string, create_tables=False):
def raw_query(query, **kwargs):
def require_unique_document_schema(schema=None):
    def decorator(f):
        def wrapper(bucket_name, documents, *args, **kwargs):
def documents_create(bucket_name, documents, validations=None,
                     session=None):
def _documents_create(bucket_name, documents, session=None):
    def _document_create(document):
def _fill_in_metadata_defaults(document):
def _make_hash(data):
def document_get(session=None, raw_dict=False, revision_id=None, **filters):
def document_get_all(session=None, raw_dict=False, revision_id=None,
                     **filters):
def bucket_get_or_create(bucket_name, session=None):
def revision_create(session=None):
def revision_get(revision_id=None, session=None):
def revision_get_latest(session=None):
def require_revision_exists(f):
    def wrapper(revision_id=None, *args, **kwargs):
def _update_revision_history(documents):
def revision_get_all(session=None, **filters):
def revision_delete_all():
def _exclude_deleted_documents(documents):
def _filter_revision_documents(documents, unique_only, **filters):
def revision_documents_get(revision_id=None, include_history=True,
                           unique_only=True, session=None, **filters):
def revision_diff(revision_id, comparison_revision_id):
    def _compare_buckets(b1, b2):
def revision_tag_create(revision_id, tag, data=None, session=None):
def revision_tag_get(revision_id, tag, session=None):
def revision_tag_get_all(revision_id, session=None):
def revision_tag_delete(revision_id, tag, session=None):
def revision_tag_delete_all(revision_id, session=None):
def revision_rollback(revision_id, latest_revision, session=None):
def _get_validation_policies_for_revision(revision_id, session=None):
def validation_create(revision_id, val_name, val_data, session=None):
def validation_get_all(revision_id, session=None):
def _check_validation_entries_against_validation_policies(
        revision_id, entries, val_name=None, session=None):
def validation_get_all_entries(revision_id, val_name=None, session=None):
def validation_get_entry(revision_id, val_name, entry_id, session=None): LOG = logging.getLogger(__name__)
_DEFAULT_SCHEMAS = {}
_SUPPORTED_SCHEMA_VERSIONS = ('v1',)
    _DEFAULT_SCHEMAS = {k: {} for k in _SUPPORTED_SCHEMA_VERSIONS}
def _get_schema_parts(document, schema_key='schema'):
def _get_schema_dir():
def _build_schema_map():
    def __init__(self):
    def matches(self, document):
    def validate(self, document):
    def __init__(self):
    def matches(self, document):
    def validate(self, document, **kwargs):
    def _build_schema_map(self, data_schemas):
    def __init__(self, data_schemas):
    def matches(self, document):
    def _generate_validation_error_output(self, schema, document, error,
                                          root_path):
    def _get_schemas(self, document):
    def validate(self, document, pre_validate=True):
    def __init__(self, documents, existing_data_schemas=None,
                 pre_validate=True):
    def _get_supported_schema_list(self):
    def _format_validation_results(self, results):
    def _validate_one(self, document):
    def validate_all(self):
class BaseValidator(object):
class GenericValidator(BaseValidator):
class DataSchemaValidator(GenericValidator):
class DocumentValidation(object): LOG = logging.getLogger(__name__)
    _SUPPORTED_METHODS = (_MERGE_ACTION, _REPLACE_ACTION, _DELETE_ACTION) = (
        'merge', 'replace', 'delete')
class DocumentLayering(object):
    def _calc_replacements_and_substitutions(
            self, substitution_sources):
        def _check_document_with_replacement_field_has_parent(
                parent_meta, parent, document):
        def _check_replacement_and_parent_same_schema_and_name(
                parent, document):
        def _check_non_replacement_and_parent_different_schema_and_name(
                parent, document):
        def _check_replacement_not_itself_replaced_by_another(src_ref):
    def _replace_older_parent_with_younger_parent(self, child, parent,
                                                  all_children):
    def _is_actual_child_document(self, document, potential_child):
    def _calc_document_children(self, document):
    def _calc_all_document_children(self):
    def _get_layering_order(self, layering_policy):
    def _topologically_sort_documents(self, substitution_sources):
        def _get_ancestor(doc, parent_meta):
    def _pre_validate_documents(self, documents):
    def __init__(self, documents, substitution_sources=None, validate=True,
                 fail_on_missing_sub_src=True):
    def _log_data_for_layering_failure(self, child, parent, action):
    def _log_data_for_substitution_failure(self, document):
    def _apply_action(self, action, child_data, overall_data):
    def render(self):
    def documents(self): LOG = logging.getLogger(__name__)
def get_version_from_request(req):
def format_error_resp(req,
                      resp,
                      status_code=falcon.HTTP_500,
                      message="",
                      reason=None,
                      error_type=None,
                      error_list=None,
                      info_list=None):
def default_exception_handler(ex, req, resp, params):
def default_exception_serializer(req, resp, exception):
    def __init__(self, message=None, **kwargs):
    def format_message(self):
class DeckhandException(Exception):
class InvalidDocumentFormat(DeckhandException):
class InvalidDocumentLayer(DeckhandException):
class InvalidDocumentParent(DeckhandException):
class IndeterminateDocumentParent(DeckhandException):
class SubstitutionDependencyCycle(DeckhandException):
class MissingDocumentKey(DeckhandException):
class MissingDocumentPattern(DeckhandException):
class InvalidDocumentReplacement(DeckhandException):
class UnsupportedActionMethod(DeckhandException):
class RevisionTagBadFormat(DeckhandException):
class SubstitutionSourceDataNotFound(DeckhandException):
class DocumentNotFound(DeckhandException):
class RevisionNotFound(DeckhandException):
class RevisionTagNotFound(DeckhandException):
class ValidationNotFound(DeckhandException):
class DuplicateDocumentExists(DeckhandException):
class SingletonDocumentConflict(DeckhandException):
class LayeringPolicyNotFound(DeckhandException):
class SubstitutionSourceNotFound(DeckhandException):
class PolicyNotAuthorized(DeckhandException):
class BarbicanException(DeckhandException):
class UnknownSubstitutionError(DeckhandException): CONF = cfg.CONF
LOG = logging.getLogger(__name__)
_ENFORCER = None
        _ENFORCER = None
        _ENFORCER = policy.Enforcer(CONF,
                                    policy_file=policy_file,
                                    rules=rules,
                                    default_rule=default_rule,
                                    use_conf=use_conf)
def reset():
def init(policy_file=None, rules=None, default_rule=None, use_conf=True):
def _do_enforce_rbac(action, context, do_raise=True):
def authorize(action):
    def decorator(func):
        def handler(*args, **kwargs):
def conditional_authorize(action, context, do_raise=True):
def register_rules(enforcer): DOCUMENT_SCHEMA_TYPES = (
    CERTIFICATE_AUTHORITY_SCHEMA,
    CERTIFICATE_KEY_AUTHORITY_SCHEMA,
    CERTIFICATE_SCHEMA,
    CERTIFICATE_KEY_SCHEMA,
    PRIVATE_KEY_SCHEMA,
    PUBLIC_KEY_SCHEMA,
    PASSPHRASE_SCHEMA,
    DATA_SCHEMA_SCHEMA,
    LAYERING_POLICY_SCHEMA,
    PASSPHRASE_SCHEMA,
    VALIDATION_POLICY_SCHEMA,
) = (
    'deckhand/CertificateAuthority',
    'deckhand/CertificateAuthorityKey',
    'deckhand/Certificate',
    'deckhand/CertificateKey',
    'deckhand/PrivateKey',
    'deckhand/PublicKey',
    'deckhand/Passphrase',
    'deckhand/DataSchema',
    'deckhand/LayeringPolicy',
    'deckhand/Passphrase',
    'deckhand/ValidationPolicy',
)
DOCUMENT_SECRET_TYPES = (
    CERTIFICATE_AUTHORITY_SCHEMA,
    CERTIFICATE_KEY_AUTHORITY_SCHEMA,
    CERTIFICATE_KEY_SCHEMA,
    CERTIFICATE_SCHEMA,
    PRIVATE_KEY_SCHEMA,
    PUBLIC_KEY_SCHEMA,
    PASSPHRASE_SCHEMA
) = (
    'deckhand/CertificateAuthority',
    'deckhand/CertificateAuthorityKey',
    'deckhand/Certificate',
    'deckhand/CertificateKey',
    'deckhand/PrivateKey',
    'deckhand/PublicKey',
    'deckhand/Passphrase'
)
DECKHAND_VALIDATION_TYPES = (
    DECKHAND_SCHEMA_VALIDATION,
) = (
    'deckhand-schema-validation',
)
ENCRYPTION_TYPES = (
    CLEARTEXT,
    ENCRYPTED
) = (
    'cleartext',
    'encrypted'
)
import falcon
import six
from oslo_log import log as logging
from oslo_utils import excutils
from deckhand.common import utils
from deckhand.common import validation_message as vm
from deckhand.control import base as api_base
from deckhand.control import common
from deckhand.control.views import document as document_view
from deckhand.db.sqlalchemy import api as db_api
from deckhand.engine import document_validation
from deckhand.engine import layering
from deckhand import errors
from deckhand import policy
from deckhand import types
            raise falcon.HTTPNotFound(description=e.format_message())

        # Sorts by creation date by default.
        documents = utils.multisort(documents, sort_by, order_by)
        if limit is not None:
            documents = documents[:limit]

        resp.status = falcon.HTTP_200
        resp.body = self.view_builder.list(documents)


class RenderedDocumentsResource(api_base.BaseResource):
    """API resource for realizing rendered documents endpoint.

    Rendered documents are also revision documents, but unlike revision
    documents, they are finalized documents, having undergone secret
    substitution and document layering.

    Returns a multi-document YAML response containing all the documents
    matching the filters specified via query string parameters. Returned
    documents will have secrets substituted into them and be layered with
    other documents in the revision, in accordance with the ``LayeringPolicy``
    that currently exists in the system.
    """

    view_builder = document_view.ViewBuilder()

    @policy.authorize('deckhand:list_cleartext_documents')
    @common.sanitize_params([
        'schema', 'metadata.name', 'metadata.layeringDefinition.layer',
        'metadata.label', 'status.bucket', 'order', 'sort', 'limit'])
    def on_get(self, req, resp, sanitized_params, revision_id):
        include_encrypted = policy.conditional_authorize(
            'deckhand:list_encrypted_documents', req.context, do_raise=False)
        filters = {
            'metadata.storagePolicy': ['cleartext'],
            'deleted': False
        }
        if include_encrypted:
            filters['metadata.storagePolicy'].append('encrypted')

        documents = self._retrieve_documents_for_rendering(revision_id,
                                                           **filters)

        try:
            # NOTE(fmontei): `validate` is False because documents have already
            # been pre-validated during ingestion. Documents are post-validated
            # below, regardless.
            document_layering = layering.DocumentLayering(
                documents, validate=False)
            rendered_documents = document_layering.render()
        except (errors.InvalidDocumentLayer,
                errors.InvalidDocumentParent,
                errors.InvalidDocumentReplacement,
                errors.IndeterminateDocumentParent,
                errors.LayeringPolicyNotFound,
                errors.MissingDocumentKey,
                errors.SubstitutionSourceDataNotFound,
                errors.SubstitutionSourceNotFound,
                errors.UnknownSubstitutionError,
                errors.UnsupportedActionMethod) as e:
            with excutils.save_and_reraise_exception():
                LOG.exception(e.format_message())

        # Filters to be applied post-rendering, because many documents are
        # involved in rendering. User filters can only be applied once all
        # documents have been rendered. Note that `layering` module only
        # returns concrete documents, so no filtering for that is needed here.
        order_by = sanitized_params.pop('order', None)
        sort_by = sanitized_params.pop('sort', None)
        limit = sanitized_params.pop('limit', None)
        user_filters = sanitized_params.copy()

        rendered_documents = [
            d for d in rendered_documents if utils.deepfilter(
                d, **user_filters)]

        if sort_by:
            rendered_documents = utils.multisort(
                rendered_documents, sort_by, order_by)

        if limit is not None:
            rendered_documents = rendered_documents[:limit]

        resp.status = falcon.HTTP_200
        resp.body = self.view_builder.list(rendered_documents)
        self._post_validate(rendered_documents)

    def _retrieve_documents_for_rendering(self, revision_id, **filters):
        """Retrieve all necessary documents needed for rendering. If a layering
        policy isn't found in the current revision, retrieve it in a subsequent
        call and add it to the list of documents.
        """
        try:
            documents = db_api.revision_documents_get(revision_id, **filters)
        except errors.RevisionNotFound as e:
            LOG.exception(six.text_type(e))
            raise falcon.HTTPNotFound(description=e.format_message())

        if not any([d['schema'].startswith(types.LAYERING_POLICY_SCHEMA)
                    for d in documents]):
            try:
                layering_policy_filters = {
                    'deleted': False,
                    'schema': types.LAYERING_POLICY_SCHEMA
                }
                layering_policy = db_api.document_get(
                    **layering_policy_filters)
            except errors.DocumentNotFound as e:
                LOG.exception(e.format_message())
            else:
                documents.append(layering_policy)

        return documents

    def _post_validate(self, rendered_documents):
        # Perform schema validation post-rendering to ensure that rendering
        # and substitution didn't break anything.
        data_schemas = db_api.revision_documents_get(
            schema=types.DATA_SCHEMA_SCHEMA, deleted=False)

        doc_validator = document_validation.DocumentValidation(