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