From 82e395ca8953872a4a795c754396f703310399bc Mon Sep 17 00:00:00 2001 From: Brian Helba Date: Sat, 28 Feb 2026 14:31:12 -0500 Subject: [PATCH 1/2] Set DRF `permission_classes` and `filter_backends` globally Move the de facto `GuardianPermission` and `GuardianFilter` defaults up to actual DRF defaults. This eliminates boilerplate and ensures that new `ViewSets` will automatically inherit the correct access control. Note, `DatasetViewSet` and `UserViewSet` have exceptional overrides, to preserve their existing behavior. --- uvdat/core/rest/analytics.py | 6 ------ uvdat/core/rest/basemap.py | 3 --- uvdat/core/rest/chart.py | 3 --- uvdat/core/rest/colormap.py | 3 --- uvdat/core/rest/data.py | 4 ---- uvdat/core/rest/dataset.py | 3 +-- uvdat/core/rest/file_item.py | 3 --- uvdat/core/rest/layer.py | 7 ------- uvdat/core/rest/networks.py | 3 --- uvdat/core/rest/project.py | 3 --- uvdat/core/rest/regions.py | 3 --- uvdat/core/rest/user.py | 3 +++ uvdat/settings/base.py | 4 ++-- 13 files changed, 6 insertions(+), 42 deletions(-) diff --git a/uvdat/core/rest/analytics.py b/uvdat/core/rest/analytics.py index cba825a8..fb410759 100644 --- a/uvdat/core/rest/analytics.py +++ b/uvdat/core/rest/analytics.py @@ -6,10 +6,6 @@ from rest_framework.viewsets import ReadOnlyModelViewSet from uvdat.core.models import Project, TaskResult -from uvdat.core.rest.access_control import ( - GuardianFilter, - GuardianPermission, -) import uvdat.core.rest.serializers as uvdat_serializers from uvdat.core.tasks.analytics import analysis_types @@ -17,8 +13,6 @@ class AnalyticsViewSet(ReadOnlyModelViewSet): queryset = TaskResult.objects.all() serializer_class = uvdat_serializers.TaskResultSerializer - permission_classes = [GuardianPermission] - filter_backends = [GuardianFilter] @action( detail=False, diff --git a/uvdat/core/rest/basemap.py b/uvdat/core/rest/basemap.py index f7c647ef..445abc71 100644 --- a/uvdat/core/rest/basemap.py +++ b/uvdat/core/rest/basemap.py @@ -3,12 +3,9 @@ from rest_framework.viewsets import ModelViewSet from uvdat.core.models import Basemap -from uvdat.core.rest.access_control import GuardianFilter, GuardianPermission from uvdat.core.rest.serializers import BasemapSerializer class BasemapViewSet(ModelViewSet): queryset = Basemap.objects.all() serializer_class = BasemapSerializer - permission_classes = [GuardianPermission] - filter_backends = [GuardianFilter] diff --git a/uvdat/core/rest/chart.py b/uvdat/core/rest/chart.py index da078281..f2163b01 100644 --- a/uvdat/core/rest/chart.py +++ b/uvdat/core/rest/chart.py @@ -6,15 +6,12 @@ from rest_framework.viewsets import ModelViewSet from uvdat.core.models import Chart -from uvdat.core.rest.access_control import GuardianFilter, GuardianPermission from uvdat.core.rest.serializers import ChartSerializer, FileItemSerializer class ChartViewSet(ModelViewSet): queryset = Chart.objects.all() serializer_class = ChartSerializer - permission_classes = [GuardianPermission] - filter_backends = [GuardianFilter] def get_queryset(self): qs = super().get_queryset() diff --git a/uvdat/core/rest/colormap.py b/uvdat/core/rest/colormap.py index 32116e15..49303e73 100644 --- a/uvdat/core/rest/colormap.py +++ b/uvdat/core/rest/colormap.py @@ -3,12 +3,9 @@ from rest_framework.viewsets import ModelViewSet from uvdat.core.models import Colormap -from uvdat.core.rest.access_control import GuardianFilter, GuardianPermission from uvdat.core.rest.serializers import ColormapSerializer class ColormapViewSet(ModelViewSet): queryset = Colormap.objects.all() serializer_class = ColormapSerializer - permission_classes = [GuardianPermission] - filter_backends = [GuardianFilter] diff --git a/uvdat/core/rest/data.py b/uvdat/core/rest/data.py index be365f05..2a31e853 100644 --- a/uvdat/core/rest/data.py +++ b/uvdat/core/rest/data.py @@ -12,7 +12,6 @@ from rest_framework.viewsets import GenericViewSet from uvdat.core.models import RasterData, VectorData, VectorFeature -from uvdat.core.rest.access_control import GuardianFilter, GuardianPermission from uvdat.core.rest.explorer import IPyLeafletTokenAuth from uvdat.core.rest.serializers import RasterDataSerializer, VectorDataSerializer @@ -84,9 +83,6 @@ def get_filter_string(filters: dict | None = None): class GenericDataViewSet(GenericViewSet, mixins.RetrieveModelMixin): - permission_classes = [GuardianPermission] - filter_backends = [GuardianFilter] - @property def authentication_classes(self): auth_classes = [IPyLeafletTokenAuth] diff --git a/uvdat/core/rest/dataset.py b/uvdat/core/rest/dataset.py index 20ccf291..d8215dd1 100644 --- a/uvdat/core/rest/dataset.py +++ b/uvdat/core/rest/dataset.py @@ -5,7 +5,7 @@ from rest_framework.viewsets import ModelViewSet from uvdat.core.models import Dataset, DatasetTag -from uvdat.core.rest.access_control import DatasetGuardianPermission, GuardianFilter +from uvdat.core.rest.access_control import DatasetGuardianPermission from uvdat.core.rest.serializers import ( DatasetSerializer, FileItemSerializer, @@ -21,7 +21,6 @@ class DatasetViewSet(ModelViewSet): queryset = Dataset.objects.all() serializer_class = DatasetSerializer permission_classes = [DatasetGuardianPermission] - filter_backends = [GuardianFilter] def get_queryset(self): qs = super().get_queryset() diff --git a/uvdat/core/rest/file_item.py b/uvdat/core/rest/file_item.py index 5cd92491..074abf6c 100644 --- a/uvdat/core/rest/file_item.py +++ b/uvdat/core/rest/file_item.py @@ -6,7 +6,6 @@ from rest_framework.viewsets import ModelViewSet from uvdat.core.models import Dataset, FileItem, RasterData, VectorData -from uvdat.core.rest.access_control import GuardianFilter, GuardianPermission from uvdat.core.rest.serializers import ( FileItemSerializer, RasterDataSerializer, @@ -17,8 +16,6 @@ class FileItemViewSet(ModelViewSet): queryset = FileItem.objects.all() serializer_class = FileItemSerializer - permission_classes = [GuardianPermission] - filter_backends = [GuardianFilter] def get_queryset(self): qs = super().get_queryset() diff --git a/uvdat/core/rest/layer.py b/uvdat/core/rest/layer.py index 868fdc6a..95e12c21 100644 --- a/uvdat/core/rest/layer.py +++ b/uvdat/core/rest/layer.py @@ -7,7 +7,6 @@ from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet from uvdat.core.models import Layer, LayerFrame, LayerStyle -from uvdat.core.rest.access_control import GuardianFilter, GuardianPermission from uvdat.core.rest.serializers import ( LayerFrameSerializer, LayerSerializer, @@ -18,8 +17,6 @@ class LayerViewSet(ReadOnlyModelViewSet): queryset = Layer.objects.select_related("dataset").all() serializer_class = LayerSerializer - permission_classes = [GuardianPermission] - filter_backends = [GuardianFilter] @action(detail=True, methods=["get"]) def frames(self, request, **kwargs): @@ -32,15 +29,11 @@ def frames(self, request, **kwargs): class LayerFrameViewSet(ReadOnlyModelViewSet): queryset = LayerFrame.objects.all() serializer_class = LayerFrameSerializer - permission_classes = [GuardianPermission] - filter_backends = [GuardianFilter] class LayerStyleViewSet(ModelViewSet): queryset = LayerStyle.objects.all() serializer_class = LayerStyleSerializer - permission_classes = [GuardianPermission] - filter_backends = [GuardianFilter] def get_queryset(self): qs = super().get_queryset() diff --git a/uvdat/core/rest/networks.py b/uvdat/core/rest/networks.py index 10e0f870..19f63bfc 100644 --- a/uvdat/core/rest/networks.py +++ b/uvdat/core/rest/networks.py @@ -7,7 +7,6 @@ from rest_framework.viewsets import ModelViewSet from uvdat.core.models import Network -from uvdat.core.rest.access_control import GuardianFilter, GuardianPermission from uvdat.core.rest.serializers import ( NetworkEdgeSerializer, NetworkNodeSerializer, @@ -26,8 +25,6 @@ class GCCResultSerializer(serializers.Serializer): class NetworkViewSet(ModelViewSet): queryset = Network.objects.all() serializer_class = NetworkSerializer - permission_classes = [GuardianPermission] - filter_backends = [GuardianFilter] @action(detail=True, methods=["get"]) def nodes(self, request, **kwargs): diff --git a/uvdat/core/rest/project.py b/uvdat/core/rest/project.py index bec9c2c9..b4a09484 100644 --- a/uvdat/core/rest/project.py +++ b/uvdat/core/rest/project.py @@ -10,7 +10,6 @@ from rest_framework.viewsets import ModelViewSet from uvdat.core.models import Project -from uvdat.core.rest.access_control import GuardianFilter, GuardianPermission from uvdat.core.rest.serializers import ProjectPermissionsSerializer, ProjectSerializer if typing.TYPE_CHECKING: @@ -20,8 +19,6 @@ class ProjectViewSet(ModelViewSet): queryset = Project.objects.all().order_by("name") serializer_class = ProjectSerializer - permission_classes = [GuardianPermission] - filter_backends = [GuardianFilter] def perform_create(self, serializer): project: Project = serializer.save() diff --git a/uvdat/core/rest/regions.py b/uvdat/core/rest/regions.py index 3ec215e2..6f370344 100644 --- a/uvdat/core/rest/regions.py +++ b/uvdat/core/rest/regions.py @@ -4,7 +4,6 @@ from rest_framework.viewsets import GenericViewSet from uvdat.core.models import Region -from uvdat.core.rest.access_control import GuardianFilter, GuardianPermission from .serializers import RegionSerializer @@ -12,5 +11,3 @@ class RegionViewSet(mixins.RetrieveModelMixin, mixins.ListModelMixin, GenericViewSet): queryset = Region.objects.all() serializer_class = RegionSerializer - permission_classes = [GuardianPermission] - filter_backends = [GuardianFilter] diff --git a/uvdat/core/rest/user.py b/uvdat/core/rest/user.py index 5cbc893f..960e1412 100644 --- a/uvdat/core/rest/user.py +++ b/uvdat/core/rest/user.py @@ -5,6 +5,7 @@ from django.contrib.auth.models import User from django.http import HttpResponse from rest_framework.decorators import action +from rest_framework.permissions import IsAuthenticated from rest_framework.viewsets import ReadOnlyModelViewSet from .serializers import UserSerializer @@ -13,6 +14,8 @@ class UserViewSet(ReadOnlyModelViewSet): queryset = User.objects.all() serializer_class = UserSerializer + permission_classes = [IsAuthenticated] + filter_backends = [] @action(detail=False, pagination_class=None) def me(self, request): diff --git a/uvdat/settings/base.py b/uvdat/settings/base.py index 0fdd0abf..77fe2da7 100644 --- a/uvdat/settings/base.py +++ b/uvdat/settings/base.py @@ -129,8 +129,8 @@ REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"] += [ "rest_framework.authentication.TokenAuthentication", ] -# Overwrite this -REST_FRAMEWORK["DEFAULT_PERMISSION_CLASSES"] = ["rest_framework.permissions.IsAuthenticated"] +REST_FRAMEWORK["DEFAULT_PERMISSION_CLASSES"] = ["uvdat.core.rest.access_control.GuardianPermission"] +REST_FRAMEWORK["DEFAULT_FILTER_BACKENDS"] = ["uvdat.core.rest.access_control.GuardianFilter"] CORS_ALLOWED_ORIGINS: list[str] = env.list("DJANGO_CORS_ALLOWED_ORIGINS", cast=str, default=[]) CORS_ALLOWED_ORIGIN_REGEXES: list[str] = env.list( From c9b105c23fecd6df7c2ee05b580bf43fac44dd7d Mon Sep 17 00:00:00 2001 From: Brian Helba Date: Wed, 4 Mar 2026 23:36:08 -0500 Subject: [PATCH 2/2] Move `access_control` out of `rest` module --- uvdat/core/{rest => }/access_control.py | 0 uvdat/core/rest/dataset.py | 2 +- uvdat/settings/base.py | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) rename uvdat/core/{rest => }/access_control.py (100%) diff --git a/uvdat/core/rest/access_control.py b/uvdat/core/access_control.py similarity index 100% rename from uvdat/core/rest/access_control.py rename to uvdat/core/access_control.py diff --git a/uvdat/core/rest/dataset.py b/uvdat/core/rest/dataset.py index d8215dd1..ebdbddb4 100644 --- a/uvdat/core/rest/dataset.py +++ b/uvdat/core/rest/dataset.py @@ -4,8 +4,8 @@ from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet +from uvdat.core.access_control import DatasetGuardianPermission from uvdat.core.models import Dataset, DatasetTag -from uvdat.core.rest.access_control import DatasetGuardianPermission from uvdat.core.rest.serializers import ( DatasetSerializer, FileItemSerializer, diff --git a/uvdat/settings/base.py b/uvdat/settings/base.py index 77fe2da7..58f5cca7 100644 --- a/uvdat/settings/base.py +++ b/uvdat/settings/base.py @@ -129,8 +129,8 @@ REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"] += [ "rest_framework.authentication.TokenAuthentication", ] -REST_FRAMEWORK["DEFAULT_PERMISSION_CLASSES"] = ["uvdat.core.rest.access_control.GuardianPermission"] -REST_FRAMEWORK["DEFAULT_FILTER_BACKENDS"] = ["uvdat.core.rest.access_control.GuardianFilter"] +REST_FRAMEWORK["DEFAULT_PERMISSION_CLASSES"] = ["uvdat.core.access_control.GuardianPermission"] +REST_FRAMEWORK["DEFAULT_FILTER_BACKENDS"] = ["uvdat.core.access_control.GuardianFilter"] CORS_ALLOWED_ORIGINS: list[str] = env.list("DJANGO_CORS_ALLOWED_ORIGINS", cast=str, default=[]) CORS_ALLOWED_ORIGIN_REGEXES: list[str] = env.list(