Introduce IPNetworkSerializer to serialize allowed token IPs

This commit is contained in:
jeremystretch 2022-06-22 21:51:43 -04:00
parent e3b7bba84f
commit 3c15419bd0
6 changed files with 29 additions and 11 deletions

View File

@ -13,6 +13,8 @@
#### PoE Interface Attributes ([#1099](https://github.com/netbox-community/netbox/issues/1099))
#### Restrict API Tokens by Client IP ([#8233](https://github.com/netbox-community/netbox/issues/8233))
### Enhancements
* [#1202](https://github.com/netbox-community/netbox/issues/1202) - Support overlapping assignment of NAT IP addresses
@ -21,7 +23,6 @@
* [#7120](https://github.com/netbox-community/netbox/issues/7120) - Add `termination_date` field to Circuit
* [#7744](https://github.com/netbox-community/netbox/issues/7744) - Add `status` field to Location
* [#8222](https://github.com/netbox-community/netbox/issues/8222) - Enable the assignment of a VM to a specific host device within a cluster
* [#8233](https://github.com/netbox-community/netbox/issues/8233) - Restrict API token access by source IP
* [#8471](https://github.com/netbox-community/netbox/issues/8471) - Add `status` field to Cluster
* [#8495](https://github.com/netbox-community/netbox/issues/8495) - Enable custom field grouping
* [#8995](https://github.com/netbox-community/netbox/issues/8995) - Enable arbitrary ordering of REST API results

View File

@ -1,4 +1,4 @@
from .fields import ChoiceField, ContentTypeField, SerializedPKRelatedField
from .fields import *
from .routers import NetBoxRouter
from .serializers import BulkOperationSerializer, ValidatedModelSerializer, WritableNestedSerializer
@ -7,6 +7,7 @@ __all__ = (
'BulkOperationSerializer',
'ChoiceField',
'ContentTypeField',
'IPNetworkSerializer',
'NetBoxRouter',
'SerializedPKRelatedField',
'ValidatedModelSerializer',

View File

@ -1,12 +1,18 @@
from collections import OrderedDict
import pytz
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from netaddr import IPNetwork
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.relations import PrimaryKeyRelatedField, RelatedField
__all__ = (
'ChoiceField',
'ContentTypeField',
'IPNetworkSerializer',
'SerializedPKRelatedField',
)
class ChoiceField(serializers.Field):
"""
@ -104,6 +110,17 @@ class ContentTypeField(RelatedField):
return f"{obj.app_label}.{obj.model}"
class IPNetworkSerializer(serializers.Serializer):
"""
Representation of an IP network value (e.g. 192.0.2.0/24).
"""
def to_representation(self, instance):
return str(instance)
def to_internal_value(self, value):
return IPNetwork(value)
class SerializedPKRelatedField(PrimaryKeyRelatedField):
"""
Extends PrimaryKeyRelatedField to return a serialized object on read. This is useful for representing related

View File

@ -2,7 +2,7 @@ from django.contrib.auth.models import Group, User
from django.contrib.contenttypes.models import ContentType
from rest_framework import serializers
from netbox.api import ContentTypeField, SerializedPKRelatedField, ValidatedModelSerializer
from netbox.api import ContentTypeField, IPNetworkSerializer, SerializedPKRelatedField, ValidatedModelSerializer
from users.models import ObjectPermission, Token
from .nested_serializers import *
@ -64,6 +64,7 @@ class TokenSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='users-api:token-detail')
key = serializers.CharField(min_length=40, max_length=40, allow_blank=True, required=False)
user = NestedUserSerializer()
allowed_ips = serializers.ListField(child=IPNetworkSerializer())
class Meta:
model = Token

View File

@ -4,12 +4,12 @@ import os
from django.contrib.auth.models import Group, User
from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import ValidationError
from django.core.validators import MinLengthValidator
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.utils import timezone
from netaddr import IPNetwork
from ipam.fields import IPNetworkField
from netbox.config import get_config
@ -17,8 +17,6 @@ from utilities.querysets import RestrictedQuerySet
from utilities.utils import flatten_dict
from .constants import *
import ipaddress
__all__ = (
'ObjectPermission',
'Token',
@ -259,7 +257,7 @@ class Token(models.Model):
return True
for ip_network in self.allowed_ips:
if client_ip in ipaddress.ip_network(ip_network):
if client_ip in IPNetwork(ip_network):
return True
return False

View File

@ -1,4 +1,4 @@
import ipaddress
from netaddr import IPAddress
__all__ = (
'get_client_ip',
@ -19,7 +19,7 @@ def get_client_ip(request, additional_headers=()):
if header in request.META:
client_ip = request.META[header].split(',')[0]
try:
return ipaddress.ip_address(client_ip)
return IPAddress(client_ip)
except ValueError:
raise ValueError(f"Invalid IP address set for {header}: {client_ip}")