Fix schema validation to use JSONSchema for empty entity
APIs such as region creation allow an empty entity (a random UUID will
be generated as the region in this case).
Keystone uses JSONShema for validation and will allow an empty entity
to be passed if the schema doesn't have `required` properties (as the
region creation schema allows).
The patch fixes the schema validation code to pass even an empty entity
to JSONSchema validation so that an API such as region create can pass
the validation and can be created successfully.
Change-Id: If3dd49af5e16bb3b741efa4573f9f8e9085ddd14
Closes-Bug: #1501740
(cherry picked from commit ba7973b869
)
This commit is contained in:
parent
40d2fc2456
commit
01b89c6195
|
@ -28,8 +28,7 @@ def validated(request_body_schema, resource_to_validate):
|
|||
:param request_body_schema: a schema to validate the resource reference
|
||||
:param resource_to_validate: the reference to validate
|
||||
:raises keystone.exception.ValidationError: if `resource_to_validate` is
|
||||
not passed by or passed with an empty value (see wrapper method
|
||||
below).
|
||||
None. (see wrapper method below).
|
||||
:raises TypeError: at decoration time when the expected resource to
|
||||
validate isn't found in the decorated method's
|
||||
signature
|
||||
|
@ -49,15 +48,15 @@ def validated(request_body_schema, resource_to_validate):
|
|||
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if kwargs.get(resource_to_validate):
|
||||
if (resource_to_validate in kwargs and
|
||||
kwargs[resource_to_validate] is not None):
|
||||
schema_validator.validate(kwargs[resource_to_validate])
|
||||
else:
|
||||
try:
|
||||
resource = args[arg_index]
|
||||
# If resource to be validated is empty, no need to do
|
||||
# validation since the message given by jsonschema doesn't
|
||||
# help in this case.
|
||||
if resource:
|
||||
# If the resource to be validated is not None but
|
||||
# empty, it is possible to be validated by jsonschema.
|
||||
if resource is not None:
|
||||
schema_validator.validate(resource)
|
||||
else:
|
||||
raise exception.ValidationError(
|
||||
|
|
|
@ -67,6 +67,12 @@ entity_create = {
|
|||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
entity_create_optional_body = {
|
||||
'type': 'object',
|
||||
'properties': _entity_properties,
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
entity_update = {
|
||||
'type': 'object',
|
||||
'properties': _entity_properties,
|
||||
|
@ -122,12 +128,14 @@ class ValidatedDecoratorTests(unit.BaseTestCase):
|
|||
'name': uuid.uuid4().hex,
|
||||
}
|
||||
|
||||
invalid_entity = {}
|
||||
|
||||
@validation.validated(entity_create, 'entity')
|
||||
def create_entity(self, entity):
|
||||
"""Used to test cases where validated param is the only param."""
|
||||
|
||||
@validation.validated(entity_create_optional_body, 'entity')
|
||||
def create_entity_optional_body(self, entity):
|
||||
"""Used to test cases where there is an optional body."""
|
||||
|
||||
@validation.validated(entity_update, 'entity')
|
||||
def update_entity(self, entity_id, entity):
|
||||
"""Used to test cases where validated param is not the only param."""
|
||||
|
@ -135,13 +143,9 @@ class ValidatedDecoratorTests(unit.BaseTestCase):
|
|||
def test_calling_create_with_valid_entity_kwarg_succeeds(self):
|
||||
self.create_entity(entity=self.valid_entity)
|
||||
|
||||
@expected_validation_failure
|
||||
def test_calling_create_with_invalid_entity_kwarg_fails(self):
|
||||
self.create_entity(entity=self.invalid_entity)
|
||||
|
||||
@expected_validation_failure
|
||||
def test_calling_create_with_empty_entity_kwarg_fails(self):
|
||||
self.create_entity(entity={})
|
||||
def test_calling_create_with_empty_entity_kwarg_succeeds(self):
|
||||
"""Test the case when client passing in an empty kwarg reference."""
|
||||
self.create_entity_optional_body(entity={})
|
||||
|
||||
@expected_validation_failure
|
||||
def test_calling_create_with_kwarg_as_None_fails(self):
|
||||
|
@ -150,13 +154,9 @@ class ValidatedDecoratorTests(unit.BaseTestCase):
|
|||
def test_calling_create_with_valid_entity_arg_succeeds(self):
|
||||
self.create_entity(self.valid_entity)
|
||||
|
||||
@expected_validation_failure
|
||||
def test_calling_create_with_invalid_entity_arg_fails(self):
|
||||
self.create_entity(self.invalid_entity)
|
||||
|
||||
@expected_validation_failure
|
||||
def test_calling_create_with_empty_entity_arg_fails(self):
|
||||
self.create_entity({})
|
||||
def test_calling_create_with_empty_entity_arg_succeeds(self):
|
||||
"""Test the case when client passing in an empty entity reference."""
|
||||
self.create_entity_optional_body({})
|
||||
|
||||
@expected_validation_failure
|
||||
def test_calling_create_with_entity_arg_as_None_fails(self):
|
||||
|
@ -180,9 +180,14 @@ class ValidatedDecoratorTests(unit.BaseTestCase):
|
|||
def test_calling_update_with_valid_entity_succeeds(self):
|
||||
self.update_entity(uuid.uuid4().hex, self.valid_entity)
|
||||
|
||||
@expected_validation_failure
|
||||
def test_calling_update_with_invalid_entity_fails(self):
|
||||
self.update_entity(uuid.uuid4().hex, self.invalid_entity)
|
||||
def test_calling_update_with_empty_entity_kwarg_succeeds(self):
|
||||
"""Test the case when client passing in an empty entity reference."""
|
||||
global entity_update
|
||||
original_entity_update = entity_update.copy()
|
||||
# pop 'minProperties' from schema so that empty body is allowed.
|
||||
entity_update.pop('minProperties')
|
||||
self.update_entity(uuid.uuid4().hex, entity={})
|
||||
entity_update = original_entity_update
|
||||
|
||||
|
||||
class EntityValidationTestCase(unit.BaseTestCase):
|
||||
|
@ -906,6 +911,11 @@ class RegionValidationTestCase(unit.BaseTestCase):
|
|||
request_to_validate = {'other_attr': uuid.uuid4().hex}
|
||||
self.create_region_validator.validate(request_to_validate)
|
||||
|
||||
def test_validate_region_create_succeeds_with_no_parameters(self):
|
||||
"""Validate create region request with no parameters."""
|
||||
request_to_validate = {}
|
||||
self.create_region_validator.validate(request_to_validate)
|
||||
|
||||
def test_validate_region_update_succeeds(self):
|
||||
"""Test that we validate a region update request."""
|
||||
request_to_validate = {'id': 'us-west',
|
||||
|
|
Loading…
Reference in New Issue