Fix json schema nullable to add None to ENUM

The JSON Schema validation implementation of nullable(), which makes
values possible to be null was not adding None to the enum if it exists.
This causes validation to fail on ``None`` especially in the case of
keystone's boolean parameter_type implementation. ``nullable()`` now
adds ``None`` to the enum if the enum exists.

Closes-Bug: #1763824
Change-Id: I176fa90df63049661413c445554dba9b7d87272a
(cherry picked from commit 78adf4b40f)
This commit is contained in:
Morgan Fainberg 2018-04-13 13:34:31 -07:00 committed by Lance Bragstad
parent 6de0a147d6
commit 22af1d9f35
3 changed files with 53 additions and 3 deletions

View File

@ -40,6 +40,14 @@ def nullable(property_schema):
# do that yet so I'm not wasting time on it # do that yet so I'm not wasting time on it
new_schema = property_schema.copy() new_schema = property_schema.copy()
new_schema['type'] = [property_schema['type'], 'null'] new_schema['type'] = [property_schema['type'], 'null']
# NOTE(kmalloc): If enum is specified (such as our boolean case) ensure we
# add null to the enum as well so that null can be passed/validated as
# expected. Without adding to the enum, null will not validate as enum is
# explicitly listing valid values. According to the JSON Schema
# specification, the values must be unique in the enum array.
if 'enum' in new_schema and None not in new_schema['enum']:
# In the enum the 'null' is NoneType
new_schema['enum'].append(None)
return new_schema return new_schema

View File

@ -11,6 +11,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import copy
import uuid import uuid
from keystone.assignment import schema as assignment_schema from keystone.assignment import schema as assignment_schema
@ -107,6 +108,43 @@ _INVALID_FILTERS = ['some string', 1, 0, True, False]
_INVALID_NAMES = [True, 24, ' ', ''] _INVALID_NAMES = [True, 24, ' ', '']
class CommonValidationTestCase(unit.BaseTestCase):
def test_nullable_type_only(self):
bool_without_enum = copy.deepcopy(parameter_types.boolean)
bool_without_enum.pop('enum')
schema_type_only = {
'type': 'object',
'properties': {'test': validation.nullable(bool_without_enum)},
'additionalProperties': False,
'required': ['test']}
# Null should be in the types
self.assertIn('null', schema_type_only['properties']['test']['type'])
# No Enum, and nullable should not have added it.
self.assertNotIn('enum', schema_type_only['properties']['test'].keys())
validator = validators.SchemaValidator(schema_type_only)
reqs_to_validate = [{'test': val} for val in [True, False, None]]
for req in reqs_to_validate:
validator.validate(req)
def test_nullable_with_enum(self):
schema_with_enum = {
'type': 'object',
'properties': {
'test': validation.nullable(parameter_types.boolean)},
'additionalProperties': False,
'required': ['test']}
# Null should be in enum and type
self.assertIn('null', schema_with_enum['properties']['test']['type'])
self.assertIn(None, schema_with_enum['properties']['test']['enum'])
validator = validators.SchemaValidator(schema_with_enum)
reqs_to_validate = [{'test': val} for val in [True, False, None]]
for req in reqs_to_validate:
validator.validate(req)
class EntityValidationTestCase(unit.BaseTestCase): class EntityValidationTestCase(unit.BaseTestCase):
def setUp(self): def setUp(self):
@ -1814,9 +1852,7 @@ class UserValidationTestCase(unit.BaseTestCase):
ro.IGNORE_CHANGE_PASSWORD_OPT.option_name: None ro.IGNORE_CHANGE_PASSWORD_OPT.option_name: None
} }
} }
self.assertRaises(exception.SchemaValidationError, self.create_user_validator.validate(request_to_validate)
self.create_user_validator.validate,
request_to_validate)
def test_user_update_with_options_change_password_required(self): def test_user_update_with_options_change_password_required(self):
request_to_validate = { request_to_validate = {

View File

@ -0,0 +1,6 @@
---
fixes:
- |
[`bug 1763824 <https://bugs.launchpad.net/keystone/+bug/1763824>`_]
JSON Schema implementation ``nullable`` in keystone.common.validation now
properly adds ``None`` to the enum if the enum exists.