Browse Source

Fixes #2769: improve prefix_length validations

kobayashi 6 years ago
parent
commit
7ef9a6c0a7

+ 1 - 0
docs/release-notes/version-2.7.md

@@ -9,6 +9,7 @@
 
 ### Bug Fixes
 
+* [#2769](https://github.com/netbox-community/netbox/issues/2769) - Improve `prefix_length` validation on available-prefixes API
 * [#4340](https://github.com/netbox-community/netbox/issues/4340) - Enforce unique constraints for device and virtual machine names in the API
 * [#4343](https://github.com/netbox-community/netbox/issues/4343) - Fix Markdown support for tables
 * [#4365](https://github.com/netbox-community/netbox/issues/4365) - Fix exception raised on IP address bulk add view

+ 27 - 0
netbox/ipam/api/serializers.py

@@ -154,6 +154,33 @@ class PrefixSerializer(TaggitSerializer, CustomFieldModelSerializer):
         read_only_fields = ['family']
 
 
+class PrefixLengthSerializer(serializers.Serializer):
+
+    prefix_length = serializers.IntegerField()
+
+    def to_internal_value(self, data):
+        requested_prefix = data.get('prefix_length')
+        if requested_prefix is None:
+            raise serializers.ValidationError({
+                'prefix_length': 'this field can not be missing'
+            })
+        if not isinstance(requested_prefix, int):
+            raise serializers.ValidationError({
+                'prefix_length': 'this field must be int type'
+            })
+
+        prefix = self.context.get('prefix')
+        if prefix.family == 4 and requested_prefix > 32:
+            raise serializers.ValidationError({
+                'prefix_length': 'Invalid prefix length ({}) for IPv4'.format((requested_prefix))
+            })
+        elif prefix.family == 6 and requested_prefix > 128:
+            raise serializers.ValidationError({
+                'prefix_length': 'Invalid prefix length ({}) for IPv6'.format((requested_prefix))
+            })
+        return data
+
+
 class AvailablePrefixSerializer(serializers.Serializer):
     """
     Representation of a prefix which does not exist in the database.

+ 15 - 35
netbox/ipam/api/views.py

@@ -105,45 +105,25 @@ class PrefixViewSet(CustomFieldModelViewSet):
             if not request.user.has_perm('ipam.add_prefix'):
                 raise PermissionDenied()
 
-            # Normalize to a list of objects
-            requested_prefixes = request.data if isinstance(request.data, list) else [request.data]
+            # Validate Requested Prefixes' length
+            serializer = serializers.PrefixLengthSerializer(
+                data=request.data if isinstance(request.data, list) else [request.data],
+                many=True,
+                context={
+                    'request': request,
+                    'prefix': prefix,
+                }
+            )
+            if not serializer.is_valid():
+                return Response(
+                    serializer.errors,
+                    status=status.HTTP_400_BAD_REQUEST
+                )
 
+            requested_prefixes = serializer.validated_data
             # Allocate prefixes to the requested objects based on availability within the parent
             for i, requested_prefix in enumerate(requested_prefixes):
 
-                # Validate requested prefix size
-                prefix_length = requested_prefix.get('prefix_length')
-                if prefix_length is None:
-                    return Response(
-                        {
-                            "detail": "Item {}: prefix_length field missing".format(i)
-                        },
-                        status=status.HTTP_400_BAD_REQUEST
-                    )
-                try:
-                    prefix_length = int(prefix_length)
-                except ValueError:
-                    return Response(
-                        {
-                            "detail": "Item {}: Invalid prefix length ({})".format(i, prefix_length),
-                        },
-                        status=status.HTTP_400_BAD_REQUEST
-                    )
-                if prefix.family == 4 and prefix_length > 32:
-                    return Response(
-                        {
-                            "detail": "Item {}: Invalid prefix length ({}) for IPv4".format(i, prefix_length),
-                        },
-                        status=status.HTTP_400_BAD_REQUEST
-                    )
-                elif prefix.family == 6 and prefix_length > 128:
-                    return Response(
-                        {
-                            "detail": "Item {}: Invalid prefix length ({}) for IPv6".format(i, prefix_length),
-                        },
-                        status=status.HTTP_400_BAD_REQUEST
-                    )
-
                 # Find the first available prefix equal to or larger than the requested size
                 for available_prefix in available_prefixes.iter_cidrs():
                     if requested_prefix['prefix_length'] >= available_prefix.prefixlen:

+ 6 - 1
netbox/ipam/tests/test_api.py

@@ -611,10 +611,15 @@ class PrefixTest(APITestCase):
             self.assertEqual(response.data['description'], data['description'])
 
         # Try to create one more prefix
-        response = self.client.post(url, {'prefix_length': 30}, **self.header)
+        response = self.client.post(url, {'prefix_length': 30}, format='json', **self.header)
         self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
         self.assertIn('detail', response.data)
 
+        # Try to create invalid prefix type
+        response = self.client.post(url, {'prefix_length': '30'}, format='json', **self.header)
+        self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
+        self.assertIn('prefix_length', response.data[0])
+
     def test_create_multiple_available_prefixes(self):
 
         prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/28'), is_pool=True)