Skip to content

Commit 3c15419

Browse files
committed
Introduce IPNetworkSerializer to serialize allowed token IPs
1 parent e3b7bba commit 3c15419

File tree

6 files changed

+29
-11
lines changed

6 files changed

+29
-11
lines changed

docs/release-notes/version-3.3.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
#### PoE Interface Attributes ([#1099](https://github.com/netbox-community/netbox/issues/1099))
1515

16+
#### Restrict API Tokens by Client IP ([#8233](https://github.com/netbox-community/netbox/issues/8233))
17+
1618
### Enhancements
1719

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

netbox/netbox/api/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from .fields import ChoiceField, ContentTypeField, SerializedPKRelatedField
1+
from .fields import *
22
from .routers import NetBoxRouter
33
from .serializers import BulkOperationSerializer, ValidatedModelSerializer, WritableNestedSerializer
44

@@ -7,6 +7,7 @@
77
'BulkOperationSerializer',
88
'ChoiceField',
99
'ContentTypeField',
10+
'IPNetworkSerializer',
1011
'NetBoxRouter',
1112
'SerializedPKRelatedField',
1213
'ValidatedModelSerializer',

netbox/netbox/api/fields.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
from collections import OrderedDict
22

3-
import pytz
4-
from django.contrib.contenttypes.models import ContentType
53
from django.core.exceptions import ObjectDoesNotExist
4+
from netaddr import IPNetwork
65
from rest_framework import serializers
76
from rest_framework.exceptions import ValidationError
87
from rest_framework.relations import PrimaryKeyRelatedField, RelatedField
98

9+
__all__ = (
10+
'ChoiceField',
11+
'ContentTypeField',
12+
'IPNetworkSerializer',
13+
'SerializedPKRelatedField',
14+
)
15+
1016

1117
class ChoiceField(serializers.Field):
1218
"""
@@ -104,6 +110,17 @@ def to_representation(self, obj):
104110
return f"{obj.app_label}.{obj.model}"
105111

106112

113+
class IPNetworkSerializer(serializers.Serializer):
114+
"""
115+
Representation of an IP network value (e.g. 192.0.2.0/24).
116+
"""
117+
def to_representation(self, instance):
118+
return str(instance)
119+
120+
def to_internal_value(self, value):
121+
return IPNetwork(value)
122+
123+
107124
class SerializedPKRelatedField(PrimaryKeyRelatedField):
108125
"""
109126
Extends PrimaryKeyRelatedField to return a serialized object on read. This is useful for representing related

netbox/users/api/serializers.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from django.contrib.contenttypes.models import ContentType
33
from rest_framework import serializers
44

5-
from netbox.api import ContentTypeField, SerializedPKRelatedField, ValidatedModelSerializer
5+
from netbox.api import ContentTypeField, IPNetworkSerializer, SerializedPKRelatedField, ValidatedModelSerializer
66
from users.models import ObjectPermission, Token
77
from .nested_serializers import *
88

@@ -64,6 +64,7 @@ class TokenSerializer(ValidatedModelSerializer):
6464
url = serializers.HyperlinkedIdentityField(view_name='users-api:token-detail')
6565
key = serializers.CharField(min_length=40, max_length=40, allow_blank=True, required=False)
6666
user = NestedUserSerializer()
67+
allowed_ips = serializers.ListField(child=IPNetworkSerializer())
6768

6869
class Meta:
6970
model = Token

netbox/users/models.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,19 @@
44
from django.contrib.auth.models import Group, User
55
from django.contrib.contenttypes.models import ContentType
66
from django.contrib.postgres.fields import ArrayField
7-
from django.core.exceptions import ValidationError
87
from django.core.validators import MinLengthValidator
98
from django.db import models
109
from django.db.models.signals import post_save
1110
from django.dispatch import receiver
1211
from django.utils import timezone
12+
from netaddr import IPNetwork
1313

1414
from ipam.fields import IPNetworkField
1515
from netbox.config import get_config
1616
from utilities.querysets import RestrictedQuerySet
1717
from utilities.utils import flatten_dict
1818
from .constants import *
1919

20-
import ipaddress
21-
2220
__all__ = (
2321
'ObjectPermission',
2422
'Token',
@@ -259,7 +257,7 @@ def validate_client_ip(self, client_ip):
259257
return True
260258

261259
for ip_network in self.allowed_ips:
262-
if client_ip in ipaddress.ip_network(ip_network):
260+
if client_ip in IPNetwork(ip_network):
263261
return True
264262

265263
return False

netbox/utilities/request.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import ipaddress
1+
from netaddr import IPAddress
22

33
__all__ = (
44
'get_client_ip',
@@ -19,7 +19,7 @@ def get_client_ip(request, additional_headers=()):
1919
if header in request.META:
2020
client_ip = request.META[header].split(',')[0]
2121
try:
22-
return ipaddress.ip_address(client_ip)
22+
return IPAddress(client_ip)
2323
except ValueError:
2424
raise ValueError(f"Invalid IP address set for {header}: {client_ip}")
2525

0 commit comments

Comments
 (0)