현재 우리는 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이 표시됩니다.