Esempio n. 1
0
    def on_put(self, req, resp, bucket_name=None):
        data = self.from_yaml(req, expect_list=True, allow_empty=True)
        documents = document_wrapper.DocumentDict.from_list(data)

        # NOTE: Must validate documents before doing policy enforcement,
        # because we expect certain formatting of the documents while doing
        # policy enforcement. If any documents fail basic schema validaiton
        # raise an exception immediately.
        data_schemas = db_api.revision_documents_get(
            schema=types.DATA_SCHEMA_SCHEMA, deleted=False)
        try:
            doc_validator = document_validation.DocumentValidation(
                documents, data_schemas, pre_validate=True)
            validations = doc_validator.validate_all()
        except deckhand_errors.InvalidDocumentFormat as e:
            with excutils.save_and_reraise_exception():
                LOG.exception(e.format_message())

        for document in documents:
            if secrets_manager.SecretsManager.requires_encryption(document):
                policy.conditional_authorize(
                    'deckhand:create_encrypted_documents', req.context)
                break

        documents = self._encrypt_secret_documents(documents)

        created_documents = self._create_revision_documents(
            bucket_name, documents)

        if created_documents:
            revision_id = created_documents[0]['revision_id']
            self._create_revision_validations(revision_id, validations)

        resp.body = self.view_builder.list(created_documents)
        resp.status = falcon.HTTP_200
Esempio n. 2
0
    def on_get(self, req, resp, sanitized_params, revision_id):
        """Returns all documents for a `revision_id`.

        Returns a multi-document YAML response containing all the documents
        matching the filters specified via query string parameters. Returned
        documents will be as originally posted with no substitutions or
        layering applied.
        """
        include_encrypted = policy.conditional_authorize(
            'deckhand:list_encrypted_documents', req.context, do_raise=False)

        order_by = sanitized_params.pop('order', None)
        sort_by = sanitized_params.pop('sort', None)

        filters = sanitized_params.copy()
        filters['metadata.storagePolicy'] = ['cleartext']
        if include_encrypted:
            filters['metadata.storagePolicy'].append('encrypted')
        filters['deleted'] = False  # Never return deleted documents to user.

        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())

        # Sorts by creation date by default.
        documents = utils.multisort(documents, sort_by, order_by)

        resp.status = falcon.HTTP_200
        resp.body = self.view_builder.list(documents)
Esempio n. 3
0
    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 = sanitized_params.copy()
        filters['metadata.storagePolicy'] = ['cleartext']
        if include_encrypted:
            filters['metadata.storagePolicy'].append('encrypted')

        try:
            documents = db_api.revision_get_documents(revision_id, **filters)
        except errors.RevisionNotFound as e:
            LOG.exception(six.text_type(e))
            raise falcon.HTTPNotFound(description=e.format_message())

        # TODO(fmontei): Currently the only phase of rendering that is
        # performed is secret substitution, which can be done in any randomized
        # order. However, secret substitution logic will have to be moved into
        # a separate module that handles layering alongside substitution once
        # layering has been fully integrated into this endpoint.
        secrets_substitution = secrets_manager.SecretsSubstitution(documents)
        try:
            rendered_documents = secrets_substitution.substitute_all()
        except errors.DocumentNotFound as e:
            LOG.error('Failed to render the documents because a secret '
                      'document could not be found.')
            LOG.exception(six.text_type(e))
            raise falcon.HTTPNotFound(description=e.format_message())

        resp.status = falcon.HTTP_200
        resp.body = self.view_builder.list(rendered_documents)
    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')

        rendered_documents = common.get_rendered_docs(revision_id, **filters)

        # 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
        self._post_validate(rendered_documents)
        resp.body = self.view_builder.list(rendered_documents)
Esempio n. 5
0
    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)
Esempio n. 6
0
    def on_post(self, req, resp, revision_id):
        try:
            latest_revision = db_api.revision_get_latest()
        except errors.RevisionNotFound as e:
            raise falcon.HTTPNotFound(description=e.format_message())

        for document in latest_revision['documents']:
            if document['metadata'].get('storagePolicy') == 'encrypted':
                policy.conditional_authorize(
                    'deckhand:create_encrypted_documents', req.context)
                break

        try:
            rollback_revision = db_api.revision_rollback(
                revision_id, latest_revision)
        except errors.InvalidRollback as e:
            raise falcon.HTTPBadRequest(description=e.format_message())

        revision_resp = self.view_builder.show(rollback_revision)
        resp.status = falcon.HTTP_201
        resp.body = revision_resp
Esempio n. 7
0
    def on_get(self, req, resp, 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')

        cleartext_secrets = req.get_param_as_bool('cleartext-secrets')
        if cleartext_secrets is None:
            cleartext_secrets = True
        req.params.pop('cleartext-secrets', None)
        rendered_documents, cache_hit = common.get_rendered_docs(
            revision_id, cleartext_secrets, **filters)

        # If the rendered documents result set is cached, then post-validation
        # for that result set has already been performed successfully, so it
        # can be safely skipped over as an optimization.
        if not cache_hit:
            data_schemas = db_api.revision_documents_get(
                schema=types.DATA_SCHEMA_SCHEMA, deleted=False)
            validator = document_validation.DocumentValidation(
                rendered_documents, data_schemas, pre_validate=False)
            engine.validate_render(revision_id, rendered_documents, validator)

        # 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 = req.params.pop('order', None)
        sort_by = req.params.pop('sort', None)
        limit = req.params.pop('limit', None)
        user_filters = req.params.copy()

        if not cleartext_secrets:
            rendered_documents = utils.redact_documents(rendered_documents)

        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)
Esempio n. 8
0
    def on_put(self, req, resp, bucket_name=None):
        document_data = req.stream.read(req.content_length or 0)
        try:
            documents = list(yaml.safe_load_all(document_data))
        except yaml.YAMLError as e:
            error_msg = ("Could not parse the document into YAML data. "
                         "Details: %s." % e)
            LOG.error(error_msg)
            raise falcon.HTTPBadRequest(description=six.text_type(e))

        # NOTE: Must validate documents before doing policy enforcement,
        # because we expect certain formatting of the documents while doing
        # policy enforcement. If any documents fail basic schema validaiton
        # raise an exception immediately.
        data_schemas = db_api.revision_documents_get(
            schema=types.DATA_SCHEMA_SCHEMA, deleted=False)
        try:
            doc_validator = document_validation.DocumentValidation(
                documents, data_schemas, pre_validate=True)
            validations = doc_validator.validate_all()
        except deckhand_errors.InvalidDocumentFormat as e:
            LOG.exception(e.format_message())
            raise falcon.HTTPBadRequest(description=e.format_message())

        for document in documents:
            if document['metadata'].get('storagePolicy') == 'encrypted':
                policy.conditional_authorize(
                    'deckhand:create_encrypted_documents', req.context)
                break

        self._prepare_secret_documents(documents)

        created_documents = self._create_revision_documents(
            bucket_name, documents, validations)

        resp.body = self.view_builder.list(created_documents)
        resp.status = falcon.HTTP_200
Esempio n. 9
0
    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)
        substitution_sources = self._retrieve_substitution_sources()

        try:
            document_layering = layering.DocumentLayering(
                documents, substitution_sources)
            rendered_documents = document_layering.render()
        except (errors.IndeterminateDocumentParent,
                errors.UnsupportedActionMethod,
                errors.MissingDocumentKey) as e:
            raise falcon.HTTPBadRequest(description=e.format_message())
        except (errors.LayeringPolicyNotFound,
                errors.SubstitutionDependencyNotFound) as e:
            raise falcon.HTTPConflict(description=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.
        order_by = sanitized_params.pop('order', None)
        sort_by = sanitized_params.pop('sort', None)
        user_filters = sanitized_params.copy()
        user_filters['metadata.layeringDefinition.abstract'] = False

        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)

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