core: update application list API to show applications accessible by policy

This commit is contained in:
Jens Langhammer 2020-10-07 19:57:45 +02:00
parent bfc1bae0bb
commit 63041d788b
3 changed files with 28 additions and 32 deletions

View File

@ -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

View File

@ -3,10 +3,10 @@ from django.urls import path, re_path
from drf_yasg import openapi from drf_yasg import openapi
from drf_yasg.views import get_schema_view from drf_yasg.views import get_schema_view
from rest_framework import routers from rest_framework import routers
from rest_framework.permissions import AllowAny
from passbook.admin.api.overview import AdministrationOverviewViewSet from passbook.admin.api.overview import AdministrationOverviewViewSet
from passbook.admin.api.overview_metrics import AdministrationMetricsViewSet from passbook.admin.api.overview_metrics import AdministrationMetricsViewSet
from passbook.api.permissions import CustomObjectPermissions
from passbook.api.v2.messages import MessagesViewSet from passbook.api.v2.messages import MessagesViewSet
from passbook.audit.api import EventViewSet from passbook.audit.api import EventViewSet
from passbook.core.api.applications import ApplicationViewSet from passbook.core.api.applications import ApplicationViewSet
@ -127,7 +127,7 @@ info = openapi.Info(
SchemaView = get_schema_view( SchemaView = get_schema_view(
info, info,
public=True, public=True,
permission_classes=(CustomObjectPermissions,), permission_classes=(AllowAny,),
) )
urlpatterns = [ urlpatterns = [

View File

@ -1,8 +1,13 @@
"""Application API Views""" """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.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from rest_framework_guardian.filters import ObjectPermissionsFilter
from passbook.core.models import Application from passbook.core.models import Application
from passbook.policies.engine import PolicyEngine
class ApplicationSerializer(ModelSerializer): class ApplicationSerializer(ModelSerializer):
@ -29,3 +34,24 @@ class ApplicationViewSet(ModelViewSet):
queryset = Application.objects.all() queryset = Application.objects.all()
serializer_class = ApplicationSerializer 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)