Fixup v2 API Validation

* Added list type checking to recordset.records
* Added list type checking to domain.masters
* Added extra debug output
* Catch all Exception clause in objects.adaptors.base.parse()

Change-Id: I900d418c16f016e0bd4e3f30cec393734b2feca4
Closes-Bug: #1473212
This commit is contained in:
Graham Hayes 2015-07-13 19:55:00 +01:00
parent 6e4b21ede2
commit 16c84d40d7
5 changed files with 140 additions and 16 deletions

View File

@ -15,6 +15,8 @@ from oslo_log import log as logging
from designate.objects.adapters.api_v2 import base from designate.objects.adapters.api_v2 import base
from designate import objects from designate import objects
from designate import exceptions
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -66,8 +68,22 @@ class DomainAPIv2Adapter(base.APIv2Adapter):
# https://bugs.launchpad.net/designate/+bug/1432842 is fixed # https://bugs.launchpad.net/designate/+bug/1432842 is fixed
if 'masters' in values: if 'masters' in values:
object.set_masters(values.get('masters')) if isinstance(values['masters'], list):
del values['masters'] object.set_masters(values.get('masters'))
del values['masters']
else:
errors = objects.ValidationErrorList()
e = objects.ValidationError()
e.path = ['masters']
e.validator = 'type'
e.validator_value = ["list"]
e.message = ("'%(data)s' is not a valid list of masters"
% {'data': values['masters']})
# Add it to the list for later
errors.append(e)
raise exceptions.InvalidObject(
"Provided object does not match "
"schema", errors=errors, object=cls.ADAPTER_OBJECT())
return super(DomainAPIv2Adapter, cls)._parse_object( return super(DomainAPIv2Adapter, cls)._parse_object(
values, object, *args, **kwargs) values, object, *args, **kwargs)

View File

@ -73,8 +73,23 @@ class RecordSetAPIv2Adapter(base.APIv2Adapter):
# Get new list of Records # Get new list of Records
new_records = set() new_records = set()
if 'records' in new_recordset: if 'records' in new_recordset:
for record in new_recordset['records']: if isinstance(new_recordset['records'], list):
new_records.add(record) for record in new_recordset['records']:
new_records.add(record)
else:
errors = objects.ValidationErrorList()
e = objects.ValidationError()
e.path = ['records']
e.validator = 'type'
e.validator_value = ["list"]
e.message = ("'%(data)s' is not a valid list of records"
% {'data': new_recordset['records']})
# Add it to the list for later
errors.append(e)
raise exceptions.InvalidObject(
"Provided object does not match "
"schema", errors=errors, object=cls.ADAPTER_OBJECT())
# Get differences of Records # Get differences of Records
records_to_add = new_records.difference(original_records) records_to_add = new_records.difference(original_records)
records_to_rm = original_records.difference(new_records) records_to_rm = original_records.difference(new_records)

View File

@ -17,6 +17,8 @@ import six
from designate import objects from designate import objects
from designate import exceptions from designate import exceptions
from designate.i18n import _LE
from designate.i18n import _LI
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -139,18 +141,57 @@ class DesignateAdapter(object):
@classmethod @classmethod
def parse(cls, format_, values, output_object, *args, **kwargs): def parse(cls, format_, values, output_object, *args, **kwargs):
if isinstance(output_object, objects.ListObjectMixin): LOG.debug("Creating %s object with values %r" %
# type_ = 'list' (output_object.obj_name(), values))
return cls.get_object_adapter(
format_, try:
output_object)._parse_list( if isinstance(output_object, objects.ListObjectMixin):
values, output_object, *args, **kwargs) # type_ = 'list'
else: return cls.get_object_adapter(
# type_ = 'object' format_,
return cls.get_object_adapter( output_object)._parse_list(
format_, values, output_object, *args, **kwargs)
output_object)._parse_object( else:
values, output_object, *args, **kwargs) # type_ = 'object'
return cls.get_object_adapter(
format_,
output_object)._parse_object(
values, output_object, *args, **kwargs)
except TypeError as e:
LOG.exception(_LE("TypeError creating %(name)s with values"
" %(values)r") %
{"name": output_object.obj_name(), "values": values})
error_message = str.format(
'Provided object does not match schema. '
'Got a TypeError with message %s' % six.text_type(e))
raise exceptions.InvalidObject(error_message)
except AttributeError as e:
LOG.exception(_LE("AttributeError creating %(name)s "
"with values %(values)r") %
{"name": output_object.obj_name(), "values": values})
error_message = str.format(
'Provided object is not valid. '
'Got an AttributeError with message %s' % six.text_type(e))
raise exceptions.InvalidObject(error_message)
except exceptions.InvalidObject:
LOG.info(_LI("InvalidObject creating %(name)s with "
"values %(values)r") %
{"name": output_object.obj_name(), "values": values})
raise
except Exception as e:
LOG.exception(_LE("Exception creating %(name)s with "
"values %(values)r") %
{"name": output_object.obj_name(), "values": values})
error_message = str.format(
'Provided object is not valid. '
'Got a %s error with message %s' %
(type(e).__name__, six.text_type(e)))
raise exceptions.InvalidObject(error_message)
@classmethod @classmethod
def _parse_object(cls, values, output_object, *args, **kwargs): def _parse_object(cls, values, output_object, *args, **kwargs):

View File

@ -308,6 +308,13 @@ class DesignateObject(object):
errors.append(ValidationError.from_js_error(error)) errors.append(ValidationError.from_js_error(error))
if len(errors) > 0: if len(errors) > 0:
LOG.debug(
"Error Validating '%(name)s' object with values: "
"%(values)r", {
'name': self.obj_name(),
'values': values,
}
)
raise exceptions.InvalidObject( raise exceptions.InvalidObject(
"Provided object does not match " "Provided object does not match "
"schema", errors=errors, object=self) "schema", errors=errors, object=self)

View File

@ -12,9 +12,12 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# 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 logging import logging
from copy import deepcopy from copy import deepcopy
import six
from designate import exceptions from designate import exceptions
from designate import utils from designate import utils
from designate.objects import base from designate.objects import base
@ -136,6 +139,11 @@ class RecordSet(base.DictObjectMixin, base.PersistentObjectMixin,
def validate(self): def validate(self):
LOG.debug("Validating '%(name)s' object with values: %(values)r", {
'name': self.obj_name(),
'values': self.to_dict(),
})
errors = ValidationErrorList() errors = ValidationErrorList()
# Get the right classes (e.g. A for Recordsets with type: 'A') # Get the right classes (e.g. A for Recordsets with type: 'A')
@ -191,6 +199,36 @@ class RecordSet(base.DictObjectMixin, base.PersistentObjectMixin,
# Add it to the list for later # Add it to the list for later
errors.append(e) errors.append(e)
error_indexes.append(i) error_indexes.append(i)
except TypeError as e:
e = ValidationError()
e.path = ['records', i]
e.validator = 'format'
e.validator_value = [self.type]
e.message = ("'%(data)s' is not a '%(type)s' Record"
% {'data': record.data, 'type': self.type})
# Add it to the list for later
errors.append(e)
error_indexes.append(i)
except AttributeError as e:
e = ValidationError()
e.path = ['records', i]
e.validator = 'format'
e.validator_value = [self.type]
e.message = ("'%(data)s' is not a '%(type)s' Record"
% {'data': record.data, 'type': self.type})
# Add it to the list for later
errors.append(e)
error_indexes.append(i)
except Exception as e:
error_message = str.format(
'Provided object is not valid. '
'Got a %s error with message %s' %
(type(e).__name__, six.text_type(e)))
raise exceptions.InvalidObject(error_message)
else: else:
# Seems to have loaded right - add it to be validated by # Seems to have loaded right - add it to be validated by
# JSONSchema # JSONSchema
@ -222,6 +260,13 @@ class RecordSet(base.DictObjectMixin, base.PersistentObjectMixin,
# If JSONSchema passes, but we found parsing errors, # If JSONSchema passes, but we found parsing errors,
# raise an exception # raise an exception
if len(errors) > 0: if len(errors) > 0:
LOG.debug(
"Error Validating '%(name)s' object with values: "
"%(values)r", {
'name': self.obj_name(),
'values': self.to_dict(),
}
)
raise exceptions.InvalidObject( raise exceptions.InvalidObject(
"Provided object does not match " "Provided object does not match "
"schema", errors=errors, object=self) "schema", errors=errors, object=self)