From 63041d788baf502cd45e7a71a48278e3e9c9131b Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Wed, 7 Oct 2020 19:57:45 +0200 Subject: [PATCH] core: update application list API to show applications accessible by policy --- passbook/api/permissions.py | 30 ------------------------------ passbook/api/v2/urls.py | 4 ++-- passbook/core/api/applications.py | 26 ++++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 32 deletions(-) delete mode 100644 passbook/api/permissions.py diff --git a/passbook/api/permissions.py b/passbook/api/permissions.py deleted file mode 100644 index b9da70d82..000000000 --- a/passbook/api/permissions.py +++ /dev/null @@ -1,30 +0,0 @@ -"""permission classes for django restframework""" -from rest_framework.permissions import BasePermission, DjangoObjectPermissions - -from passbook.policies.engine import PolicyEngine -from passbook.policies.models import PolicyBindingModel - - -class CustomObjectPermissions(DjangoObjectPermissions): - """Similar to `DjangoObjectPermissions`, but adding 'view' permissions.""" - - perms_map = { - "GET": ["%(app_label)s.view_%(model_name)s"], - "OPTIONS": ["%(app_label)s.view_%(model_name)s"], - "HEAD": ["%(app_label)s.view_%(model_name)s"], - "POST": ["%(app_label)s.add_%(model_name)s"], - "PUT": ["%(app_label)s.change_%(model_name)s"], - "PATCH": ["%(app_label)s.change_%(model_name)s"], - "DELETE": ["%(app_label)s.delete_%(model_name)s"], - } - - -class PolicyPermissions(BasePermission): - """Permission checker based on PolicyEngine""" - - policy_engine: PolicyEngine - - def has_object_permission(self, request, view, obj: PolicyBindingModel) -> bool: - self.policy_engine = PolicyEngine(obj.policies, request.user, request) - self.policy_engine.request.obj = obj - return self.policy_engine.build().passing diff --git a/passbook/api/v2/urls.py b/passbook/api/v2/urls.py index 7785a0ccc..610c17280 100644 --- a/passbook/api/v2/urls.py +++ b/passbook/api/v2/urls.py @@ -3,10 +3,10 @@ from django.urls import path, re_path from drf_yasg import openapi from drf_yasg.views import get_schema_view from rest_framework import routers +from rest_framework.permissions import AllowAny from passbook.admin.api.overview import AdministrationOverviewViewSet from passbook.admin.api.overview_metrics import AdministrationMetricsViewSet -from passbook.api.permissions import CustomObjectPermissions from passbook.api.v2.messages import MessagesViewSet from passbook.audit.api import EventViewSet from passbook.core.api.applications import ApplicationViewSet @@ -127,7 +127,7 @@ info = openapi.Info( SchemaView = get_schema_view( info, public=True, - permission_classes=(CustomObjectPermissions,), + permission_classes=(AllowAny,), ) urlpatterns = [ diff --git a/passbook/core/api/applications.py b/passbook/core/api/applications.py index f2bcb5e9c..f75df3d16 100644 --- a/passbook/core/api/applications.py +++ b/passbook/core/api/applications.py @@ -1,8 +1,13 @@ """Application API Views""" +from django.db.models import QuerySet +from rest_framework.request import Request +from rest_framework.response import Response from rest_framework.serializers import ModelSerializer from rest_framework.viewsets import ModelViewSet +from rest_framework_guardian.filters import ObjectPermissionsFilter from passbook.core.models import Application +from passbook.policies.engine import PolicyEngine class ApplicationSerializer(ModelSerializer): @@ -29,3 +34,24 @@ class ApplicationViewSet(ModelViewSet): queryset = Application.objects.all() serializer_class = ApplicationSerializer + lookup_field = "slug" + + def _filter_queryset_for_list(self, queryset: QuerySet) -> QuerySet: + """Custom filter_queryset method which ignores guardian, but still supports sorting""" + for backend in list(self.filter_backends): + if backend == ObjectPermissionsFilter: + continue + queryset = backend().filter_queryset(self.request, queryset, self) + return queryset + + def list(self, request: Request, *_, **__) -> Response: + """Custom list method that checks Policy based access instead of guardian""" + queryset = self._filter_queryset_for_list(self.get_queryset()) + allowed_applications = [] + for application in queryset.order_by("name"): + engine = PolicyEngine(application, self.request.user, self.request) + engine.build() + if engine.passing: + allowed_applications.append(application) + serializer = self.get_serializer(allowed_applications, many=True) + return Response(serializer.data)