djangoのpermissionについてまとめる

Permissionとは
-
`Permission`とはdjangoアプリケーションに実装されている概念であり、ユーザーの行動に制限をかけるために用いられる。

例えばdjangoアプリの管理者サイトへのログインはsuperuserでないとできないが、これはsuperuserに`is_staff`というpermissionが付与されていることによる（逆に言えばこの権限さえ与えてしまえばどのユーザーでも管理者サイトに入れるようになる）。

permissionは管理者サイトのような組み込みのシステムだけでなく、アプリケーション内に実装した機能の利用に制限をかける際にも使用可能。

アプリの独自機能に制限をかける場合にはMixinを用います。
そちらについては以下の記事をどうぞ。

https://qiita.com/keishi04hrikzira/items/50e7a8254cbbd6b5ed93

Permissionの付与の仕組み
-
Permissionの付与の仕方には、ユーザーに直接付与する方法とあるグループを作ってそこに付与し、そのグループにユーザーを追加するという方法の２種類がある。

また、Permissionの実態は`auth_permission`というテーブルのレコードであり、ユーザー及びグループにPermissionが付与されているのかどうかは、`アプリ名_customuser_user_permissions`テーブルと`auth_group_permissions`テーブルという二つの中間テーブルによって管理されている。

具体的なpermission操作のコードは以下の通り。

```python
# "Can Change Item"というpermissionの付与・剥奪を行う場合。
permission = Permission.objects.get(name="Can change Item")

# ユーザーにpermissionを付与
user.user_permissions.add(permission)

# 以下の書き方でも可
user.user_permissions.set([permission])

# permissionの剥奪
user.user_permissions.remove(permission)

# permissionの全権剥奪
user.user_permission.clear()

# groupの場合は以下のように対応(add以外についてもuserと同様）
group.permissions.add(permission)

# ユーザーとグループの紐付けも同じようにして行う
user1.groups.add(group1)

# 以下でも同じ内容
group1.users.add(user1)
```

auth_permissionテーブルについて
-

スキーマ及びレコードについては以下の通り（レコードは一部を抜粋しています）。

```shell
sqlite> .schema auth_permission
CREATE TABLE IF NOT EXISTS "auth_permission" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "content_type_id" integer NOT NULL REFERENCES "django_content_type" ("id") DEFERRABLE INITIALLY DEFERRED, "codename" varchar(100) NOT NULL, "name" varchar(255) NOT NULL);
CREATE UNIQUE INDEX "auth_permission_content_type_id_codename_01ab375a_uniq" ON "auth_permission" ("content_type_id", "codename");
CREATE INDEX "auth_permission_content_type_id_2f476e4b" ON "auth_permission" ("content_type_id");
sqlite> select * from auth_permission;
1|1|add_logentry|Can add log entry
2|1|change_logentry|Can change log entry
3|1|delete_logentry|Can delete log entry
4|1|view_logentry|Can view log entry
5|2|add_permission|Can add permission
6|2|change_permission|Can change permission
7|2|delete_permission|Can delete permission
8|2|view_permission|Can view permission
# ...中略...
29|8|add_emailaddress|Can add email address
30|8|change_emailaddress|Can change email address
31|8|delete_emailaddress|Can delete email address
32|8|view_emailaddress|Can view email address
# ...中略...
49|13|add_subject|Can add subject
50|13|change_subject|Can change subject
51|13|delete_subject|Can delete subject
52|13|view_subject|Can view subject
```

permissionレコードの`condename`(左から３番目のカラム）については`操作名(add, changeなど)`と`操作対象(emailやpermissionなど)`を組み合わせたものになっている。

ほとんどは初回のマイグレーション時に作成されるほか、新たなモデルを定義した場合にはそれに合わせたpermissionも追加される(上記例の末尾にある`xxx_subject`permissionはsubjectモデルの追加に伴って作成されたもの）。

なお、上記例の`add_subject`permissionを例にとると、このpermissionがなければsubjectモデルのインスタンスを追加できないという意味ではない。

permissionを用いて特定の操作に対して制限をかけたい場合には、`PermissionRequiredMixin`を用いて必要なpermissionを指定する必要がある。

あくまでその際にわかりやすいように、djangoの側で組み込みのpermissionが用意されているという理解で問題ない。

PermissionRequiredMixinにおけるpermissionの指定方法
-

>`PermissionRequiredMixin`において`permission_required`という属性値を用いて必要なpermissionを指定する場合には、`対象permissionのcontent_typeのapp_label`.`対象permissionのcodename`という形式を用いる。
例えば上記の`id=52`のpermissionを指定したい場合には、app_labelは`subject`, codenameは`view_subject`なので以下のようになる。

```python:Mixinを用いたpermissionの指定の例
class TestView(PermissionRequiredMixin, TemplateView):
  permission_required = ["subject.view_subject"]
```

というのが厳密な定義らしいですが、`content_typeのapp_label`は基本的にそのモデルを定義しているアプリケーション名という認識で大丈夫そうです。

不安であれば、`django_content_type`というテーブルから確認可能です。

```shell
sqlite> .schema django_content_type
CREATE TABLE IF NOT EXISTS "django_content_type" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "app_label" varchar(100) NOT NULL, "model" varchar(100) NOT NULL);
CREATE UNIQUE INDEX "django_content_type_app_label_model_76bd3d3b_uniq" ON "django_content_type" ("app_label", "model");
sqlite> select * from django_content_type
   ...> ;
1|admin|logentry
2|auth|permission
3|auth|group
4|contenttypes|contenttype
5|sessions|session
6|user|customuser
7|sites|site
8|account|emailaddress
9|account|emailconfirmation
10|socialaccount|socialaccount
11|socialaccount|socialapp
12|socialaccount|socialtoken
13|subject|subject
```
例えば`codename=view_subject`のpermissionは`content_type_id(左から２番目のカラム）`の値が13なので`django_content_type`テーブルの`id=13`のレコードを確認すると、`app_label（左から２番目のカラム）`が`subject`となっています。

終わりに
-
Permissionについてはとりあえずはここまでにします。

必要があれば随時追加していきます。