Probleme mit permissons in DjangoRestFramework

Django, Flask, Bottle, WSGI, CGI…
Antworten
Benutzeravatar
frogi001
User
Beiträge: 15
Registriert: Samstag 17. März 2018, 00:35

Hallo,
habe ein Problem mit den permissions im DRF:

habe eine Model:

Code: Alles auswählen

from django.db import models

class lic_test(models.Model):
    id = models.AutoField(primary_key='true')
    lfn = models.IntegerField(default=0,blank=True) 
    lic = models.CharField(max_length=128,blank=True)
die serializers.py:

Code: Alles auswählen

from rest_framework import serializers
from .models import lic_test   

class licSerializer(serializers.ModelSerializer):
    class Meta:
        model = lic_test       
        fields = '__all__'   
und die view dazu:

Code: Alles auswählen

from django.shortcuts import render
from django.http.response import JsonResponse
from rest_framework.parsers import JSONParser
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated, DjangoModelPermissions
from .models import lic_test
from .serializers import licSerializer

class liclist(APIView):
    permission_classes = [IsAuthenticated]

    def get(self,request):
        if 'lfn' in request.data:
            nr = request.data['lfn']
            lic1 = lic_test.objects.filter(lfn__in=nr)
        else:
            lic1 = lic_test.objects.all()
        serializer = licSerializer(lic1, many= True)
        return Response(serializer.data)
       
    def post(self, request):
        lic_data = JSONParser().parse(request)
        lic_serializer = licSerializer(data=lic_data)
        if lic_serializer.is_valid():   
            lic_serializer.save()           
            return JsonResponse(lic_serializer.data, status=status.HTTP_201_CREATED) 
        return JsonResponse(lic_serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Im Django-Admin-Bereich habe ich einen Superuser, und einen "normalen" User angelegt.


Mit

Code: Alles auswählen

data = {'lfn':[29]}
data = {'lfn':[]}
data = {} 
response = requests.get("http://*****", json=data, auth=HTTPBasicAuth('user', 'pass'))
response = requests.post("http://*****", json=data, auth=HTTPBasicAuth('user', 'pass'))
kann ich GET und/oder POST-Requests senden, und bekomme auch immer die korrekten JSON-Daten zurückgeliefert,
bzw. neue Datensätze werden angelegt.


Soweit alles ok.
Mit der Einstellung:

Code: Alles auswählen

permission_classes = [DjangoModelPermissions]
kann ich wohl die Django Standard Permissions benutzen.
Hierzu muss ich aber in der view die

Code: Alles auswählen

.queryset
property oder die

Code: Alles auswählen

get_queryset()
Methode implementieren.

Ab hier beißt es aber aus :?
Viele Grüße
HS
Benutzeravatar
sparrow
User
Beiträge: 4183
Registriert: Freitag 17. April 2009, 10:28

Du solltest Klassen so benennen, wie sie laut Konvention benannt werden sollen. CamelCase.

Was genau ist deine Frage?
Benutzeravatar
frogi001
User
Beiträge: 15
Registriert: Samstag 17. März 2018, 00:35

Hallo,
Du solltest Klassen so benennen, wie sie laut Konvention benannt werden sollen. CamelCase.
... stimmt, das ist wirklich schlampig!

Ich will dem "normalen" user XY im Admin-Bereich Rechte geben, oder auch entziehen.
Dort kann man ja für ein model ( in diesem Fall für das Model: lic_test )
die Rechte :
  • add lic_test
  • change lic_test
  • delete lic_test und
  • view lic_test
setzen, oder auch wieder entziehen.

---
... noch etwas zum Coding-Style: wie gesagt, stimmt ... ist nicht nur schlechter Stil, sonder
wahrscheinlich auch kontraproduktiv!
Da gibt es ein "PEP 8 -- Style Guide for Python Code" ... kann ich das als "Grundlage für
korrekten Python-Code-Style" nehmen, oder gibt es da auch andere Meinungen?
Viele Grüße
HS
Benutzeravatar
sparrow
User
Beiträge: 4183
Registriert: Freitag 17. April 2009, 10:28

Genau, PIP 8 ist korrekt.
Der Vorteil ist, dass man bei LicTest sofort sehen würde, dass es sich um eine Klasse handelt.
Ich würde übrigens auch sprechende Namen verwenden. Ich habe keine Ahnung, was ein LicTest ist. Allerdings auch nicht was ein lfn und ein lic ist.

Genau für das beschriebene Szenario sind die DjangoModelPermissions da. Wie du ja schon korrekt aus der Dokumentation herausgelesen hast, funktioniert das nur, wenn der View ein Queryset hat, gegen das die Berechtigung geprüft werden kann. Im einfachsten Fall get_queryset implementieren und das Queryset zurückgeben, das alle Instanzen umfasst - wenn du es nicht einschränken willst. Also Model.objects.all()
Benutzeravatar
frogi001
User
Beiträge: 15
Registriert: Samstag 17. März 2018, 00:35

Habe die view angepasst:

Code: Alles auswählen

from django.shortcuts import render
from django.http.response import JsonResponse
from rest_framework.parsers import JSONParser
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated, DjangoModelPermissions
from .models import lic_test
from .serializers import licSerializer

class liclist(APIView):
    permission_classes = [IsAuthenticated]
    ########## NEU ##########
    def get_queryset(self):
        if request.user.has_permission('read_lic_test')
            return lic_test.objects.all()
        else:
            return None
    ####################
    def get(self,request):
        if 'lfn' in request.data:
            nr = request.data['lfn']
            lic1 = lic_test.objects.filter(lfn__in=nr)
        else:
            lic1 = lic_test.objects.all()
        serializer = licSerializer(lic1, many= True)
        return Response(serializer.data)
       
    def post(self, request):
        lic_data = JSONParser().parse(request)
        lic_serializer = licSerializer(data=lic_data)
        if lic_serializer.is_valid():   
            lic_serializer.save()           
            return JsonResponse(lic_serializer.data, status=status.HTTP_201_CREATED) 
        return JsonResponse(lic_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
habe die get_queryset-Methode aufgenommen :?:
Ungefähr so ?
Viele Grüße
HS
Benutzeravatar
sparrow
User
Beiträge: 4183
Registriert: Freitag 17. April 2009, 10:28

Nein, du nimmst DjangoModelPermissions als PermissionClasses und nimmst in get_queryset keine Filterung vor, sondern gibst das ungefilterte Queyset zurück.

Code: Alles auswählen

    def get_queryset(self):
        return LicTest.objects.all()
Benutzeravatar
frogi001
User
Beiträge: 15
Registriert: Samstag 17. März 2018, 00:35

Hallo,

ich habe die view nochmal angepasst:

Code: Alles auswählen

class liclist(APIView):
    permission_classes = [DjangoModelPermissions]
    ########## NEU ##########
    def get_queryset(self):
        return lic_test.objects.all()
Der User, der keine der 4 permissions hat, bekommt alle Datensätze zu sehen, was nicht sein sollte.

Aber noch seltsamer ist, wenn ich die get_queryset()-Methode nochmal anpasse, so wie nachfolgend:

Code: Alles auswählen

class liclist(APIView):
    permission_classes = [DjangoModelPermissions]
    ########## NEU ##########
    def get_queryset(self):
        return lic_test.objects.filter(id=1)
dann bekommt der User auch alle Datensätze zurückgeliefert :shock:
Viele Grüße
HS
Benutzeravatar
sparrow
User
Beiträge: 4183
Registriert: Freitag 17. April 2009, 10:28

Hier erklärt jemand ausführlich, wie man es benutzt.
Benutzeravatar
frogi001
User
Beiträge: 15
Registriert: Samstag 17. März 2018, 00:35

Hallo,
herzlichen Dank ... das klappt wunderbar!!

Zusammenfassung:

habe die view wie folgt angepasst:

Code: Alles auswählen

from django.shortcuts import render
from django.http.response import JsonResponse
from rest_framework.parsers import JSONParser
from django.shortcuts import get_object_or_404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
#from rest_framework.permissions import IsAuthenticated, DjangoModelPermissions
from .models import lic_test
from .serializers import licSerializer
# Name der app ist 'erest'
from erest.permissions import OwnModPerm

class liclist(APIView):
    permission_classes = [OwnModPerm]
    #permission_classes = [DjangoModelPermissions]

    def get_queryset(self):
        return lic_test.objects.all()

    def get(self,request):
        if 'lfn' in request.data:
            nr = request.data['lfn']
            lic1 = lic_test.objects.filter(lfn__in=nr)
        else:
            lic1 = lic_test.objects.all()
        serializer = licSerializer(lic1, many= True)
        return Response(serializer.data)


    def post(self, request):
        lic_data = JSONParser().parse(request)
        lic_serializer = licSerializer(data=lic_data)
        if lic_serializer.is_valid():
            lic_serializer.save()
            return JsonResponse(lic_serializer.data, status=status.HTTP_201_CREATED)
        return JsonResponse(lic_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
neu ist eigentlich nur die get_queryset()-Methode und

Code: Alles auswählen

permission_classes = [OwnModPerm]
die wie folgt aussieht ( eine neue Datei permissions.py im App-Ordner ):

Code: Alles auswählen

from rest_framework import permissions
from rest_framework.permissions import (DjangoModelPermissions)

class OwnModPerm(DjangoModelPermissions):
    def __init__(self):
        self.perms_map['GET'] = ['%(app_label)s.view_%(model_name)s']
        #self.perms_map['POST'] = ['%(app_label)s.add_%(model_name)s']
Der ganze Aufwand ist NUR notwendig, wenn ich die Permissions für GET anpassen möchte.
Für PUT, POST und DELETE funktioniert alle auch mit:

Code: Alles auswählen

permission_classes = [DjangoModelPermissions]
Viele Grüße
HS
Antworten