Browse Source

Fixes #1385: Connected device API endpoint no longer requires authentication if LOGIN_REQUIRED=False

Jeremy Stretch 7 years ago
parent
commit
fd6df8e52a
2 changed files with 30 additions and 4 deletions
  1. 2 3
      netbox/dcim/api/views.py
  2. 28 1
      netbox/utilities/api.py

+ 2 - 3
netbox/dcim/api/views.py

@@ -3,7 +3,6 @@ from collections import OrderedDict
 
 
 from rest_framework.decorators import detail_route
 from rest_framework.decorators import detail_route
 from rest_framework.mixins import ListModelMixin
 from rest_framework.mixins import ListModelMixin
-from rest_framework.permissions import IsAuthenticated
 from rest_framework.response import Response
 from rest_framework.response import Response
 from rest_framework.viewsets import GenericViewSet, ModelViewSet, ViewSet
 from rest_framework.viewsets import GenericViewSet, ModelViewSet, ViewSet
 
 
@@ -21,7 +20,7 @@ from dcim import filters
 from extras.api.serializers import RenderedGraphSerializer
 from extras.api.serializers import RenderedGraphSerializer
 from extras.api.views import CustomFieldModelViewSet
 from extras.api.views import CustomFieldModelViewSet
 from extras.models import Graph, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
 from extras.models import Graph, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
-from utilities.api import ServiceUnavailable, WritableSerializerMixin
+from utilities.api import IsAuthenticatedOrLoginNotRequired, ServiceUnavailable, WritableSerializerMixin
 from .exceptions import MissingFilterException
 from .exceptions import MissingFilterException
 from . import serializers
 from . import serializers
 
 
@@ -387,7 +386,7 @@ class ConnectedDeviceViewSet(ViewSet):
     * `peer-device`: The name of the peer device
     * `peer-device`: The name of the peer device
     * `peer-interface`: The name of the peer interface
     * `peer-interface`: The name of the peer interface
     """
     """
-    permission_classes = [IsAuthenticated]
+    permission_classes = [IsAuthenticatedOrLoginNotRequired]
 
 
     def get_view_name(self):
     def get_view_name(self):
         return "Connected Device Locator"
         return "Connected Device Locator"

+ 28 - 1
netbox/utilities/api.py

@@ -4,9 +4,10 @@ from django.conf import settings
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
 
 
 from rest_framework import authentication, exceptions
 from rest_framework import authentication, exceptions
+from rest_framework.compat import is_authenticated
 from rest_framework.exceptions import APIException
 from rest_framework.exceptions import APIException
 from rest_framework.pagination import LimitOffsetPagination
 from rest_framework.pagination import LimitOffsetPagination
-from rest_framework.permissions import DjangoModelPermissions, SAFE_METHODS
+from rest_framework.permissions import BasePermission, DjangoModelPermissions, SAFE_METHODS
 from rest_framework.serializers import Field, ValidationError
 from rest_framework.serializers import Field, ValidationError
 
 
 from users.models import Token
 from users.models import Token
@@ -20,6 +21,10 @@ class ServiceUnavailable(APIException):
     default_detail = "Service temporarily unavailable, please try again later."
     default_detail = "Service temporarily unavailable, please try again later."
 
 
 
 
+#
+# Authentication
+#
+
 class TokenAuthentication(authentication.TokenAuthentication):
 class TokenAuthentication(authentication.TokenAuthentication):
     """
     """
     A custom authentication scheme which enforces Token expiration times.
     A custom authentication scheme which enforces Token expiration times.
@@ -61,6 +66,20 @@ class TokenPermissions(DjangoModelPermissions):
         return super(TokenPermissions, self).has_permission(request, view)
         return super(TokenPermissions, self).has_permission(request, view)
 
 
 
 
+class IsAuthenticatedOrLoginNotRequired(BasePermission):
+    """
+    Returns True if the user is authenticated or LOGIN_REQUIRED is False.
+    """
+    def has_permission(self, request, view):
+        if not settings.LOGIN_REQUIRED:
+            return True
+        return request.user and is_authenticated(request.user)
+
+
+#
+# Serializers
+#
+
 class ChoiceFieldSerializer(Field):
 class ChoiceFieldSerializer(Field):
     """
     """
     Represent a ChoiceField as {'value': <DB value>, 'label': <string>}.
     Represent a ChoiceField as {'value': <DB value>, 'label': <string>}.
@@ -98,6 +117,10 @@ class ContentTypeFieldSerializer(Field):
             raise ValidationError("Invalid content type")
             raise ValidationError("Invalid content type")
 
 
 
 
+#
+# Mixins
+#
+
 class ModelValidationMixin(object):
 class ModelValidationMixin(object):
     """
     """
     Enforce a model's validation through clean() when validating serializer data. This is necessary to ensure we're
     Enforce a model's validation through clean() when validating serializer data. This is necessary to ensure we're
@@ -119,6 +142,10 @@ class WritableSerializerMixin(object):
         return self.serializer_class
         return self.serializer_class
 
 
 
 
+#
+# Pagination
+#
+
 class OptionalLimitOffsetPagination(LimitOffsetPagination):
 class OptionalLimitOffsetPagination(LimitOffsetPagination):
     """
     """
     Override the stock paginator to allow setting limit=0 to disable pagination for a request. This returns all objects
     Override the stock paginator to allow setting limit=0 to disable pagination for a request. This returns all objects