카테고리 없음

DRF tutorial - 4 Authentication & Permissions

두잇 두두 2024. 1. 5. 22:44
728x90

현재 우리는 API에 데이터를 편집하거나 삭제할 수 있는 사람에 대한 제한이 없습니다. 그래서 아래의 기능을 추가해서 보안을 강화하려고 합니다

  • 코드는 항상 작성자와 연결됩니다
  • 인증된 사용자만 스니펫을 생성할 수 있습니다
  • 데이터 작성자만 데이터를 업데이트 하거나 삭제할 수 있습니다
  • 인증되지 않은 요청에는 전체 읽기 전용 엑세스 권한이 있어야 합니다.

모델에 정보 추가

모델 클래스에 몇 가지 변경 사항을 적용하겠습니다.

Snippet 모델에 두 필드를 추가합니다 snippets/models.py

owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
highlighted = models.TextField()

 

또한 모델이 저장될 대 코드 강조 라이브러리를 사용하여 강조 표시된 필드를 채우는지 확인하기 위해 pygments를 사용해서 highlight library를 사용합니다

 

그리고 .save() method를 우리 모델 클래스에 추가합니다.

class Snippet(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=100, blank=True, default='')
    code = models.TextField()
    linenos = models.BooleanField(default=False)
    language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
    style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)
    owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
    highlist = models.TextField()

    class Meta:
        ordering = ['created']
        
    def save(self, *args, **kwargs):
        """
        Use the `pygments` library to create a highlighted HTML
        representation of the code snippet.
        """
        lexer = get_lexer_by_name(self.language)
        linenos = 'table' if self.linenos else False
        options = {'title': self.title } if self.title else {}
        formatter = HtmlFormatter(style=self.style, linenos=linenos,
                                  full=True, **options)
        self.highlighted = highlight(self.code, lexer, formatter)
        super().save(*args, **kwargs)

이렇게 작성 후 db 테이블을 업데이트해야 합니다. 보통 이를 수행하기 위해 데이터베이스 마이그레이션을 생성하지만 튜토리얼의 목적에 따라 db를 삭제하고 다시 시작하겠습니다

 

rm -f db.sqlite3
#window del /f db.sqlite3
rm -r snippets/migrations
#window del /f snippets.migrations
python manage.py makemigrations snippets
python manage.py migrate
python manage.py createsuperuser

Adding endpoints for our User models

새로운 사용자가 생겼으므로 serializers.py에 추가해줍니다

from django.contrib.auth.models import User

class UserSerializer(serializers.ModelSerializer):
    snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())

    class Meta:
        model = User
        fields = ['id', 'username', 'snippets']

'snippets'는 User 모델의 역관계이므로 클래스를 사용할 때 기본적으로 포함되지 않는다. 그래서 ModelSerializer 에 대해 명시적으로 필드를 추가해야 합니다.

또한 view.py에 사용자 표현을 읽기 전용 뷰만 사용하기 위해 일반 class view인 ListAPIView와 RetrieveAPIView를 써보겠습니다.

from snippets.serializers import UserSerializer
from django.contrib.auth.models import User

class UserList(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer


class UserDetail(generics.RetrieveAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

그리고 urls.py도 다음을 추가합니다

path('users/', views.UserList.as_view()),
path('users/<int:pk>/', views.UserDetail.as_view()),

 

보안

Snippet의 경우 list는 모두에게 볼 수 있기를 원하지만 Snippet을 만든 사람만 업데이트 하고 삭제 할 수 있게 만들고 싶습니다.

그럴 경우 사용자 정의 권한을 생성해야 합니다.

snippets/permissions.py 를 추가해줍니다

from rest_framework import permissions


class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    Custom permission to only allow owners of an object to edit it.
    """

    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
            # save method 즉 get의 경우 true

        # Write permissions are only allowed to the owner of the snippet.
        return obj.owner == request.user
        # 다른 post나 put, delete는 owner와 request.user(로그인 된 유저)가 같은 경우만 true

 

그리고 snippets/view.py 내에 permission_classes속성을 편집합니다

from snippets.permissions import IsOwnerOrReadOnly

permission_classes = [permissions.IsAuthenticatedOrReadOnly,
                      IsOwnerOrReadOnly]

이렇게 작업 시 snippet을 생성한 사용자와 동일한 사용자로 로그인한 경우만 DELETE및 PUT이 표시됩니다.