Jsonschema validation: base schema framework

This patch adds a base jsonschema framework.

This patch follows the Nova-Schema-framework:
https://github.com/openstack/nova/tree/master/nova/api/validation

Partial-Implements: blueprint tosca-csar-mgmt-driver

Co-Author: Neha Alhat <neha.alhat@nttdata.com>
Change-Id: I6ffeaaffb92fa9949a439a049f3092878b884634
This commit is contained in:
Niraj Singh 2019-05-02 04:37:26 +00:00 committed by nirajsingh
parent 2e461e1223
commit f519f23638
7 changed files with 241 additions and 0 deletions

View File

@ -10,6 +10,7 @@ anyjson>=0.3.3 # BSD
Babel!=2.4.0,>=2.3.4 # BSD
eventlet!=0.23.0,!=0.25.0,>=0.22.0 # MIT
requests>=2.14.2 # Apache-2.0
jsonschema>=2.6.0 # MIT
keystonemiddleware>=4.17.0 # Apache-2.0
kombu!=4.0.2,>=4.0.0 # BSD
netaddr>=0.7.18 # BSD

View File

View File

@ -0,0 +1,50 @@
# Copyright (C) 2019 NTT DATA
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Schema for vnf packages create API.
"""
from tacker.api.validation import parameter_types
create = {
'type': 'object',
'properties': {
'userDefinedData': parameter_types.keyvalue_pairs
},
'additionalProperties': False,
}
upload_from_uri = {
'type': 'object',
'properties': {
'addressInformation': {
'type': 'string', 'minLength': 0,
'maxLength': 2048, 'format': 'uri'
},
'userName': {
'type': 'string', 'maxLength': 255,
'pattern': '^[a-zA-Z0-9-_]*$'
},
'password': {
# Allow to specify any string for strong password.
'type': 'string', 'maxLength': 255,
},
},
'required': ['addressInformation'],
'additionalProperties': False,
}

View File

@ -0,0 +1,51 @@
# Copyright (C) 2019 NTT DATA
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Request Body validating middleware.
"""
import functools
import webob
from tacker.api.validation import validators
def schema(request_body_schema):
"""Register a schema to validate request body.
Registered schema will be used for validating request body just before
API method executing.
:param dict request_body_schema: a schema to validate request body
"""
def add_validator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
schema_validator = validators._SchemaValidator(
request_body_schema)
try:
schema_validator.validate(kwargs['body'])
except KeyError:
raise webob.exc.HTTPBadRequest(
explanation=_("Malformed request body"))
return func(*args, **kwargs)
return wrapper
return add_validator

View File

@ -0,0 +1,30 @@
# Copyright (C) 2019 NTT DATA
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Common parameter types for validating request Body.
"""
keyvalue_pairs = {
'type': 'object',
'patternProperties': {
'^[a-zA-Z0-9-_:. /]{1,255}$': {
'type': 'string', 'maxLength': 255
}
},
'additionalProperties': False
}

View File

@ -0,0 +1,105 @@
# Copyright (C) 2019 NTT DATA
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Internal implementation of request Body validating middleware.
"""
import jsonschema
from jsonschema import exceptions as jsonschema_exc
import rfc3986
import six
from tacker.common import exceptions as exception
@jsonschema.FormatChecker.cls_checks('uri')
def _validate_uri(instance):
return rfc3986.is_valid_uri(instance, require_scheme=True,
require_authority=True)
class FormatChecker(jsonschema.FormatChecker):
"""A FormatChecker can output the message from cause exception
We need understandable validation errors messages for users. When a
custom checker has an exception, the FormatChecker will output a
readable message provided by the checker.
"""
def check(self, param_value, format):
"""Check whether the param_value conforms to the given format.
:argument param_value: the param_value to check
:type: any primitive type (str, number, bool)
:argument str format: the format that param_value should conform to
:raises: :exc:`FormatError` if param_value does not conform to format
"""
if format not in self.checkers:
return
# For safety reasons custom checkers can be registered with
# allowed exception types. Anything else will fall into the
# default formatter.
func, raises = self.checkers[format]
result, cause = None, None
try:
result = func(param_value)
except raises as e:
cause = e
if not result:
msg = "%r is not a %r" % (param_value, format)
raise jsonschema_exc.FormatError(msg, cause=cause)
class _SchemaValidator(object):
"""A validator class
This class is changed from Draft4Validator to validate minimum/maximum
value of a string number(e.g. '10'). This changes can be removed when
we tighten up the API definition and the XML conversion.
Also FormatCheckers are added for checking data formats which would be
passed through cinder api commonly.
"""
validator_org = jsonschema.Draft4Validator
def __init__(self, schema):
validator_cls = jsonschema.validators.extend(self.validator_org,
validators={})
format_checker = FormatChecker()
self.validator = validator_cls(schema, format_checker=format_checker)
def validate(self, *args, **kwargs):
try:
self.validator.validate(*args, **kwargs)
except jsonschema.ValidationError as ex:
if len(ex.path) > 0:
detail = _("Invalid input for field/attribute %(path)s."
" Value: %(value)s. %(message)s") % {
'path': ex.path.pop(), 'value': ex.instance,
'message': ex.message
}
else:
detail = ex.message
raise exception.ValidationError(detail=detail)
except TypeError as ex:
# NOTE: If passing non string value to patternProperties parameter,
# TypeError happens. Here is for catching the TypeError.
detail = six.text_type(ex)
raise exception.ValidationError(detail=detail)

View File

@ -195,3 +195,7 @@ class DuplicateResourceName(TackerException):
class DuplicateEntity(Conflict):
message = _("%(_type)s already exist with given %(entry)s")
class ValidationError(BadRequest):
message = "%(detail)s"