Django REST Framework/DRF 일반

[DRF] 공식 문서 - Permissions 정리

bluebamus 2024. 1. 20.

 1. 권한(Permissions)

   - 인증 및 속도 제한과 함께 권한은 요청이 접근을 허용 할지 거부할지를 결정한다.

   - 권한 검사는 항상 뷰의 시작부분에서 실행되며, 다른 코드가 실행되기 전에 이루어진다. 권한 검사는 일반적으로 request.user와 request.auth 속성의 인증 정보를 사용하여 들어오는 요청이 허용되어야 하는지 결정힌다.

    - 권한은 다른 클래스의 사용자들이 API의 다른 부분에 접근하는 것을 허용하거나 거부하는데 사용된다.

   - 가장 간단한 스타일의 권한은 인증된 모든 사용자에게 접근을 허용하고, 인증되지 않은 모든 사용자에게 접근을 거부하는 것이다. 이는 REST 프레임워크의 IsAuthenticated 클래스에 해당한다.

   - 조금 덜 엄격한 스타일의 권한은 인증된 사용자에게는 전체 접근을 허용하고, 인증되지 않은 사용자에게는 읽기 전용 접근을 허용하는 것이다. 이는 REST 프레임워크의 IsAuthenticatedOrReadOnly 클래스에 해당한다.

 

   1) 권한 결정 방법(How permissions are determined)

      - REST 프레임워크에서의 권한은 항상 권한 클래스 목록으로 정의된다.

      - 뷰의 주요 본문을 실행하기 전에 목록에 있는 각 권한이 확인된다. 만약 어떤 권한 검사라도 실패하면, exceptions.PermissionDenied 또는 exceptions.NotAuthenticated 예외가 발생하고, 뷰의 주요 본문은 실행되지 않는다.

      - 권한 검사가 실패하면, 다음의 규칙에 따라 "403 Forbidden" 또는 "401 Unauthorized" 응답이 반환된다.

         - 요청은 성공적으로 인증되었지만, 권한이 거부되었다. - HTTP 403 Forbidden 응답이 반환된다.

         - 요청이 성공적으로 인증되지 않았고, 가장 우선순위가 높은 인증 클래스가 WWW-Authenticate 헤더를 사용하지 않는다. - HTTP 403 Forbidden 응답이 반환된다.

         - 요청이 성공적으로 인증되지 않았고, 가장 우선순위가 높은 인증 클래스가 WWW-Authenticate 헤더를 사용합니다. - 적절한 WWW-Authenticate 헤더와 함께 HTTP 401 Unauthorized 응답이 반환된다.

 

   2) 객체 수준 권한(Object level permissions)

      - REST 프레임워크 권한은 object-level의 권한 설정을 지원한다. object-level 권한은 사용자가 특정 객체에 대해 작업을 수행할 수 있는지 여부를 결정하는데 사용되며, 이는 일반적으로 모델 인스턴스일 것이다.

      - 객체 수준 권한은 .get_object()가 호출될 때 REST 프레임워크의 일반 뷰에 의해 실행된다. 뷰 수준 권한과 마찬가지로, 사용자가 주어진 객체에 대해 작업을 수행할 수 없는 경우 exceptions.PermissionDenied 예외가 발생한다.

      - 자신의 뷰를 작성하고 객체 수준 권한을 강제하려거나, 일반 뷰에서 get_object 메서드를 오버라이드하는 경우, 객체를 검색한 시점에서 뷰에 .check_object_permissions(request, obj) 메서드를 명시적으로 호출해야 한다.

      - 이는 PermissionDenied 또는 NotAuthenticated 예외를 발생시키거나, 뷰가 적절한 권한이 있는 경우 간단히 반환한다.

def get_object(self):
    obj = get_object_or_404(self.get_queryset(), pk=self.kwargs["pk"])
    self.check_object_permissions(self.request, obj)
    return obj

      - 주의: DjangoObjectPermissions를 제외하고, rest_framework.permissions에 제공된 권한 클래스들은 객체 권한을 확인하는데 필요한 메소드를 구현하지 않는다.

      - 제공된 권한 클래스를 사용하여 객체 권한을 확인하려는 경우, 이를 서브클래스화하고 커스텀 권한 섹션(아래)에서 설명한 has_object_permission() 메소드를 구현해야 한다.

 

      - 예시 : 

         -  각 블로그 포스트는 한 명의 사용자(작성자)에게 속하고, 이 작성자만이 자신의 블로그 포스트를 편집하거나 삭제할 수 있는 권한을 가진다. 그러나 누구나 블로그 포스트를 읽을 수 있어야 한다. 이 경우, 다음과 같이 사용자 정의 권한 클래스를 구현할 수 있다.

from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    객체 수준 권한. 오직 객체의 소유자만 수정 또는 삭제할 수 있음.
    다른 사용자는 읽기만 가능함.
    """
    def has_object_permission(self, request, view, obj):
        # GET, HEAD, OPTIONS 요청은 안전한 메소드로 판단되므로, 이들 요청은 항상 허용
        if request.method in permissions.SAFE_METHODS:
            return True

        # 쓰기 권한은 오직 해당 객체의 소유자에게만 부여
        # 이 예시에서는 'author'가 객체의 소유자를 나타내는 속성임
        return obj.author == request.user

      1. Limitations of object level permissions

         - 성능상의 이유로 일반 뷰는 객체 목록을 반환할 때 쿼리셋의 각 인스턴스에 자동으로 객체 수준 권한을 적용하지 않는다.

         - 객체 수준 권한을 사용할 때 종종 쿼리셋을 적절하게 필터링하여, 사용자가 볼 수 있는 인스턴스가 그들이 보기를 허용된 인스턴스만이 되도록 보장하려고 할 것이다.

         - get_object() 메소드가 호출되지 않기 때문에, has_object_permission() 메소드의 객체 수준 권한은 객체를 생성할 때 적용되지 않는다. 객체 생성을 제한하려면, Serializer 클래스에서 권한 검사를 구현하거나 ViewSet 클래스의 perform_create() 메소드를 오버라이드해야 한다.

 

   3) 권한 정책 설정(Setting the permission policy)

      - 기본 권한 정책은 DEFAULT_PERMISSION_CLASSES 설정을 사용하여 전역적으로 설정할 수 있다.

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

 

         - 이 설정이 지정되지 않으면, 제한 없는 접근을 허용하는 것으로 기본 설정된다.

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

 

         - 또한 APIView 클래스 기반 뷰를 사용하여 뷰 별 또는 뷰셋 별로 인증 정책을 설정할 수도 있다.

from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

class ExampleView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, format=None):
        content = {
            'status': 'request was permitted'
        }
        return Response(content)

 

         - 또는 함수 기반 뷰에 @api_view 데코레이터를 사용하는 경우에도 가능하다.

from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def example_view(request, format=None):
    content = {
        'status': 'request was permitted'
    }
    return Response(content)

         - 주의: 클래스 속성이나 데코레이터를 통해 새로운 권한 클래스를 설정하는 것은, 뷰에게 settings.py 파일에 설정된 기본 목록을 무시하도록 지시하는 것이다.


         - rest_framework.permissions.BasePermission에서 상속받는 한, 권한은 표준 Python 비트 연산자를 사용하여 구성할 수 있다. 예를 들어, IsAuthenticatedOrReadOnly는 다음과 같이 작성될 수 있다.

from rest_framework.permissions import BasePermission, IsAuthenticated, SAFE_METHODS
from rest_framework.response import Response
from rest_framework.views import APIView

class ReadOnly(BasePermission):
    def has_permission(self, request, view):
        return request.method in SAFE_METHODS

class ExampleView(APIView):
    permission_classes = [IsAuthenticated|ReadOnly]

    def get(self, request, format=None):
        content = {
            'status': 'request was permitted'
        }
        return Response(content)

 


         - 주의: & (and), | (or), ~ (not)을 지원합니다.


 

 2. API Reference

   1) 누구나 허용(AllowAny)

      - AllowAny 권한 클래스는 요청이 인증되었는지 여부에 관계없이 제한 없는 접근을 허용한다.

      - 이 권한은 엄격하게 필요한 것은 아니며, 권한 설정에 빈 리스트나 튜플을 사용함으로써 동일한 결과를 얻을 수 있지만, 이 클래스를 지정하는 것이 더 유용할 수 있다. 왜냐하면 이것은 의도를 명확하게 만들어주기 때문이다.

 

   2) 인증된 사용자(IsAuthenticated)

      - IsAuthenticated 권한 클래스는 인증되지 않은 모든 사용자에게 권한을 거부하고, 그 외의 경우에는 권한을 허용한다.

      - 이 권한은 API가 등록된 사용자만 접근할 수 있도록 하고 싶은 경우에 적합하다.

 

   3) 관리자 사용자(IsAdminUser)

      - IsAdminUser 권한 클래스는 모든 사용자에게 권한을 거부하며, user.is_staff가 True인 경우에만 권한을 허용한다.

      - 이 권한은 API가 신뢰할 수 있는 관리자의 일부만 접근할 수 있도록 하고 싶은 경우에 적합하다.

 

   4) 인증된 사용자 또는 읽기 전용(IsAuthenticatedOrReadOnly)

      - IsAuthenticatedOrReadOnly는 인증된 사용자가 모든 요청을 수행할 수 있게 한다. 인증되지 않은 사용자의 요청은 요청 메소드가 "안전한" 메소드인 GET, HEAD 또는 OPTIONS 중 하나일 경우에만 허용된다.

      - 이 권한은 API가 익명 사용자에게 읽기 권한을 허용하고, 쓰기 권한은 인증된 사용자에게만 허용하도록 하고 싶은 경우에 적합하다.

 

   5) Django 모델 권한(DjangoModelPermissions)

      - 이 권한 클래스는 Django의 표준 django.contrib.auth 모델 권한과 연결된다. 이 권한은 .queryset 속성이나 get_queryset() 메소드를 가진 뷰에만 적용해야 한다. 사용자가 인증되었고 관련 모델 권한이 할당되어 있는 경우에만 인증이 부여된다. 적절한 모델은 get_queryset().model 또는 queryset.model을 확인하여 결정된다.
         - GET 요청은 사용자가 모델에 대한 보기 또는 변경 권한을 가지고 있어야 한다.
         - POST 요청은 사용자가 모델에 대한 추가 권한을 가지고 있어야 한다.
         - PUT과 PATCH 요청은 사용자가 모델에 대한 변경 권한을 가지고 있어야 한다.
         - DELETE 요청은 사용자가 모델에 대한 삭제 권한을 가지고 있어야 한다.


      - 기본 동작도 또한 사용자 정의 모델 권한을 지원하기 위해 오버라이드 될 수 있다.

      - 사용자 정의 모델 권한을 사용하려면, DjangoModelPermissions를 오버라이드하고 .perms_map 속성을 설정하면 된다. 자세한 내용은 소스 코드를 참조하면 된다.

 

   6) Django 모델 권한 또는 익명 읽기 전용(DjangoModelPermissionsOrAnonReadOnly)

      - DjangoModelPermissions와 유사하지만, 인증되지 않은 사용자도 API에 대한 읽기 전용 접근을 허용한다.

 

   7) Django 객체 권한(DjangoObjectPermissions)

      - 이 권한 클래스는 모델에 대한 객체별 권한을 허용하는 Django의 표준 객체 권한 프레임워크에 연결된다. 이 권한 클래스를 사용하려면, django-guardian과 같은 객체 수준 권한을 지원하는 권한 백엔드를 추가해야 한다.

      - DjangoModelPermissions와 마찬가지로, 이 권한은 .queryset 속성이나 .get_queryset() 메소드를 가진 뷰에만 적용해야 한다. 사용자가 인증되었고 관련 객체별 권한과 관련 모델 권한이 할당되어 있는 경우에만 인증이 부여된다.

         - POST 요청은 사용자가 모델 인스턴스에 대한 추가 권한을 가지고 있어야 한다.

         - PUT과 PATCH 요청은 사용자가 모델 인스턴스에 대한 변경 권한을 가지고 있어야 한다.

         - DELETE 요청은 사용자가 모델 인스턴스에 대한 삭제 권한을 가지고 있어야 한다.

 

      - DjangoObjectPermissions는 django-guardian 패키지를 필요로 하지 않으며, 다른 객체 수준 백엔드를 동일하게 잘 지원해야 한다.

 

      - DjangoModelPermissions와 마찬가지로, DjangoObjectPermissions를 오버라이드하고 .perms_map 속성을 설정하여 사용자 정의 모델 권한을 사용할 수 있다. 자세한 내용은 소스 코드를 참조하면 된다.


      - 주의: GET, HEAD, OPTIONS 요청에 대한 객체 수준 뷰 권한이 필요하고 객체 수준 권한 백엔드로 django-guardian을 사용하고 있다면, djangorestframework-guardian2 패키지에서 제공하는 DjangoObjectPermissionsFilter 클래스를 사용하는 것을 고려해 보는 것이 좋다. 이 클래스는 리스트 엔드포인트가 사용자가 적절한 뷰 권한을 가진 객체만을 포함한 결과를 반환하도록 보장한다.


 

 3. 사용자 정의 권한(Custom permissions)

   - 사용자 정의 권한을 구현하려면, BasePermission을 오버라이드하고 다음 메소드 중 하나 또는 둘 다를 구현해야 한다.
      - .has_permission(self, request, view)
      - .has_object_permission(self, request, view, obj)


   - 이 메소드들은 요청이 접근을 허용해야 하는 경우 True를, 그렇지 않은 경우 False를 반환해야 한다.

   - 요청이 읽기 작업인지 쓰기 작업인지 테스트해야 하는 경우, 요청 메소드를 'GET', 'OPTIONS', 'HEAD'를 포함하는 튜플인 상수 SAFE_METHODS와 비교해야 한다.

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

 


   - 주의: 인스턴스 수준의 has_object_permission 메소드는 뷰 수준의 has_permission 검사가 이미 통과된 경우에만 호출된다. 또한, 인스턴스 수준의 검사를 실행하려면, 뷰 코드에서 .check_object_permissions(request, obj)를 명시적으로 호출해야 한다. 일반 뷰를 사용하는 경우 이는 기본적으로 처리된다. (함수 기반 뷰는 객체 권한을 명시적으로 확인해야 하며, 실패시 PermissionDenied를 발생시켜야 합니다.)

 


 

   - 사용자 정의 권한은 테스트가 실패하면 PermissionDenied 예외를 발생시킨다. 예외와 연관된 오류 메시지를 변경하려면, 사용자 정의 권한에 직접 메시지 속성을 구현해야 한다. 그렇지 않으면 PermissionDenied의 default_detail 속성이 사용된다. 마찬가지로, 예외와 연관된 코드 식별자를 변경하려면, 사용자 정의 권한에 직접 코드 속성을 구현해야 한다 - 그렇지 않으면 PermissionDenied의 default_code 속성이 사용된다.

from rest_framework import permissions

class CustomerAccessPermission(permissions.BasePermission):
    message = 'Adding customers not allowed.'

    def has_permission(self, request, view):
         ...

 

   1) Examples

   - 다음은 들어오는 요청의 IP 주소를 차단 목록과 비교하고, IP가 차단되었을 경우 요청을 거부하는 권한 클래스의 예시이다.

from rest_framework import permissions

class BlocklistPermission(permissions.BasePermission):
    """
    Global permission check for blocked IPs.
    """

    def has_permission(self, request, view):
        ip_addr = request.META['REMOTE_ADDR']
        blocked = Blocklist.objects.filter(ip_addr=ip_addr).exists()
        return not blocked

 

   - 다음은 들어오는 요청의 IP 주소를 차단 목록과 비교하고, IP가 차단되었을 경우 요청을 거부하는 권한 클래스의 예시이다.모든 들어오는 요청에 대해 실행되는 전역 권한뿐만 아니라, 특정 객체 인스턴스에 영향을 미치는 작업에 대해서만 실행되는 객체 수준 권한도 만들 수 있다.

class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    Object-level permission to only allow owners of an object to edit it.
    Assumes the model instance has an `owner` attribute.
    """

    def has_object_permission(self, request, view, obj):
        # Read permissions are allowed to any request,
        # so we'll always allow GET, HEAD or OPTIONS requests.
        if request.method in permissions.SAFE_METHODS:
            return True

        # Instance must have an attribute named `owner`.
        return obj.owner == request.user

 

   - 일반 뷰는 적절한 객체 수준 권한을 확인할 것이지만, 사용자 정의 뷰를 작성하는 경우, 객체 수준 권한 검사를 직접 확인해야 한다. 객체 인스턴스를 얻은 후 뷰에서 self.check_object_permissions(request, obj)를 호출하여 이를 수행할 수 있다. 이 호출은 객체 수준 권한 검사 중 어떤 것이든 실패하면 적절한 APIException을 발생시키고, 그렇지 않으면 단순히 반환한다.

   - 또한 일반 뷰는 단일 모델 인스턴스를 검색하는 뷰에 대해서만 객체 수준 권한을 확인할 것이다. 리스트 뷰의 객체 수준 필터링이 필요한 경우, 쿼리셋을 별도로 필터링해야 한다. 자세한 내용은 필터링 문서를 참조하면 된다.

      - filtering documentation : https://www.django-rest-framework.org/api-guide/filtering/

 

 4. 접근 제한 방법 개요(Overview of access restriction methods)

   - REST 프레임워크는 경우에 따른 접근 제한을 커스터마이즈하는 세 가지 다른 방법을 제공한다. 이들은 다른 시나리오에서 적용되며, 그 효과와 제한 사항도 다르다.
      - queryset/get_queryset(): 데이터베이스의 기존 객체의 일반적인 가시성을 제한한다. 쿼리셋은 어떤 객체가 나열될 것인지, 어떤 객체가 수정되거나 삭제될 수 있는지를 제한한다. get_queryset() 메소드는 현재 작업에 따라 다른 쿼리셋을 적용할 수 있다.

      - permission_classes/get_permissions(): 현재 작업, 요청, 대상 객체를 기반으로 한 일반적인 권한 검사이다. 객체 수준 권한은 검색, 수정, 삭제 작업에만 적용될 수 있다. 리스트와 생성에 대한 권한 검사는 전체 객체 유형에 적용된다. (리스트의 경우: 쿼리셋의 제한 사항에 따라 달라진다.)

      - serializer_class/get_serializer(): 입력과 출력에 대한 모든 객체에 적용되는 인스턴스 수준의 제한이다. Serializer는 요청 컨텍스트에 접근할 수 있다. get_serializer() 메소드는 현재 작업에 따라 다른 Serializer를 적용할 수 있다.


   - 다음 표는 접근 제한 방법과 그들이 어떤 작업에 대해 어떤 수준의 제어를 제공하는지를 나열한다.

  queryset permission_classes serializer_class
Action: list global global object-level*
Action: create no global object-level
Action: retrieve global object-level object-level
Action: update global object-level object-level
Action: partial_update global object-level object-level
Action: destroy global global object-level no
Can reference action in decision no** yes no**
Can reference request in decision no** yes yes

 

   * Serializer 클래스는 리스트 작업에서 PermissionDenied를 발생시키면 전체 리스트가 반환되지 않는다. 
   ** get_*() 메소드는 현재 뷰에 접근할 수 있으며, 요청이나 작업에 따라 다른 Serializer 또는 QuerySet 인스턴스를 반환할 수 있다.

 

 5. Third party packages

   1) DRF - Access Policy

      - Django REST - Access Policy 패키지는 선언적인 정책 클래스에 복잡한 접근 규칙을 정의하고 뷰셋이나 함수 기반 뷰에 첨부하는 방법을 제공한다. 이 정책들은 AWS의 Identity & Access Management 정책과 유사한 형식의 JSON에서 정의된다.

         - Django REST - Access Policy : https://github.com/rsinger86/drf-access-policy

 

      - 예시 :

         - 이 시나리오에서는 모든 사용자가 블로그 포스트를 읽을 수 있고, 인증된 사용자만이 새 포스트를 작성할 수 있으며, 포스트의 작성자만이 해당 포스트를 수정하거나 삭제할 수 있다. 관리자는 모든 포스트를 수정하거나 삭제할 수 있다.

from rest_framework import viewsets
from rest_framework_access_policy import AccessPolicy

class BlogPostAccessPolicy(AccessPolicy):
    statements = [
        {
            "action": ["list", "retrieve"],
            "principal": "*",
            "effect": "allow",
        },
        {
            "action": ["create"],
            "principal": "authenticated",
            "effect": "allow",
        },
        {
            "action": ["update", "partial_update", "destroy"],
            "principal": "authenticated",
            "effect": "allow",
            "condition": "is_owner_or_admin",
        },
    ]

    def is_owner_or_admin(self, request, view, action) -> bool:
        # Check if the user is the owner of the object or an admin
        return request.user == view.get_object().author or request.user.is_staff

class BlogPostViewSet(viewsets.ModelViewSet):
    permission_classes = [BlogPostAccessPolicy]

    # ... other viewset methods ...

 

         - 위 코드에서 BlogPostAccessPolicy는 블로그 포스트에 대한 접근 정책을 정의하고 있습니다. 이 정책에는 세 가지 조건이 있다:

            - 모든 사용자("principal": "*")는 포스트를 리스트로 보거나 개별 포스트를 조회("action": ["list", "retrieve"])할 수 있다.

            - 인증된 사용자("principal": "authenticated")만이 새 포스트를 작성("action": ["create"])할 수 있다.

            - 인증된 사용자는 자신이 작성한 포스트를 수정하거나 삭제("action": ["update", "partial_update", "destroy"])할 수 있다. 이는 is_owner_or_admin 조건 함수를 통해 확인됩니다. 이 함수는 요청한 사용자가 포스트의 작성자거나 관리자인 경우에만 True를 반환한다.

 

   2) Composed Permissions

      - Composed Permissions 패키지는 작고 재사용 가능한 컴포넌트를 사용하여 복잡하고 다중 깊이(논리 연산자와 함께)의 권한 객체를 정의하는 간단한 방법을 제공한다.

         - Composed Permissions : https://github.com/niwinz/djangorestframework-composed-permissions

 

      - 예시

         - 이 시나리오에서는 모든 사용자가 블로그 포스트를 읽을 수 있고, 인증된 사용자만이 새 포스트를 작성할 수 있으며, 포스트의 작성자만이 해당 포스트를 수정하거나 삭제할 수 있다.

from rest_framework import viewsets
from composed_permissions import And, Or, Not, Add, Subtract, BasePermission

class IsAuthenticated(BasePermission):
    def has_permission(self, request, view):
        return request.user and request.user.is_authenticated

class IsOwner(BasePermission):
    def has_object_permission(self, request, view, obj):
        return obj.author == request.user

class ReadOnly(BasePermission):
    def has_permission(self, request, view):
        return request.method in ['GET', 'HEAD', 'OPTIONS']

class BlogPostViewSet(viewsets.ModelViewSet):
    permission_classes = [Or(IsAuthenticated, ReadOnly), And(IsAuthenticated, IsOwner)]
    # ... other viewset methods ...

 

         - 위 코드에서 IsAuthenticated, IsOwner, ReadOnly는 각각 인증된 사용자, 객체의 소유자, 읽기 전용 요청에 대한 권한을 정의하는 클래스이다. BlogPostViewSet에서는 이들 권한을 조합하여 사용하고 있다. 특히, 인증된 사용자 또는 읽기 전용 요청(Or(IsAuthenticated, ReadOnly))에 대해 접근을 허용하며, 인증된 사용자이면서 해당 객체의 소유자(And(IsAuthenticated, IsOwner))인 경우에만 객체에 대한 수정이나 삭제를 허용한다.

         - 이렇게 Composed Permissions 패키지를 사용하면, 다양한 권한 조건을 논리 연산자를 통해 쉽게 조합할 수 있다.

 

   3) REST Condition

      - REST Condition 패키지는 복잡한 권한을 간단하고 편리하게 구축하는 또 다른 확장이다. 이 확장은 권한을 논리 연산자로 결합할 수 있게 해준다.

         - REST Condition : https://github.com/caxap/rest_condition

 

      - 예시

         - 모델명: Book
         - 필드: title (문자열), author (문자열), publication_date (날짜)
         - 권한 설정: 인증된 사용자 중에서도 슈퍼유저만 책을 수정할 수 있고, 일반 사용자는 책을 조회만 할 수 있도록 권한 설정

 

         1. 패키지 설치 

pip install django-rest-condition

 

         2. 권한 클래스 생성: 아래의 예시 코드는 슈퍼유저에게는 모든 권한을 부여하고, 일반 사용자에게는 읽기 권한만 부여하는 코드이다.

from rest_condition import Or, And
from rest_framework.permissions import BasePermission, IsAuthenticated, SAFE_METHODS

class IsSuperuser(BasePermission):
    def has_permission(self, request, view):
        return request.user and request.user.is_superuser

class IsReadOnly(BasePermission):
    def has_permission(self, request, view):
        return request.method in SAFE_METHODS

class BookPermissions(Or(IsSuperuser, IsReadOnly)):
    pass

 

          3. 뷰 생성 : 아래의 예시 코드는 rest_condition 패키지를 사용하여 조건부 권한을 구현하는 뷰를 생성하는 코드이다.

from rest_framework import generics
from .models import Book
from .serializers import BookSerializer
from .permissions import BookPermissions

class BookList(generics.ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = [BookPermissions]

class BookDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = [BookPermissions]

 

          4. URL 매핑 : 뷰를 URL에 매핑시켜야 클라이언트가 해당 API를 호출할 수 있다. 아래의 예시 코드는 /books/ URL에 BookList 뷰를, /books/<pk>/ URL에 BookDetail 뷰를 매핑하는 코드이다.

from django.urls import path
from .views import BookList, BookDetail

urlpatterns = [
    path('books/', BookList.as_view(), name='book-list'),
    path('books/<int:pk>/', BookDetail.as_view(), name='book-detail'),
]

 

   4) DRY Rest Permissions

      - DRY Rest Permissions 패키지는 개별 기본 및 사용자 정의 작업에 대해 다른 권한을 정의할 수 있는 기능을 제공한다. 이 패키지는 앱의 데이터 모델에 정의된 관계에서 파생된 권한을 가진 앱을 위해 만들어졌다. 또한 권한 검사가 API의 Serializer를 통해 클라이언트 앱으로 반환되는 것을 지원한다. 또한 사용자 별로 검색하는 데이터를 제한하기 위해 기본 및 사용자 정의 리스트 작업에 권한을 추가하는 것을 지원한다.

         - DRY Rest Permissions : https://github.com/FJNR-inc/dry-rest-permissions

 

      - 예시

 

      1. 패키지 설치:

pip install dry-rest-permissions

 

      2. 권한 클래스 생성 : DRY REST Permissions 패키지를 사용하여 재사용 가능한 권한 클래스를 생성한다. 아래의 예시 코드는 인증된 사용자만 책을 생성하거나 수정할 수 있도록 권한을 설정하는 코드이다.

from dry_rest_permissions.generics import DRYPermissions

class BookPermissions(DRYPermissions):
    def has_permission(self, request, view):
        if view.action == 'create' or view.action == 'update':
            return request.user and request.user.is_authenticated
        return super().has_permission(request, view)

    def has_object_permission(self, request, view, obj):
        if view.action == 'create' or view.action == 'update':
            return obj.author == request.user
        return super().has_object_permission(request, view, obj)

 

      3. 뷰 생성 : 뷰는 API 요청을 처리하고, 응답을 반환하는 역할을 한다. 아래의 예시 코드는 DRY REST Permissions를 사용하여 책 생성, 수정, 삭제를 처리하는 뷰를 생성하는 코드이다.

from rest_framework import generics
from .models import Book
from .serializers import BookSerializer
from .permissions import BookPermissions

class BookList(generics.ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = [BookPermissions]

class BookDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = [BookPermissions]

 

      3. URL 매핑 : 뷰를 URL에 매핑시켜야 클라이언트가 해당 API를 호출할 수 있다. 아래의 예시 코드는 /books/ URL에 BookList 뷰를, /books/<pk>/ URL에 BookDetail 뷰를 매핑하는 코드이다.

 

 

   5) Django Rest Framework Roles

      - Django Rest Framework Roles 패키지는 여러 유형의 사용자에 대해 API를 매개변수화하는 것을 쉽게 만들어준다.

         - Django Rest Framework Roles : https://github.com/computer-lab/django-rest-framework-roles

 

      - 예시

         - 모델명: Book
         - 필드: title (문자열), author (문자열), publication_date (날짜)
         - 롤: 슈퍼유저(Superuser), 일반 사용자(User)
         - 권한 설정: 슈퍼유저는 모든 권한을 가지고, 일반 사용자는 책을 조회할 수 있고 새로운 책을 생성할 수 있으며, 책을 수정하거나 삭제할 수는 없도록 권한 설정

 

      1. 패키지 설치

pip install django-rest-framework-roles

 

      2. 롤(Role) 정의 : 아래의 예시 코드는 슈퍼유저와 일반 사용자를 정의하는 코드이다.

from roles.roles import AbstractUserRole

class Superuser(AbstractUserRole):
    available_permissions = {
        'create_book': True,
        'view_book': True,
        'change_book': True,
        'delete_book': True,
    }

class User(AbstractUserRole):
    available_permissions = {
        'create_book': True,
        'view_book': True,
        'change_book': False,
        'delete_book': False,
    }

 

      3. 권한 클래스 생성: 각 롤(Role)에 대한 권한 클래스를 생성한다. 아래의 예시 코드는 django-rest-framework-roles 패키지를 사용하여 롤(Role) 기반의 권한을 구현하는 코드이다.

from rest_framework.permissions import BasePermission
from roles.roles import get_user_roles, has_permission

class HasRole(BasePermission):
    def has_permission(self, request, view):
        roles = get_user_roles(request.user)
        return has_permission(view.action, roles)

class BookPermissions(HasRole):
    required_roles = {
        'create': [Superuser, User],
        'list': [Superuser, User],
        'retrieve': [Superuser, User],
        'update': [Superuser],
        'partial_update': [Superuser],
        'destroy': [Superuser],
    }

 

      4. 뷰 생성

from rest_framework import generics
from .models import Book
from .serializers import BookSerializer
from .permissions import BookPermissions

class BookList(generics.ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = [BookPermissions]

class BookDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = [BookPermissions]

 

       5. URL 매핑 :

from django.urls import path
from .views import BookList, BookDetail

urlpatterns = [
    path('books/', BookList.as_view(), name='book-list'),
    path('books/<int:pk>/', BookDetail.as_view(), name='book-detail'),
]

 

   6) Rest Framework Roles

      - Rest Framework Roles는 역할에 따라 뷰를 보호하는 것을 매우 쉽게 만들어준다. 가장 중요한 것은, 이것이 모델과 뷰에서 접근성 로직을 분리하고 깔끔하고 사람이 읽을 수 있는 방식으로 표현할 수 있게 해준다.

         - Rest Framework Roles : https://github.com/Pithikos/rest-framework-roles

 

      - 예시

 

 

   7) Django REST Framework API Key

      - Django REST Framework API Key 패키지는 API 키 인증을 API에 추가하는 권한 클래스, 모델, 헬퍼를 제공한다. 사용자 계정이 없는 내부 또는 제3자 백엔드와 서비스(즉, 기기)를 인증하는 데 사용할 수 있다. API 키는 Django의 비밀번호 해싱 인프라를 사용하여 안전하게 저장되며, 언제든지 Django 관리자에서 보고, 편집하고, 취소할 수 있다.

         - Django REST Framework API Key : https://florimondmanca.github.io/djangorestframework-api-key/

 

      - 예시

         - 모델명: Book
         - 필드: title (문자열), author (문자열), publication_date (날짜)
         - 롤: 슈퍼유저(Superuser), 일반 사용자(User)
         - 권한 설정: 슈퍼유저는 모든 권한을 가지고, 일반 사용자는 책을 조회할 수 있고 새로운 책을 생성할 수 있으며, 책을 수정하거나 삭제할 수는 없도록 권한 설정

 

      1. 패키지 설치

pip install django-rest-framework-roles

 

      2. 롤(Role) 정의

from roles.roles import AbstractUserRole

class Superuser(AbstractUserRole):
    available_permissions = {
        'create_book': True,
        'view_book': True,
        'change_book': True,
        'delete_book': True,
    }

class User(AbstractUserRole):
    available_permissions = {
        'create_book': True,
        'view_book': True,
        'change_book': False,
        'delete_book': False,
    }

 

      3. 권한 클래스 생성

from rest_framework.permissions import BasePermission
from roles.roles import get_user_roles, has_permission

class HasRole(BasePermission):
    def has_permission(self, request, view):
        roles = get_user_roles(request.user)
        return has_permission(view.action, roles)

class BookPermissions(HasRole):
    required_roles = {
        'create': [Superuser, User],
        'list': [Superuser, User],
        'retrieve': [Superuser, User],
        'update': [Superuser],
        'partial_update': [Superuser],
        'destroy': [Superuser],
    }

 

       4. 시리얼라이저 생성 :

from rest_framework import serializers
from .models import Book

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'

 

        5. 뷰 생성 :

from rest_framework import generics
from .models import Book
from .serializers import BookSerializer
from .permissions import BookPermissions

class BookList(generics.ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = [BookPermissions]

class BookDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = [BookPermissions]

 

   8) Django Rest Framework Role Filters

      - Django Rest Framework Role Filters 패키지는 여러 유형의 역할에 대해 간단한 필터링을 제공한다.

         - Django Rest Framework Role Filters : https://github.com/allisson/django-rest-framework-role-filters

 

      - 예시

         - 모델명: Book
         - 필드: title (문자열), author (문자열), publication_date (날짜)
         - 롤: 슈퍼유저(Superuser), 일반 사용자(User)
         - 권한 설정: 슈퍼유저는 모든 권한을 가지고, 일반 사용자는 책을 조회할 수 있고 새로운 책을 생성할 수 있으며, 책을 수정하거나 삭제할 수는 없도록 권한 설정

 

      1. 패키지 설치

pip install django-rest-framework-role-filters

 

      2. 롤(Role) 정의 : 슈퍼유저와 일반 사용자를 정의하는 코드이다.

from role_filters.roles import AbstractUserRole

class Superuser(AbstractUserRole):
    available_filters = {
        'book__title': True,
        'book__author': True,
        'book__publication_date': True,
    }

class User(AbstractUserRole):
    available_filters = {
        'book__title': True,
        'book__author': True,
        'book__publication_date': False,
    }

 

      3. 필터 클래스 생성 :

from role_filters.filters import RoleFilterBackend
from rest_framework.filters import OrderingFilter, SearchFilter
from .models import Book
from .serializers import BookSerializer
from .permissions import BookPermissions

class BookFilterBackend(RoleFilterBackend):
    role_filters = {
        Superuser: {},
        User: {'book__publication_date': ['exact']},
    }

class BookList(generics.ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = [BookPermissions]
    filter_backends = [BookFilterBackend, OrderingFilter, SearchFilter]
    ordering_fields = ['book__publication_date']
    search_fields = ['book__title', 'book__author']

 

 

       4. 시리얼라이저 생성 :

from rest_framework import serializers
from .models import Book

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'

 

      5. 뷰 생성 :

from rest_framework import generics
from .models import Book
from .serializers import BookSerializer
from .permissions import BookPermissions
from .filters import BookFilterBackend

class BookList(generics.ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = [BookPermissions]
    filter_backends = [BookFilterBackend]

 

      6. URL 매핑 :

from django.urls import path
from .views import BookList

urlpatterns = [
    path('books/', BookList.as_view(), name='book-list'),
]

 

   9) Django Rest Framework PSQ

      - Django Rest Framework PSQ 패키지는 권한 기반 규칙에 따라 action-based permission_classes, serializer_class, 그리고 queryset에 대한 지원을 제공하는 확장이다.

         - Django Rest Framework PSQ : https://github.com/drf-psq/drf-psq

 

      - 예시

         - 모델명: Order
         - 필드: order_number (문자열), status (문자열), created_at (날짜)
         - 주문(Order)을 생성하면, 백그라운드에서 주문 상태를 업데이트하는 작업을 비동기로 처리하고 싶다.

 

      1. 패키지 설치 :

pip install drf-psq

 

      2. 비동기 작업 정의 :

from django.db import transaction
from psq import Job

@transaction.atomic()
def update_order_status(order_id):
    # 주문 상태 업데이트 작업을 수행하는 코드 작성
    order = Order.objects.get(id=order_id)
    order.status = '배송 중'
    order.save()

def update_order_status_async(order_id):
    job = Job(update_order_status, order_id)
    job.save()

 

      3. 시리얼라이저 생성 :

from rest_framework import serializers
from .models import Order

class OrderSerializer(serializers.ModelSerializer):
    class Meta:
        model = Order
        fields = '__all__'

 

      4. 뷰 생성 :

from rest_framework import generics
from .models import Order
from .serializers import OrderSerializer

class OrderListCreate(generics.ListCreateAPIView):
    queryset = Order.objects.all()
    serializer_class = OrderSerializer

    def perform_create(self, serializer):
        instance = serializer.save()
        update_order_status_async(instance.id)

 

      5. URL 매핑 :

from django.urls import path
from .views import OrderListCreate

urlpatterns = [
    path('orders/', OrderListCreate.as_view(), name='order-list-create'),
]

 

 

 - 공식 사이트 문서 : https://www.django-rest-framework.org/api-guide/permissions/#overview-of-access-restriction-methods

 

Permissions - Django REST framework

 

www.django-rest-framework.org

 

 

댓글