From 293dbd8a8b2c291d1ae9efc7b2f2137fc896833b Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 31 May 2017 14:09:57 -0400 Subject: [PATCH] Fixes #1226: Improve validation for custom field values submitted via the API --- netbox/extras/api/customfields.py | 25 +++++++++++++++++++++---- netbox/extras/models.py | 6 +++++- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/netbox/extras/api/customfields.py b/netbox/extras/api/customfields.py index 5bd22189320..da15ce1aa5b 100644 --- a/netbox/extras/api/customfields.py +++ b/netbox/extras/api/customfields.py @@ -1,4 +1,5 @@ from __future__ import unicode_literals +from datetime import datetime from rest_framework import serializers from rest_framework.exceptions import ValidationError @@ -6,7 +7,9 @@ from django.contrib.contenttypes.models import ContentType from django.db import transaction -from extras.models import CF_TYPE_SELECT, CustomField, CustomFieldChoice, CustomFieldValue +from extras.models import ( + CF_TYPE_BOOLEAN, CF_TYPE_DATE, CF_TYPE_SELECT, CustomField, CustomFieldChoice, CustomFieldValue, +) # @@ -25,16 +28,30 @@ def to_internal_value(self, data): for field_name, value in data.items(): + cf = custom_fields[field_name] + # Validate custom field name if field_name not in custom_fields: raise ValidationError("Invalid custom field for {} objects: {}".format(content_type, field_name)) + # Validate boolean + if cf.type == CF_TYPE_BOOLEAN and value not in [True, False, 1, 0]: + raise ValidationError("Invalid value for boolean field {}: {}".format(field_name, value)) + + # Validate date + if cf.type == CF_TYPE_DATE: + try: + datetime.strptime(value, '%Y-%m-%d') + except ValueError: + raise ValidationError("Invalid date for field {}: {}. (Required format is YYYY-MM-DD.)".format( + field_name, value + )) + # Validate selected choice - cf = custom_fields[field_name] if cf.type == CF_TYPE_SELECT: valid_choices = [c.pk for c in cf.choices.all()] if value not in valid_choices: - raise ValidationError("Invalid choice ({}) for field {}".format(value, field_name)) + raise ValidationError("Invalid choice for field {}: {}".format(field_name, value)) # Check for missing required fields missing_fields = [] @@ -87,7 +104,7 @@ def _save_custom_fields(self, instance, custom_fields): field=custom_field, obj_type=content_type, obj_id=instance.pk, - defaults={'serialized_value': value}, + defaults={'serialized_value': custom_field.serialize_value(value)}, ) def create(self, validated_data): diff --git a/netbox/extras/models.py b/netbox/extras/models.py index ea92fae0ca3..bea8a664b77 100644 --- a/netbox/extras/models.py +++ b/netbox/extras/models.py @@ -139,7 +139,11 @@ def serialize_value(self, value): if self.type == CF_TYPE_BOOLEAN: return str(int(bool(value))) if self.type == CF_TYPE_DATE: - return value.strftime('%Y-%m-%d') + # Could be date/datetime object or string + try: + return value.strftime('%Y-%m-%d') + except AttributeError: + return value if self.type == CF_TYPE_SELECT: # Could be ModelChoiceField or TypedChoiceField return str(value.id) if hasattr(value, 'id') else str(value)