Django RESTリソース「ユーザー」にアクセスするためのフレームワークを使用しています。
ユーザー情報は個人情報なので、管理者である場合を除き、GETリクエストでシステム上のすべてのユーザーをリストする必要はありません。
ユーザーが自分のIDを指定し、ログインしている場合は、詳細を表示し、必要に応じて修正(PUT POST DELETE))できるようにしたいと思います。
したがって、要約すると、管理者以外のユーザーにはGETメソッドを許可せず、情報を表示するときに、ログインしているユーザーにGET POST DELETE PUTを許可します。
だから私はカスタム許可クラスを作成しました:
class UserPermissions(permissions.BasePermission):
"""
Owners of the object or admins can do anything.
Everyone else can do nothing.
"""
def has_permission(self, request, view):
# if admin: True otherwise False
def has_object_permission(self, request, view, obj):
# if request.user is the same user that is contained within the obj then allow
これはうまくいきませんでした。いくつかのデバッグの後、has_permissionを最初にチェックすることがわかったので、THENはhas_object_permissionをチェックします。したがって、最初のハードルGET/user /を乗り越えなければ、次のGET/user/idも考慮されません。
要約すると、誰かがこれを機能させる方法を知っていますか?
ありがとう:)
編集:
私が説明したように、この問題があるModelViewSetsを使用していました。
しかし、リスト機能を詳細で分割する場合、それらに別々の許可クラスを与えることができます:
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes=(UserPermissionsAll,)
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes=(UserPermissionsObj,)
class UserPermissionsAll(permissions.BasePermission):
"""
Owners of the object or admins can do anything.
Everyone else can do nothing.
"""
def has_permission(self, request, view):
if request.user.is_staff:
return True
else:
return False
class UserPermissionsObj(permissions.BasePermission):
"""
Owners of the object or admins can do anything.
Everyone else can do nothing.
"""
def has_object_permission(self, request, view, obj):
if request.user.is_staff:
return True
return obj == request.user
私は過去にカスタム権限を使用してこれを行い、オーバーライドしました has_object_permission
次のように:
from rest_framework import permissions
class MyUserPermissions(permissions.BasePermission):
"""
Handles permissions for users. The basic rules are
- owner may GET, PUT, POST, DELETE
- nobody else can access
"""
def has_object_permission(self, request, view, obj):
# check if user is owner
return request.user == obj
特定のリクエストタイプを拒否するなど、より詳細なことができます(たとえば、すべてのユーザーにGETリクエストを許可するため)。
class MyUserPermissions(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
# Allow get requests for all
if request.method == 'GET':
return True
return request.user == obj
次に、ビューでアクセス許可クラスを使用するように指示します。
from my_custom_permissions import MyUserPermissions
class UserView(generics.ListCreateAPIView):
...
permission_classes = (MyUserPermissions, )
...
同様のニーズがあります。私のアプリをx
と呼びましょう。これが私が思いついたものです。
まず、これをx/viewsets.py
:
# viewsets.py
from rest_framework import mixins, viewsets
class DetailViewSet(
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet):
pass
class ReadOnlyDetailViewSet(
mixins.RetrieveModelMixin,
viewsets.GenericViewSet):
pass
class ListViewSet(
mixins.ListModelMixin,
viewsets.GenericViewSet):
pass
次にx/permissions.py
:
# permissions.py
from rest_framework import permissions
class UserIsOwnerOrAdmin(permissions.BasePermission):
def has_permission(self, request, view):
return request.user and request.user.is_authenticated()
def check_object_permission(self, user, obj):
return (user and user.is_authenticated() and
(user.is_staff or obj == user))
def has_object_permission(self, request, view, obj):
return self.check_object_permission(request.user, obj)
次にx/views.py
:
# views.py
from x.viewsets import DetailViewSet, ListViewSet
from rest_framework import permissions
class UserDetailViewSet(DetailViewSet):
queryset = User.objects.all()
serializer_class = UserDetailSerializer
permission_classes = (UserIsOwnerOrAdmin,)
class UserViewSet(ListViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes (permissions.IsAdminUser,)
ちなみに、これら2つのビューセットにはdifferent serializerを使用できることに注意してください。つまり、 list
ビューよりもretrieve
ビュー!例えば:
# serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('username', 'url',)
class UserDetailSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'groups', 'profile', 'password',)
write_only_fields = ('password',)
次にx/urls.py
:
# urls.py
from x import views
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'users', views.UserDetailViewSet)
...
router
が同じパターンを2回受け入れたことに少し驚いていましたが、機能しているようです。
注意事項:私はこれがすべてAPIブラウザー経由で機能することを確認しましたが、私は試していませんupdating API経由まだ。
つまずきについては、オブジェクトレベルのアクセス許可の制限の下で documentation は次のように述べています。
For performance reasons the generic views will not automatically apply object level permissions to each instance in a queryset when returning a list of objects.
したがって、詳細ビューは機能しますが、リストの場合、現在のユーザーに対して filter を実行する必要があります。
@ will-hartの答えにもう1つだけ。
DRF3のドキュメントでは、
注:インスタンスレベルのhas_object_permissionメソッドは、ビューレベルのhas_permissionチェックがすでに合格している場合にのみ呼び出されます
したがって、_has_permission
を指定する必要がありますhas_object_permission
を使用します。
from rest_framework import permissions
class MyUserPermissions(permissions.BasePermission):
def has_permission(self, request, view):
return True
def has_object_permission(self, request, view, obj):
return request.user == obj
ただし、上記のコードは、ユーザーがユーザーのリストを取得しようとしたときに、誰にでも許可を与えます。この場合、HTTP method
ではなくaction
に従って許可を与えるほうがよいでしょう。
from rest_framework import permissions
def has_permission(self, request, view):
if request.user.is_superuser:
return True
Elif view.action == 'retrieve':
return True
else:
return False
def has_object_permission(self, request, view, obj):
if request.user.is_superuser:
return True
Elif view.action == 'retrieve':
return obj == request.user or request.user.is_staff