パーミッション
認証や識別だけでは、通常、情報やコードへのアクセス権を得るには不十分です。そのため、アクセスを要求するエンティティは承認権限を持つ必要があります。
認証とスロットリングと合わせて、パーミッションはリクエストへのアクセスを許可するか拒否するかを決定します。
パーミッションチェックは、他のコードが実行される前に、ビューの開始時に常に実行されます。パーミッションチェックは通常、受信リクエストを許可するかどうかを判断するために、request.user
とrequest.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_staff
がTrue
でない限り、すべてのユーザーのパーミッションを拒否します。その場合は、パーミッションが許可されます。
このパーミッションは、APIを信頼できる管理者のサブセットのみにアクセス可能にしたい場合に適しています。
IsAuthenticatedOrReadOnly
IsAuthenticatedOrReadOnly
は、認証済みユーザーが任意のリクエストを実行できるようにします。未認証ユーザーのリクエストは、リクエストメソッドが「セーフ」メソッド(GET
、HEAD
、OPTIONS
)のいずれかの場合にのみ許可されます。
このパーミッションは、APIで匿名ユーザーに読み取り権限を許可し、認証済みユーザーのみに書き込み権限を許可したい場合に適しています。
DjangoModelPermissions
このパーミッションクラスは、Djangoの標準的なdjango.contrib.auth
モデルパーミッションに関連付けられています。このパーミッションは、.queryset
プロパティまたはget_queryset()
メソッドを持つビューにのみ適用する必要があります。承認は、ユーザーが認証済みであり、関連するモデルパーミッションが割り当てられている場合にのみ付与されます。適切なモデルは、get_queryset().model
またはqueryset.model
をチェックすることで決定されます。
POST
リクエストには、ユーザーがモデルに対するadd
パーミッションを持つ必要があります。PUT
とPATCH
リクエストには、ユーザーがモデルに対するchange
パーミッションを持つ必要があります。DELETE
リクエストには、ユーザーがモデルに対するdelete
パーミッションを持つ必要があります。
カスタムモデルパーミッションをサポートするようにデフォルトの動作をオーバーライドすることもできます。たとえば、GET
リクエストに対してview
モデルパーミッションを含めたい場合があります。
カスタムモデルパーミッションを使用するには、DjangoModelPermissions
をオーバーライドし、.perms_map
プロパティを設定します。詳細はソースコードを参照してください。
DjangoModelPermissionsOrAnonReadOnly
DjangoModelPermissions
に似ていますが、未認証ユーザーがAPIへの読み取り専用アクセスを許可することもできます。
DjangoObjectPermissions
このパーミッションクラスは、モデル上のオブジェクトごとのパーミッションを許可するDjangoの標準的なオブジェクトパーミッションフレームワークに関連付けられています。このパーミッションクラスを使用するには、django-guardianなどのオブジェクトレベルのパーミッションをサポートするパーミッションバックエンドを追加する必要もあります。
DjangoModelPermissions
と同様に、このパーミッションは、.queryset
プロパティまたは.get_queryset()
メソッドを持つビューにのみ適用する必要があります。承認は、ユーザーが認証済みであり、関連するオブジェクトごとのパーミッションと関連するモデルパーミッションが割り当てられている場合にのみ付与されます。
POST
リクエストには、ユーザーがモデルインスタンスに対するadd
パーミッションを持つ必要があります。PUT
とPATCH
リクエストには、ユーザーがモデルインスタンスに対するchange
パーミッションを持つ必要があります。DELETE
リクエストには、ユーザーがモデルインスタンスに対するdelete
パーミッションを持つ必要があります。
DjangoObjectPermissions
はdjango-guardianパッケージを必要とせず、他のオブジェクトレベルのバックエンドも同様にサポートすることに注意してください。
DjangoModelPermissions
と同様に、DjangoObjectPermissions
をオーバーライドし、.perms_map
プロパティを設定することで、カスタムモデルパーミッションを使用できます。詳細はソースコードを参照してください。
注記: オブジェクトレベルのview
権限をGET
、HEAD
、OPTIONS
リクエストに必要とし、オブジェクトレベルの権限バックエンドに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
属性を実装します。それ以外の場合は、PermissionDenied
のdefault_detail
属性が使用されます。同様に、例外に関連付けられたコード識別子を変更するには、カスタム権限に直接code
属性を実装します。それ以外の場合は、PermissionDenied
のdefault_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_classes、serializer_class、querysetをサポートする拡張機能です。