DRF 8 - Permissions

Joey Lee·2020년 7월 11일
3

Django REST Framework

목록 보기
9/16

[참고 문헌]

1. Setting permission globally

만약, 퍼미션을 세팅하지 않으면 누구나 애플리케이션에 접근할 수 있다. 이 디폴트 상태의 코드는 아래와 같다.

'DEFAULT_PERMISSION_CLASSES': [
   'rest_framework.permissions.AllowAny',
]

만약 애플리케이션에 대해 단지 인증된 사용자만 접근하게 하려면, settings.py에서 아래 코드를 추가해 주면 된다.

[setting.py]

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ]
}

하지만 통상 웹서비스에서는 특정 뷰 혹은 클래스에서 인증을 요함으로 좀 더 작은 단위로 쪼개서 Permission을 설정해 주어야 한다.

2. Setting permission locally

Object Level과 Field Level로 나눠서 퍼미션 세팅을 해 줄 수 있다.

먼저 테스트를 위해 admin 계정 하나와 유저 계정 하나를 만든다.
그리고 아래 코드를 작성해서 퍼미션를 설정해 준다.

3. Ebook 권한 관련 코드 리뷰

1) customizing 없는 경우

  • Views.py에서 permissions 모듈 임포트
  • permission_classes에 사용할 permissions 클래스 명시

IsAuthenticatedOrReadOnly 클래스는 누구나 다 읽을 수는 있지만, 다른 기능(생성/수정/삭제)는 인증된 사용자만 접근가능하게 하는 클래스이다. 로그인한 유저가 해당 API에 접근하면 DRF API 웹화면에서 POST가 활성화되지만, 로그인 하지 않은 유저는 비활성화된다.

[api/views.py]

from rest_framework import permissions

class EbookListCreateAPIView(generics.ListCreateAPIView):
    queryset = Ebook.objects.all().order_by("id")
    serializer_class = EbookSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

2) Customized permissions 필요한 경우

2-1) permissions.py 작성

  • api 폴더 내에 permissions.py를 생성, 필요한 클래스를 직접 작성하여 사용
  • IsAdminUserOrReadOnly라는 커스터마이즈된 permission 클래스를 만들기 위해 IsAdminUser를 상속하여 코드를 작성

[permissions.py]

from rest_framework import permissions

class IsAdminUserOrReadOnly(permissions.IsAdminUser):

    def has_permission(self, request, view):
        is_admin = super().has_permission(request, view)
        return request.method in permissions.SAFE_METHODS or is_admin

2-2) views.py 수정

  • customized된 permissions 클래스를 임포트
  • 아래 코드는 IsAuthenticatedOrReadOnly와는 달리 Admin이 아니면 POST에 접근을 할 수 없음. 따라서 standard로 로그인 한 경우라면, POST 메뉴를 이제는 볼 수 없게 됨

[api/views.py]

from ebooks.api.permissions import IsAdminUserOrReadOnly

class EbookListCreateAPIView(generics.ListCreateAPIView):
    queryset = Ebook.objects.all().order_by("id")
    serializer_class = EbookSerializer
    permission_classes = [IsAdminUserOrReadOnly]

4. Custom permissions 작성법

custom permissiond을 구현하려면, BasePermission을 재정의해 다음의 메소드 중 어느 한 쪽 혹은 양 쪽 모두를 구현해야 한다.

  • .has_permission(self, request, view)
  • .has_object_permission(self, request, view, obj)

request에 액세스 권한이 부여되면 메서드는 True를 반환하고 그렇지 않으면 False를 반환해야 한다. request가 읽기 작업인지 아니면 쓰기 작업인지 테스트해야하는 경우 'GET', 'OPTIONS'및 'HEAD'가 포함된 튜플인 SAFE_METHODS 상수와 비교하여 request 메소드를 확인해야 한다.

<예>

if request.method in permissions.SAFE_METHODS:
  # Check permissions for read-only request
else:
  # Check permissions for write request

Note: view-level has_permission 검사가 이미 통과된 경우에만 인스턴스 수준의 has_object_permission 메소드가 호출된다. 또한 인스턴스 수준 검사를 실행하려면 보기 코드에서 .check_object_permissions(request, obj)를 명시적으로 호출해야 한다. generic views를 사용하는 경우 default로 이 옵션이 처리된다.

5. Review 권한 관련 코드 리뷰

회원만 리뷰 작성할 수 있도록 함.
리뷰는 1번만 작성할 수 있도록 함.

1) Review Create View

[views.py]

from rest_framework.exceptions import ValidationError
from ebooks.api.permissions import IsAdminUserOrReadOnly, IsReviewAuthorOrReadOnly

class ReviewCreateAPIView(generics.CreateAPIView):
    queryset = Review.objects.all()
    serializer_class = ReviewSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

    def perform_create(self, serializer):
        ebook_pk = self.kwargs.get("ebook_pk")
        ebook = get_object_or_404(Ebook, pk=ebook_pk)

        review_author = self.request.user

        review_queryset = Review.objects.filter(ebook=ebook, review_author=review_author)

        if review_queryset.exists():
            raise ValidationError("You Have Already Reviewed this Ebook!")

        serializer.save(ebook=ebook, review_author=review_author)

2) Review Update/Delete View

리뷰를 작성한 사람만 리뷰를 업데이트/삭제할 수 있도록 함.

[permissions.py]

class IsReviewAuthorOrReadOnly(permissions.BasePermission):

    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return True

        return obj.review_author == request.user

[views.py]

class ReviewDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Review.objects.all()
    serializer_class = ReviewSerializer
    permission_classes = [IsReviewAuthorOrReadOnly]
profile
안녕하세요!

0개의 댓글