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

#なぜ書こうと思ったか

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](https://developer.apple.com/library/mac/#documentation/security/Conceptual/AuthenticationAndAuthorizationGuide/Authorization/Authorization.html)

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 `を使って設定します。

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

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

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


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

```Python: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を書いている場合はこうなります。

```Python: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_staff`が`True`となっている場合以外の全てのユーザーを拒否します。

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

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

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

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

- `PUT`と`PATCH`リクエストには`change`モデルに対する権限が必要

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

となっています。

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

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

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

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

##DjangoObjectPermissions

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

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

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

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


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

- `.has_permission(self,request,view)`
- `.has_object_permission(self,request,view,obj)`

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

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

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


#リファレンス
[Django REST Framework](http://www.tomchristie.com/rest-framework-2-docs/api-guide/permissions)