permissions.py

パーミッション

認証や識別だけでは、通常、情報やコードへのアクセス権を得るには不十分です。そのため、アクセスを要求するエンティティは承認権限を持つ必要があります。

Apple開発者向けドキュメント

認証スロットリングと合わせて、パーミッションはリクエストへのアクセスを許可するか拒否するかを決定します。

パーミッションチェックは、他のコードが実行される前に、ビューの開始時に常に実行されます。パーミッションチェックは通常、受信リクエストを許可するかどうかを判断するために、request.userrequest.authプロパティの認証情報を使用します。

パーミッションは、異なるクラスのユーザーにAPIの異なる部分へのアクセスを許可または拒否するために使用されます。

最も単純なパーミッションスタイルは、認証済みユーザーへのアクセスを許可し、未認証ユーザーへのアクセスを拒否することです。これは、REST frameworkのIsAuthenticatedクラスに対応します。

少し緩いパーミッションスタイルは、認証済みユーザーへのフルアクセスを許可し、未認証ユーザーへの読み取り専用アクセスを許可することです。これは、REST frameworkのIsAuthenticatedOrReadOnlyクラスに対応します。

パーミッションの決定方法

REST frameworkのパーミッションは、常にパーミッションクラスのリストとして定義されます。

ビューの本体を実行する前に、リスト内の各パーミッションがチェックされます。パーミッションチェックが失敗すると、exceptions.PermissionDeniedまたはexceptions.NotAuthenticated例外が発生し、ビューの本体は実行されません。

パーミッションチェックが失敗すると、次のルールに従って、「403 Forbidden」または「401 Unauthorized」レスポンスが返されます。

  • リクエストは正常に認証されましたが、パーミッションが拒否されました。 — HTTP 403 Forbiddenレスポンスが返されます。
  • リクエストは正常に認証されませんでしたが、優先順位最高の認証クラスがWWW-Authenticateヘッダーを使用していません。— HTTP 403 Forbiddenレスポンスが返されます。
  • リクエストは正常に認証されませんでしたが、優先順位最高の認証クラスがWWW-Authenticateヘッダーを使用しています。— 適切なWWW-Authenticateヘッダーを含むHTTP 401 Unauthorizedレスポンスが返されます。

オブジェクトレベルのパーミッション

REST frameworkのパーミッションは、オブジェクトレベルのパーミッションもサポートしています。オブジェクトレベルのパーミッションは、ユーザーが特定のオブジェクト(通常はモデルインスタンス)に対して操作を行うことを許可するかどうかを判断するために使用されます。

オブジェクトレベルのパーミッションは、.get_object()が呼び出されたときに、REST frameworkのジェネリックビューによって実行されます。ビューレベルのパーミッションと同様に、ユーザーが指定されたオブジェクトに対して操作を行うことが許可されていない場合、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()メソッドを実装する必要があります


オブジェクトレベルのパーミッションの制限

パフォーマンス上の理由から、ジェネリックビューは、オブジェクトのリストを返す際に、クエリセット内の各インスタンスにオブジェクトレベルのパーミッションを自動的に適用しません。

オブジェクトレベルのパーミッションを使用する場合は、ユーザーが閲覧を許可されているインスタンスのみが表示されるように、適切にクエリセットをフィルタリングすることもよくあります。

get_object()メソッドは呼び出されないため、has_object_permission()メソッドからのオブジェクトレベルのパーミッションは、オブジェクトの作成時には適用されません。オブジェクトの作成を制限するには、シリアライザークラスでパーミッションチェックを実装するか、ViewSetクラスのperform_create()メソッドをオーバーライドする必要があります。

パーミッションポリシーの設定

デフォルトのパーミッションポリシーは、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)をサポートしています。


APIリファレンス

AllowAny

AllowAnyパーミッションクラスは、リクエストが認証済みか未認証かに関係なく、無制限のアクセスを許可します。

このパーミッションは厳密には必須ではありません。パーミッション設定に空のリストまたはタプルを使用することで同じ結果を得ることができるためですが、このクラスを指定すると意図が明確になるため、有用な場合があります。

IsAuthenticated

IsAuthenticatedパーミッションクラスは、未認証ユーザーへのパーミッションを拒否し、それ以外の場合はパーミッションを許可します。

このパーミッションは、APIを登録済みユーザーのみにアクセス可能にしたい場合に適しています。

IsAdminUser

IsAdminUserパーミッションクラスは、user.is_staffTrueでない限り、すべてのユーザーのパーミッションを拒否します。その場合は、パーミッションが許可されます。

このパーミッションは、APIを信頼できる管理者のサブセットのみにアクセス可能にしたい場合に適しています。

IsAuthenticatedOrReadOnly

IsAuthenticatedOrReadOnlyは、認証済みユーザーが任意のリクエストを実行できるようにします。未認証ユーザーのリクエストは、リクエストメソッドが「セーフ」メソッド(GETHEADOPTIONS)のいずれかの場合にのみ許可されます。

このパーミッションは、APIで匿名ユーザーに読み取り権限を許可し、認証済みユーザーのみに書き込み権限を許可したい場合に適しています。

DjangoModelPermissions

このパーミッションクラスは、Djangoの標準的なdjango.contrib.auth モデルパーミッションに関連付けられています。このパーミッションは、.querysetプロパティまたはget_queryset()メソッドを持つビューにのみ適用する必要があります。承認は、ユーザーが認証済みであり、関連するモデルパーミッションが割り当てられている場合にのみ付与されます。適切なモデルは、get_queryset().modelまたはqueryset.modelをチェックすることで決定されます。

  • POSTリクエストには、ユーザーがモデルに対するaddパーミッションを持つ必要があります。
  • PUTPATCHリクエストには、ユーザーがモデルに対するchangeパーミッションを持つ必要があります。
  • DELETEリクエストには、ユーザーがモデルに対するdeleteパーミッションを持つ必要があります。

カスタムモデルパーミッションをサポートするようにデフォルトの動作をオーバーライドすることもできます。たとえば、GETリクエストに対してviewモデルパーミッションを含めたい場合があります。

カスタムモデルパーミッションを使用するには、DjangoModelPermissionsをオーバーライドし、.perms_mapプロパティを設定します。詳細はソースコードを参照してください。

DjangoModelPermissionsOrAnonReadOnly

DjangoModelPermissionsに似ていますが、未認証ユーザーがAPIへの読み取り専用アクセスを許可することもできます。

DjangoObjectPermissions

このパーミッションクラスは、モデル上のオブジェクトごとのパーミッションを許可するDjangoの標準的なオブジェクトパーミッションフレームワークに関連付けられています。このパーミッションクラスを使用するには、django-guardianなどのオブジェクトレベルのパーミッションをサポートするパーミッションバックエンドを追加する必要もあります。

DjangoModelPermissionsと同様に、このパーミッションは、.querysetプロパティまたは.get_queryset()メソッドを持つビューにのみ適用する必要があります。承認は、ユーザーが認証済みであり、関連するオブジェクトごとのパーミッション関連するモデルパーミッションが割り当てられている場合にのみ付与されます。

  • POSTリクエストには、ユーザーがモデルインスタンスに対するaddパーミッションを持つ必要があります。
  • PUTPATCHリクエストには、ユーザーがモデルインスタンスに対するchangeパーミッションを持つ必要があります。
  • DELETEリクエストには、ユーザーがモデルインスタンスに対するdeleteパーミッションを持つ必要があります。

DjangoObjectPermissionsdjango-guardianパッケージを必要とせず、他のオブジェクトレベルのバックエンドも同様にサポートすることに注意してください。

DjangoModelPermissionsと同様に、DjangoObjectPermissionsをオーバーライドし、.perms_mapプロパティを設定することで、カスタムモデルパーミッションを使用できます。詳細はソースコードを参照してください。


注記: オブジェクトレベルのview権限をGETHEADOPTIONSリクエストに必要とし、オブジェクトレベルの権限バックエンドにdjango-guardianを使用している場合、djangorestframework-guardian2パッケージが提供するDjangoObjectPermissionsFilterクラスの使用を検討してください。これにより、リストエンドポイントは、ユーザーが適切な表示権限を持つオブジェクトのみを含む結果を返すようになります。


カスタムパーミッション

カスタム権限を実装するには、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例外を発生させます。例外に関連付けられたエラーメッセージを変更するには、カスタム権限に直接message属性を実装します。それ以外の場合は、PermissionDenieddefault_detail属性が使用されます。同様に、例外に関連付けられたコード識別子を変更するには、カスタム権限に直接code属性を実装します。それ以外の場合は、PermissionDenieddefault_code属性が使用されます。

from rest_framework import permissions

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

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

以下は、受信リクエストの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

すべての受信リクエストに対して実行されるグローバル権限に加えて、特定のオブジェクトインスタンスに影響を与える操作に対してのみ実行されるオブジェクトレベルの権限を作成することもできます。例:

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を発生させ、それ以外の場合は単に返ります。

また、ジェネリックビューは、単一のモデルインスタンスを取得するビューに対してのみオブジェクトレベルの権限をチェックすることに注意してください。リストビューのオブジェクトレベルのフィルタリングが必要な場合は、クエリセットを別途フィルタリングする必要があります。詳細はフィルタリングに関するドキュメントを参照してください。

アクセス制限方法の概要

REST frameworkは、ケースバイケースでアクセス制限をカスタマイズするための3つの異なる方法を提供します。これらは異なるシナリオで適用され、異なる効果と制限があります。

  • queryset/get_queryset(): データベースから既存オブジェクトの一般的な可視性を制限します。クエリセットは、リストされるオブジェクトと、変更または削除できるオブジェクトを制限します。get_queryset()メソッドは、現在の操作に基づいて異なるクエリセットを適用できます。
  • permission_classes/get_permissions(): 現在の操作、リクエスト、対象オブジェクトに基づいた一般的な権限チェック。オブジェクトレベルの権限は、取得、変更、削除操作にのみ適用できます。リストと作成の権限チェックは、オブジェクトタイプ全体に適用されます。(リストの場合:クエリセットの制限に従います。)
  • serializer_class/get_serializer(): 入力と出力のすべてのオブジェクトに適用されるインスタンスレベルの制限。シリアライザはリクエストコンテキストにアクセスできます。get_serializer()メソッドは、現在の操作に基づいて異なるシリアライザを適用できます。

次の表は、アクセス制限方法と、それらがどの操作に対して提供する制御レベルを示しています。

queryset permission_classes serializer_class
操作:リスト グローバル グローバル オブジェクトレベル*
操作:作成 なし グローバル オブジェクトレベル
操作:取得 グローバル オブジェクトレベル オブジェクトレベル
操作:更新 グローバル オブジェクトレベル オブジェクトレベル
操作:部分更新 グローバル オブジェクトレベル オブジェクトレベル
操作:削除 グローバル オブジェクトレベル なし
決定で操作を参照できます なし** はい なし**
決定でリクエストを参照できます なし** はい はい

* シリアライザクラスは、リスト操作でPermissionDeniedを発生させるべきではありません。そうしないと、リスト全体が返されなくなります。
** get_*()メソッドは現在のビューにアクセスでき、リクエストまたは操作に基づいて異なるシリアライザまたはクエリセットインスタンスを返すことができます。


サードパーティパッケージ

以下のサードパーティパッケージも利用可能です。

DRF - アクセスポリシー

Django REST - Access Policyパッケージは、ビューセットまたは関数ベースのビューにアタッチされる宣言的なポリシークラスで複雑なアクセスルールを定義する方法を提供します。ポリシーは、AWSのIdentity & Access Managementポリシーと同様の形式でJSONで定義されます。

複合パーミッション

Composed Permissionsパッケージは、小さく再利用可能なコンポーネントを使用して、複雑で多層(論理演算子付き)の権限オブジェクトを簡単に定義する方法を提供します。

REST Condition

REST Conditionパッケージは、シンプルで便利な方法で複雑な権限を構築するための別の拡張機能です。この拡張機能により、論理演算子を使用して権限を組み合わせることができます。

DRY Rest Permissions

DRY Rest Permissionsパッケージは、個々のデフォルトアクションとカスタムアクションに異なる権限を定義する機能を提供します。このパッケージは、アプリのデータモデルで定義されたリレーションシップから派生した権限を持つアプリ向けに作られています。また、APIのシリアライザを通じてクライアントアプリに権限チェックを返すこともサポートしています。さらに、デフォルトとカスタムのリストアクションに権限を追加して、ユーザーごとに取得するデータを制限することもサポートしています。

Django Rest Framework Roles

Django Rest Framework Rolesパッケージは、複数のタイプのユーザーに対してAPIをパラメータ化することを容易にします。

Rest Framework Roles

Rest Framework Rolesは、ロールに基づいてビューを保護することを非常に容易にします。最も重要なのは、アクセス可能性ロジックをモデルとビューからクリーンで人間が読める方法で分離できることです。

Django REST Framework API Key

Django REST Framework API Keyパッケージは、APIにAPIキー認証を追加するための権限クラス、モデル、ヘルパーを提供します。これは、ユーザーアカウントを持たない内部またはサードパーティのバックエンドおよびサービス(つまり、マシン)を認証するために使用できます。APIキーはDjangoのパスワードハッシングインフラストラクチャを使用して安全に保存され、いつでもDjango管理画面で表示、編集、取り消すことができます。

Django Rest Framework Role Filters

Django Rest Framework Role Filtersパッケージは、複数のタイプのロールに対する簡単なフィルタリングを提供します。

Django Rest Framework PSQ

Django Rest Framework PSQパッケージは、権限ベースのルールに応じて、アクションベースのpermission_classesserializer_classquerysetをサポートする拡張機能です。