[DRF] APIView에 custom permission class 적용하기

Jay·2022년 9월 16일
0
post-custom-banner

구현할 기능

User와 관련된 회원탈퇴, 복귀, 회원정보 수정 기능을 구현하고자 한다. User 모델의 삭제는 soft deletion 기능을 구현하기 위해 deleted_at 속성을 사용하여 삭제/삭제되지 않은 유저를 구분하였다.

method- DELETE(탈퇴), PUT(복귀), PATCH(수정)
http://127.0.0.1:8000/users/<int:user_id>/ 

endpoint는 위와 같다. soft deletion 기능은 serializer로 구현하였고, views.py에서는 해당 요청을 보내는 사용자가 request 자원의 접근 권한을 가지고 있는지 확인하기 위한 permission 확인 과정을 추가하였다.



Custom Permission class

from rest_framework.permissions import BasePermission
from rest_framework.exceptions import PermissionDenied, NotAuthenticated

class IsOwner(BasePermission):
    
    def has_object_permission(self, request, view, obj):
        if request.user:
            if obj.id == request.user.id:
                return True
            raise PermissionDenied()
        raise NotAuthenticated()

drf에서 다양한 권한 클래스들을 지원한다. 구현하려는 permission과 비슷한 IsOwnerOrReadOnly 클래스가 drf에 구현이 되어있지만 해당 permission은 삭제, 수정과 같은 요청에는 요청을 제한하는 것은 같지만, get 요청과 같이 자원을 변경하지 않는 요청에 대해서는 모든 요청을 제한하지 않는 다는 점에서 내가 구현하고자 하는 기능과는 달랐다. 회원 정보 삭제, 수정, 복구 이외에도 해당 자원을 소유하고 있지 않은 사용자에 대해서 모든 요청은 제한하기 위해 IsOwner라는 permission 클래스를 선언해주었다.

  • 사용자가 헤더에 토큰 없이 요청보낸다 -> NotAuthenticated
  • 헤더에 토큰과 함께 요청을 보냈지만 자원의 소유자가 아니다 -> PermissionDenied
  • 헤더의 토큰과 함께 요청을 보냈고, 토큰의 사용자가 자원의 소유주이다 -> 요청받기


views.py

class UserUpdateView(views.APIView):
    permission_classes = [IsOwner]

    def get_object(self):
        obj = get_object_or_404(get_user_model(), id=self.kwargs['user_id'])
        self.check_object_permissions(self.request, obj)
        return obj

사용할 permission class로 위에서 구현한 IsOwner 클래스를 명시해주었다. 그리고 uri에 명시된 자원을 반환하며, 동시에 권한을 확인하기 위해 get_object() 메소드를 정의해주었다. 우선 get_object_or_404로 요청을 수행할 자원을 가져온다. 그 후 check_object_permissions 메소드로 request와 요청을 수행할 자원인 obj를 전달해준다.
그러면 전달받은 request와 obj를 사용하여 IsOwner 클래스의 has_object_permission 메소드를 호출하게 되고, 이 결과에 따라 요청을 받아들일지 혹은 거부할지 결정하게 된다.

    def delete(self, request, user_id):
        try:
            user = self.get_object()
            if user:
                serializer = BaseUserSerializer(user)
                if serializer.delete():
                    return Response(status=status.HTTP_200_OK)
        except Exception as e:
            return Response(status=status.HTTP_400_BAD_REQUEST)

구현한 회원 탈퇴 api이다. user 객체에 soft delete를 실행하기 위해 해당 객체를 가져오기 위해 위에서 구현한 get_object() 메소드를 호출하였다. 여기서 모델을 사용하여 직접 자원을 가져오게 되면 permission class가 제대로 요청 permission을 검증하지 못하니 주의하도록 한다.

APIView를 사용하면서 동시에 custom permission 클래스를 사용해보기 위해 drf 공식문서에 명시된 viewset을 사용하는 방식이 아닌 다른 방식으로 permission을 구현해보았다. 하지만 위와 같은 방법으로도 내가 계획한대로 기능들이 동작하였고, 동시에 permission check에 관련된 코드들을 읽으면서 permission 확인 매커니즘을 이해할 수 있었다. 만약 해당 구현하는 endpoint에서 수정,삭제와 같은 자원을 수정하는 작업만 수행한다면 IsOwnerOrReadOnly를 사용하여도 무관할 듯 싶다.




reference

https://github.com/encode/django-rest-framework
https://www.django-rest-framework.org/api-guide/permissions/#custom-permissions

post-custom-banner

0개의 댓글