LoginSignup
12
10

More than 3 years have passed since last update.

Django REST FrameworkのPermission周りの日本語解説がないので書いてみた

Last updated at Posted at 2020-04-15

なぜ書こうと思ったか

Djangoが好きでプライベートで色々と触っているのですが、QiitaにDjango周りの記事はそれなりにあってもDjango REST Framework(以下DRF)周りの記事が少ない・・・(この記事を書いている段階で159件)
とても強力なフレームワークであり、かつフロントエンドとサーバーサイドを分割して開発するのが一般的になってきている現状で、DRFはとても魅力的な選択肢の一つであると思います。
一方で、日本語の記事や技術書が少なく、英語のドキュメントを読むことが必須になると英語ができない方からするととっつきにくい印象を与えてしまうのではないかと思います。
とりわけ初心者からすると日本語ですら理解できないような内容を英語で書かれてもなぁ・・となりがちだと思うので書きました。

DRFのPermission

アクセス制御に関する領域です。

Authentication or identification by itself is not usually sufficient to gain access to information or code. For that, the entity requesting access must have authorization.
Apple Developer Documentation

Appleのデベロッパーのドキュメントにあるように、一般的には認証や識別のみでは情報やコードにアクセスすることに対して十分ではなく、アクセスを要求するエンティティには権限が必要という考えがあります。

このDRFのPermissionはviewの開始時に実行され、権限チェックでは通常、request.user、及び、request.authプロパティの認証情報を使ってリクエストを許可するかを決定します。

権限の決定方法

DRFの権限は常に権限クラスのリストとして定義されます。

viewの本体を実行する前にリスト内の各権限がチェックされ、それに失敗した場合はexceptions.PermissionDeniedの例外が発生し、本体の実行はなくなります。

DRFの権限はまた、オブジェクトレベルの権限もサポートし、この権限はユーザーが特定のオブジェクト(通常はmodel)に対しての操作が許可されるかどうかを決定するために使われます。

自身でviewを作成し、それにオブジェクトレベルの権限を適用する場合、もしくはget_objectジェネリックビューでメソッドをオーバーライドする場合には.check_object_permissions(request,obj)viewを取得した時点でviewのメソッドを呼び出す必要があります。

そうすると、viewが適切な権限を持つ場合はそのまま続行され、権限を所有していない場合にはPermissionDeniedもしくはNotAuthenticatedの例外が返されます。

設定方法

デフォルトの許可ポリシーの設定はDEFAULT_PERMISSION_CLASSESを使って設定します。

settings.py
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    )
}

仮に指定しない場合はAlloWAnyを使います。

settings.py
REST_FRAMEWORK = {
   'DEFAULT_PERMISSION_CLASSES': (
      'rest_framework.permissions.AllowAny',
   )
}

APIViewクラスベースのviewを使ってviewごと、viewsetごとに認証の範囲を設定することもできます。

views.py
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デコレーターを用いて関数ベースのviewを書いている場合はこうなります。

views.py
@api_view('GET')
@permission_classes((IsAuthenticated, ))
def example_view(request, format=None):
    content = {
        'status': 'request was permitted'
    }
    return Response(content)

APIリファレンス

AllowAny

AllowAnyクラスは要求が認証されていようがされていまいが無制限のアクセスを許可します。
アクセス許可の設定に空のリスト、もしくはタプルを使っても同じような結果になるため、厳密に言えば必要ありませんが、意図を明示的にするために使うと便利です。

IsAuthenticated

IsAuthenticatedクラスは任意の認証されていないユーザーを拒否し、それ以外の権限を許可します。
APIに登録済みのユーザーのみがアクセスできるようにする場合に使われます。

IsAdminUser

IsAdminUserクラスはuser.is_staffTrueとなっている場合以外の全てのユーザーを拒否します。

IsAuthenticatedOrReadOnly

IsAuthenticatedOrReadOnlyクラスは認証されたユーザーが任意の要求を実行できるようになります。
権限がないユーザーのリクエストはリクエストメソッドがGETHEADまたはOPTIONSなどの安全なメソッドである場合のみに限定されます。

APIで匿名ユーザーに読み取り権限を許可し、認証済みのユーザーに対して書き込み権限を有するという時に適しています。

DjangoModelPermissions

DjangoModelPermissionsではDjangoの標準モデルであるdjango.contrib.authのアクセス許可に紐づけられています。
.querysetプロパティが設定されているviewにのみ適用する必要があり、ユーザーが認証され、関連するモデル権限が割り当てられている場合に承認されます。
例として、

  • POSTリクエストにはユーザーがaddモデルに対する権限が必要

  • PUTPATCHリクエストにはchangeモデルに対する権限が必要

  • DELETEリクエストにはdeleteモデルに対する権限が必要

となっています。

また、デフォルトの動作を上書きしてカスタムモデルの権限をサポートすることもでき、例えばviewにGETリクエストの権限を含めることもできます。

オーバーライドされたget_query()メソッドを使用する場合、queryset属性を含まない可能性があります。この場合、viewを以下のように必須のquersetでマークすることが推奨されています。

queryset = User.objects.none() #DjangoModelPermissionsに必要

DjangoModelPermissionsOrAnonReadOnly

上のDjangoModelPermissionsに似ていますが、こちらでは認証されていないユーザーがAPIに対して読み取り専用のアクセスをすることができます。

DjangoObjectPermissions

モデルに対してオブジェクトごとのパーミッションを許可するDjangoの標準オブジェクトパーミッションクラスと連携しています。
もし使用する場合はdjango-guardianなどのオブジェクトレベルのパーミッションをサポートするパーミッションバックエンドを追加する必要があります。

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

DjangoModelPermissionsと同様に、この権限は.querysetを持つviewにのみ適用する必要があり、ユーザーが認証され、オブジェクトの権限と関連するモデルの権限を割り当てられている場合にのみ承認されます。
加えて、'DjangoModelPermissions'をオーバーライドどすることでカスタムモデルの権限を使用できます。

TokenHasReadWriteScope

OAuthAuthenticationおよび、OAuth2Authenticationクラスのいずれかで使用することを目的としています。
認証されたトークンが読み取り権限が割り当てられている場合にGETOPTIONSまたはHEADのような安全なメソッドに限って許可されます。

カスタム権限

カスタム権限を実装するBasePermissionには次のメソッドいずれか、または両方をオーバーライドして実装します。

  • .has_permission(self,request,view)
  • .has_object_permission(self,request,view,obj)

リクエストに対してアクセス権を割り当てる場合にはメソッドはTrueを返し、それ以外はFalseを返します。

また、リクエストが読み取り操作か書き込み操作であるかをテストする必要がある場合は、以下のようにSAFE_METHODSを含むタプルである定数に対して、GETOPTIONSHEADのようなリクエストメソッドをチェックする必要があります。

if request.method in permissions.SAFE_METHODS:
    # Check permissions for read-only request
else:
    # Check permissions for write request

リファレンス

Django REST Framework

12
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
10