def test_validation_document_duplication(self):
        """Validate that duplicate document fails when duplicate passed in."""
        test_document = self._read_data('sample_document')

        # Should only fail when pre_validate is True as the `db` module already
        # handles this on behalf of the controller.
        validations = document_validation.DocumentValidation(
            [test_document] * 2,  # Provide 2 of the same document.
            pre_validate=True).validate_all()

        expected_error = {
            'diagnostic': mock.ANY,
            'documents': [{
                'layer': test_document['metadata']['layeringDefinition'][
                    'layer'],
                'name': test_document['metadata']['name'],
                'schema': test_document['schema']
            }],
            'error': True,
            'kind': 'ValidationMessage',
            'level': 'Error',
            'message': 'Duplicate document exists',
            'name': 'Deckhand validation error'
        }

        self.assertEqual(1, len(validations[1]['errors']))
        self.assertEqual(expected_error,
                         validations[1]['errors'][0])

        # With pre_validate=False the validation should skip.
        validations = document_validation.DocumentValidation(
            [test_document] * 2,  # Provide 2 of the same document.
            pre_validate=False).validate_all()
        self.assertEmpty(validations[1]['errors'])
    def test_invalid_validation_schema_raises_runtime_error(self):
        document = self._read_data('sample_passphrase')
        fake_schema = mock.MagicMock(schema='fake')
        fake_schema_map = {'v1': {'deckhand/Passphrase': fake_schema}}

        # Validate that broken built-in base schema raises RuntimeError.
        with mock.patch.object(
                document_validation,
                'base_schema',
                new_callable=mock.PropertyMock(return_value=fake_schema)):
            doc_validator = document_validation.DocumentValidation(document)
            with self.assertRaisesRegexp(RuntimeError, 'Unknown error'):
                doc_validator.validate_all()

        # Validate that broken built-in schema for ``SchemaValidator`` raises
        # RuntimeError.
        with mock.patch.object(
                document_validation.SchemaValidator,
                '_schema_map',
                new_callable=mock.PropertyMock(return_value=fake_schema_map)):
            doc_validator = document_validation.DocumentValidation(document)
            with self.assertRaisesRegexp(RuntimeError, 'Unknown error'):
                doc_validator.validate_all()

        # Validate that broken data schema for ``DataSchemaValidator`` raises
        # RuntimeError.
        document = self._read_data('sample_document')
        data_schema = self._read_data('sample_data_schema')
        data_schema['metadata']['name'] = document['schema']
        data_schema['data'] = 'fake'
        doc_validator = document_validation.DocumentValidation(
            [document, data_schema])
        with self.assertRaisesRegexp(RuntimeError, 'Unknown error'):
            doc_validator.validate_all()
    def test_validation_failure_sanitizes_message_secrets(self):
        data_schema_factory = factories.DataSchemaFactory()
        metadata_name = 'example/Doc/v1'
        schema_to_use = {
            '$schema': 'http://json-schema.org/schema#',
            'type': 'object',
            'properties': {
                'secret-a': {'type': 'string'}
            },
            'required': ['secret-a'],
            'additionalProperties': False
        }
        data_schema = data_schema_factory.gen_test(
            metadata_name, data=schema_to_use)

        # Case 1: Check that sensitive data is sanitized if the document has
        # substitutions and `metadata.storagePolicy` == 'cleartext'.
        document_factory = factories.DocumentFactory(1, [1])
        test_document = document_factory.gen_test({
            "_GLOBAL_DATA_1_": {'data': {'secret-a': 5}},
            "_GLOBAL_SCHEMA_1_": metadata_name,
            "_GLOBAL_SUBSTITUTIONS_1_": [{
                "dest": {
                    "path": ".secret-a"
                },
                "src": {
                    "schema": "deckhand/CertificateKey/v1",
                    "name": "site-cert",
                    "path": "."
                }
            }],
        }, global_abstract=False)[-1]
        test_document['metadata']['storagePolicy'] = 'cleartext'

        validations = document_validation.DocumentValidation(
            test_document, existing_data_schemas=[data_schema],
            pre_validate=False).validate_all()

        self.assertEqual(1, len(validations[0]['errors']))
        self.assertEqual('Sanitized to avoid exposing secret.',
                         validations[0]['errors'][0]['message'])

        # Case 2: Check that sensitive data is sanitized if the document has
        # no substitutions and `metadata.storagePolicy` == 'encrypted'.
        test_document = document_factory.gen_test({
            "_GLOBAL_DATA_1_": {'data': {'secret-a': 5}},
            "_GLOBAL_SCHEMA_1_": metadata_name,
            "_GLOBAL_SUBSTITUTIONS_1_": [],
        }, global_abstract=False)[-1]
        test_document['metadata']['storagePolicy'] = 'encrypted'

        validations = document_validation.DocumentValidation(
            test_document, existing_data_schemas=[data_schema],
            pre_validate=False).validate_all()

        self.assertEqual(1, len(validations[0]['errors']))
        self.assertEqual('Sanitized to avoid exposing secret.',
                         validations[0]['errors'][0]['message'])
Beispiel #4
0
    def _test_missing_required_sections(self, document, properties_to_remove):
        if document['metadata']['schema'].startswith(types.CONTROL):
            critial_properties = self.CRITICAL_CONTROL_PROPERTIES
        elif document['metadata']['schema'].startswith(types.DOCUMENT):
            critial_properties = self.CRITICAL_DOCUMENT_PROPERTIES
        else:
            self.fail('Document `metadata.schema` must start with '
                      '"metadata/Document" or "metadata/Control".')

        for idx, property_to_remove in enumerate(properties_to_remove):
            missing_prop = property_to_remove.split('.')[-1]
            invalid_data = self._corrupt_data(document, property_to_remove)

            exception_raised = property_to_remove in critial_properties
            expected_err_msg = "'%s' is a required property" % missing_prop

            payload = [invalid_data]
            doc_validator = document_validation.DocumentValidation(
                payload, pre_validate=False)
            if exception_raised:
                self.assertRaises(errors.InvalidDocumentFormat,
                                  doc_validator.validate_all)
            else:
                self._do_validations(doc_validator, invalid_data,
                                     expected_err_msg)
Beispiel #5
0
    def _pre_validate_documents(self, documents):
        LOG.debug('%s performing document pre-validation.',
                  self.__class__.__name__)
        validator = document_validation.DocumentValidation(
            documents, pre_validate=True)
        results = validator.validate_all()

        error_list = []
        for result in results:
            for e in result['errors']:
                for d in e['documents']:
                    LOG.error('Document [%s, %s] %s failed with '
                              'pre-validation error: "%s". Diagnostic: "%s".',
                              d['schema'], d['layer'], d['name'],
                              e['message'], e['diagnostic'])

                    error_list.append(
                        ValidationMessage(
                            message=e['message'],
                            doc_schema=d['schema'],
                            doc_name=d['name'],
                            doc_layer=d['layer']))

        if error_list:
            raise errors.InvalidDocumentFormat(error_list=error_list)
Beispiel #6
0
    def _test_missing_required_sections(self, properties_to_remove):
        for idx, property_to_remove in enumerate(properties_to_remove):
            critical = property_to_remove in self.CRITICAL_ATTRS

            missing_prop = property_to_remove.split('.')[-1]
            invalid_data = self._corrupt_data(property_to_remove)
            expected_err = self.SCHEMA_ERR % missing_prop

            doc_validator = document_validation.DocumentValidation(
                invalid_data)
            if critical:
                self.assertRaisesRegexp(
                    errors.InvalidDocumentFormat, expected_err,
                    doc_validator.validate_all)
            else:
                validations = doc_validator.validate_all()
                self.assertEqual(1, len(validations))
                self.assertEqual('failure', validations[0]['status'])
                self.assertEqual({'version': '1.0', 'name': 'deckhand'},
                                 validations[0]['validator'])
                self.assertEqual(types.DECKHAND_SCHEMA_VALIDATION,
                                 validations[0]['name'])
                self.assertEqual(1, len(validations[0]['errors']))
                self.assertEqual(self.data['metadata']['name'],
                                 validations[0]['errors'][0]['name'])
                self.assertEqual(self.data['schema'],
                                 validations[0]['errors'][0]['schema'])
                self.assertEqual(expected_err,
                                 validations[0]['errors'][0]['message'])
Beispiel #7
0
    def test_generic_document_missing_multiple_required_sections(self):
        """Validates that multiple errors are reported for a document with
        multiple validation errors.
        """
        document = self._read_data('sample_document')
        properties_to_remove = (
            'metadata.layeringDefinition.actions.0.method',
            'metadata.layeringDefinition.actions.0.path',
            'metadata.substitutions.0.dest.path',
            'metadata.substitutions.0.src.name',
            'metadata.substitutions.0.src.path',
            'metadata.substitutions.0.src.schema',
        )
        for property_to_remove in properties_to_remove:
            document = self._corrupt_data(document, property_to_remove)

        doc_validator = document_validation.DocumentValidation(document)
        e = self.assertRaises(errors.InvalidDocumentFormat,
                              doc_validator.validate_all)

        for idx, property_to_remove in enumerate(properties_to_remove):
            parts = property_to_remove.split('.')
            missing_property = parts[-1]

            error_re = r"%s is a required property" % missing_property
            self.assertRegex(str(e.error_list).replace("\'", ""), error_re)
Beispiel #8
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
Beispiel #9
0
def deckhand_render(documents=None,
                    fail_on_missing_sub_src=False,
                    validate=True):
    documents = documents or []
    errors = []
    rendered_documents = []

    schemas, schema_errors = load_schemas_from_docs(documents)
    errors.extend(schema_errors)

    try:
        deckhand_eng = layering.DocumentLayering(
            documents,
            fail_on_missing_sub_src=fail_on_missing_sub_src,
            validate=validate)
        rendered_documents = [dict(d) for d in deckhand_eng.render()]
        if validate:
            validator = document_validation.DocumentValidation(
                rendered_documents)
            results = validator.validate_all()
            for result in results:
                if result['errors']:
                    errors.append(
                        (DECKHAND_RENDER_EXCEPTION,
                         'During rendering Deckhand was unable to validate '
                         'the following document, details: %s.' %
                         (result['errors'])))
    except dh_errors.DeckhandException as e:
        errors.append((DECKHAND_RENDER_EXCEPTION,
                       'An unknown Deckhand exception occurred while trying'
                       ' to render documents: %s. Details: %s.' %
                       (str(e), e.error_list)))

    return rendered_documents, errors
    def test_validation_failure_sanitizes_error_section_secrets(
            self, mock_jsonschema):
        m_args = mock.Mock()
        mock_jsonschema.Draft4Validator(m_args).iter_errors.side_effect = [
            # Return empty list of errors for base schema and metadata
            # validator and pretend that 1 error is returned for next
            # validator.
            [],
            [],
            [mock.Mock(path=[], schema_path=[], message='scary-secret-here')]
        ]

        document_factory = factories.DocumentFactory(1, [1])
        test_document = document_factory.gen_test(
            {
                '_GLOBAL_DATA_1_': {'data': {'secret-a': 5}},
                '_GLOBAL_SUBSTITUTIONS_1_': [
                    {'src': {
                        'path': '.', 'schema': 'foo/bar/v1', 'name': 'foo'},
                     'dest': {'path': '.secret-a'}}
                ]
            },
            global_abstract=False)[-1]

        data_schema_factory = factories.DataSchemaFactory()
        data_schema = data_schema_factory.gen_test(test_document['schema'], {})

        validations = document_validation.DocumentValidation(
            test_document, existing_data_schemas=[data_schema],
            pre_validate=False).validate_all()

        self.assertEqual(1, len(validations[0]['errors']))
        self.assertIn('Sanitized to avoid exposing secret.',
                      str(validations[0]['errors'][-1]))
        self.assertNotIn('scary-secret.', str(validations[0]['errors'][-1]))
    def test_data_schema_missing_optional_sections(self):
        optional_missing_data = [
            self._corrupt_data(self.test_document, 'metadata.labels'),
        ]

        for missing_data in optional_missing_data:
            payload = [missing_data, self.dataschema]
            document_validation.DocumentValidation(payload).validate_all()
Beispiel #12
0
    def test_actions_but_no_parent_selector_raises_validation_error(self):
        # Verify that an error is thrown if actions are specified but
        # parentSelector is missing altogether.
        document = self._read_data('sample_document')
        document['metadata']['layeringDefinition'].pop('parentSelector')
        doc_validator = document_validation.DocumentValidation(
            [document], pre_validate=False)
        self.assertRaises(errors.InvalidDocumentFormat,
                          doc_validator.validate_all)

        # Verify that an error is thrown if actions are specified but no
        # parentSelector labels are.
        document['metadata']['layeringDefinition']['parentSelector'] = {}
        doc_validator = document_validation.DocumentValidation(
            [document], pre_validate=False)
        self.assertRaises(errors.InvalidDocumentFormat,
                          doc_validator.validate_all)
    def test_data_schema_missing_optional_sections(self):
        self._read_data('sample_data_schema')
        optional_missing_data = [
            self._corrupt_data('metadata.labels'),
        ]

        for missing_data in optional_missing_data:
            document_validation.DocumentValidation(missing_data).validate_all()
Beispiel #14
0
    def test_invalid_document_schema_generates_error(self, mock_log):
        document = self._read_data('sample_document')
        document['schema'] = 'foo/bar/v1'

        doc_validator = document_validation.DocumentValidation(document)
        doc_validator.validate_all()
        self.assertRegex(
            mock_log.error.mock_calls[0][1][0],
            'The provided document schema %s is invalid.' % document['schema'])
Beispiel #15
0
    def test_invalid_document_schema_version_generates_error(self, mock_log):
        document = self._read_data('sample_passphrase')
        document['schema'] = 'deckhand/Passphrase/v5'

        doc_validator = document_validation.DocumentValidation(document)
        doc_validator.validate_all()
        self.assertRegex(
            mock_log.error.mock_calls[0][1][0],
            'The provided document schema %s is invalid.' % document['schema'])
Beispiel #16
0
    def test_invalid_validation_schema_raises_runtime_error(self):
        document = self._read_data('sample_passphrase')

        # Validate that broken built-in base schema raises RuntimeError.
        doc_validator = document_validation.DocumentValidation(document)
        doc_validator._validators[0].base_schema = 'fake'
        with self.assertRaisesRegexp(RuntimeError, 'Unknown error'):
            doc_validator.validate_all()

        # Validate that broken data schema for ``DataSchemaValidator`` raises
        # RuntimeError.
        document = self._read_data('sample_document')
        data_schema = self._read_data('sample_data_schema')
        data_schema['metadata']['name'] = document['schema']
        data_schema['data'] = 'fake'
        doc_validator = document_validation.DocumentValidation(
            [document, data_schema], pre_validate=False)
        with self.assertRaisesRegexp(RuntimeError, 'Unknown error'):
            doc_validator.validate_all()
    def test_document_missing_optional_sections(self):
        properties_to_remove = (
            'metadata.substitutions',
            'metadata.substitutions.2.dest.pattern')

        for property_to_remove in properties_to_remove:
            missing_data = self._corrupt_data(self.test_document,
                                              property_to_remove)
            payload = [missing_data, self.dataschema]
            document_validation.DocumentValidation(payload).validate_all()
    def test_parent_selector_and_actions_both_provided_is_valid(self):
        test_document = self._read_data('sample_document')
        data_schema_factory = factories.DataSchemaFactory()
        data_schema = data_schema_factory.gen_test(test_document['schema'], {})

        validations = document_validation.DocumentValidation(
            test_document, existing_data_schemas=[data_schema],
            pre_validate=False).validate_all()

        self.assertEmpty(validations[0]['errors'])
 def test_abstract_document_not_validated(self, mock_log):
     test_document = self._read_data('sample_passphrase')
     # Set the document to abstract.
     abstract_document = utils.jsonpath_replace(
         test_document, True, '.metadata.layeringDefinition.abstract')
     document_validation.DocumentValidation(
         abstract_document).validate_all()
     self.assertTrue(mock_log.info.called)
     self.assertIn("Skipping schema validation for abstract document",
                   mock_log.info.mock_calls[0][1][0])
Beispiel #20
0
    def test_parent_selector_but_no_actions_raises_validation_error(self):
        # Verify that an error is thrown if parentSelector is specified but
        # actions is missing altogether.
        document = self._read_data('sample_document')
        document['metadata']['layeringDefinition']['parentSelector'] = {
            'some': 'label'
        }
        document['metadata']['layeringDefinition'].pop('actions')
        doc_validator = document_validation.DocumentValidation(
            [document], pre_validate=False)
        self.assertRaises(errors.InvalidDocumentFormat,
                          doc_validator.validate_all)

        # Verify that an error is thrown if parentSelector is specified but
        # at least 1 action isn't specified.
        document['metadata']['layeringDefinition']['actions'] = []
        doc_validator = document_validation.DocumentValidation(
            [document], pre_validate=False)
        self.assertRaises(errors.InvalidDocumentFormat,
                          doc_validator.validate_all)
    def test_document_missing_optional_sections(self):
        self._read_data('sample_document')
        properties_to_remove = ('metadata.layeringDefinition.actions',
                                'metadata.layeringDefinition.parentSelector',
                                'metadata.substitutions',
                                'metadata.substitutions.2.dest.pattern')

        for property_to_remove in properties_to_remove:
            optional_data_removed = self._corrupt_data(property_to_remove)
            document_validation.DocumentValidation(
                optional_data_removed).validate_all()
Beispiel #22
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)
    def test_abstract_document_not_validated(self, mock_log):
        self._read_data('sample_document')
        # Set the document to abstract.
        updated_data = self._corrupt_data(
            'metadata.layeringDefinition.abstract', True, op='replace')
        # Guarantee that a validation error is thrown by removing a required
        # property.
        del updated_data['metadata']['layeringDefinition']['layer']

        document_validation.DocumentValidation(updated_data).validate_all()
        self.assertTrue(mock_log.info.called)
        self.assertIn("Skipping schema validation for abstract document",
                      mock_log.info.mock_calls[0][1][0])
    def test_neither_parent_selector_nor_actions_provided_is_valid(self):
        test_document = self._read_data('sample_document')
        test_document['metadata']['layeringDefinition'].pop('actions')
        test_document['metadata']['layeringDefinition'].pop('parentSelector')

        data_schema_factory = factories.DataSchemaFactory()
        data_schema = data_schema_factory.gen_test(test_document['schema'], {})

        validations = document_validation.DocumentValidation(
            test_document, existing_data_schemas=[data_schema],
            pre_validate=False).validate_all()

        self.assertEmpty(validations[0]['errors'])
Beispiel #25
0
    def test_document_invalid_layering_definition_action(self):
        document = self._read_data('sample_document')
        missing_data = self._corrupt_data(
            document,
            'metadata.layeringDefinition.actions.0.method',
            'invalid',
            op='replace')
        error_re = (r".*invalid is not one of \[replace, delete, merge\]")

        payload = [missing_data]
        doc_validator = document_validation.DocumentValidation(payload)
        e = self.assertRaises(errors.InvalidDocumentFormat,
                              doc_validator.validate_all)
        self.assertRegex(str(e.error_list[0]).replace("\'", ""), error_re)
Beispiel #26
0
 def _post_validate(self, 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(
         documents, data_schemas)
     try:
         doc_validator.validate_all()
     except errors.InvalidDocumentFormat as e:
         LOG.error('Failed to post-validate rendered documents.')
         LOG.exception(e.format_message())
         raise falcon.HTTPInternalServerError(
             description=e.format_message())
    def test_document_invalid_layering_definition_action(self):
        document = self._read_data('sample_document')
        missing_data = self._corrupt_data(
            document,
            'metadata.layeringDefinition.actions.0.method',
            'invalid',
            op='replace')
        expected_err = "'invalid' is not one of ['replace', 'delete', 'merge']"

        # Ensure that a dataschema document exists for the random document
        # schema via mocking.
        dataschema_factory = factories.DataSchemaFactory()
        dataschema = dataschema_factory.gen_test(document['schema'], {})
        payload = [dataschema, missing_data]
        doc_validator = document_validation.DocumentValidation(payload)
        self._do_validations(doc_validator, document, expected_err)
Beispiel #28
0
 def _validate_documents(self, documents):
     LOG.debug('%s performing document pre-validation.',
               self.__class__.__name__)
     validator = document_validation.DocumentValidation(documents,
                                                        pre_validate=True)
     results = validator.validate_all()
     val_errors = []
     for result in results:
         val_errors.extend([(e['schema'], e['name'], e['message'])
                            for e in result['errors']])
     if val_errors:
         for error in val_errors:
             LOG.error(
                 'Document [%s] %s failed with pre-validation error: %s.',
                 *error)
         raise errors.InvalidDocumentFormat(
             details='The following pre-validation errors occurred '
             '(schema, name, error): %s.' % val_errors)
    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(
            rendered_documents, data_schemas, pre_validate=False)
        try:
            validations = doc_validator.validate_all()
        except errors.InvalidDocumentFormat as e:
            with excutils.save_and_reraise_exception():
                # Post-rendering validation errors likely indicate an internal
                # rendering bug, so override the default code to 500.
                e.code = 500
                LOG.error('Failed to post-validate rendered documents.')
                LOG.exception(e.format_message())
        else:
            error_list = []

            for validation in validations:
                if validation['status'] == 'failure':
                    error_list.extend([
                        vm.ValidationMessage(
                            message=error['message'],
                            name=vm.DOCUMENT_POST_RENDERING_FAILURE,
                            doc_schema=error['schema'],
                            doc_name=error['name'],
                            doc_layer=error['layer'],
                            diagnostic={
                                k: v for k, v in error.items() if k in (
                                    'schema_path',
                                    'validation_schema',
                                    'error_section'
                                )
                            }
                        )
                        for error in validation['errors']
                    ])

            if error_list:
                raise errors.InvalidDocumentFormat(
                    error_list=error_list,
                    reason='Validation'
                )
    def _test_missing_required_sections(self, document, properties_to_remove):
        for idx, property_to_remove in enumerate(properties_to_remove):
            missing_prop = property_to_remove.split('.')[-1]
            invalid_data = self._corrupt_data(document, property_to_remove)

            exception_raised = self.exception_map.get(property_to_remove, None)
            expected_err_msg = "'%s' is a required property" % missing_prop

            dataschema_factory = factories.DataSchemaFactory()
            dataschema = dataschema_factory.gen_test(
                invalid_data.get('schema', ''), {})
            payload = [dataschema, invalid_data]

            doc_validator = document_validation.DocumentValidation(payload)
            if exception_raised:
                self.assertRaises(exception_raised, doc_validator.validate_all)
            else:
                self._do_validations(doc_validator, invalid_data,
                                     expected_err_msg)