web-dev-qa-db-ja.com

Django RESTフレームワークオブジェクトレベルの権限

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
25
user1830568

私は過去にカスタム権限を使用してこれを行い、オーバーライドしました 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, )
    ...
20
will-hart

同様のニーズがあります。私のアプリを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経由まだ。

13
Tim Ruddick

つまずきについては、オブジェクトレベルのアクセス許可の制限の下で 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 を実行する必要があります。

9
keni

@ 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
7