django permissonでリソースを制御する
webアプリの場合、認証する->認可するの流れをたどる。 この認可されたパーミッションにしたがってリソースのアクセス制御を実施したい。
djangoの場合どうすれば実装できるか?
下準備
結果から言うと1のUserオブジェクトもアクセスでき、3のUserオブジェクトもアクセスできる。そしてパーミッションを全く付加していないsuperuserもリソースにアクセスできた。当然、パーミッションを追加していない普通のUserオブジェクトはリソースにアクセスできないのは言うまでもない。
下準備に関してはdjangoのあどみんからパーミッションを付与するか、コードベースで付与するか考えられる。コードベースでやる場合は以下を参考にすると良い.
Djangoのpermissionを付与するサンプルコード - diadia
実装する
# models.py リソース制御対象のリソースモデル from django.db import models from django.contrib.auth.models import User # Create your models here. class Articulo(models.Model): title = models.CharField(max_length=255) content = models.TextField() created_by = models.ForeignKey(User, on_delete=models.PROTECT) created_at = models.DateTimeField() def __str__(self): return self.title
# views.py from django.shortcuts import render from django.contrib.auth.mixins import PermissionRequiredMixin from django.views.generic import View from .models import Articulo class ArticulosListView(PermissionRequiredMixin, View): permission_required = ('articulos.view_articulo') def get(self, request): articulos_objects = Articulo.objects.all() context = {"articulos_objects": articulos_objects} return render(request, 'articulos/list.html', context)
これでリソース制御できる。 permission_requiredはPermissionRequiredMixinのプロパティである。値はstr型でも何らかのシーケンスで定める。
値はapp_label.view_小文字のモデルで定める。これがいわゆるリソースに対するパーミッションである。
https://docs.djangoproject.com/ja/3.0/topics/auth/default/#default-permissions
リソース操作 | 例 app_labelがhogesでHogeモデルの場合 |
---|---|
読み取り | hoges.view_hoge |
作成 | hogs.add_hoge |
変更編集 | hoges.change_hoge |
削除 | hoges.delete_hoge |
djangoのコードがどうなっているか
https://github.com/django/django/blob/master/django/contrib/auth/mixins.py#L74
class PermissionRequiredMixin(AccessMixin): """Verify that the current user has all specified permissions.""" permission_required = None def get_permission_required(self): """ Override this method to override the permission_required attribute. Must return an iterable. """ if self.permission_required is None: raise ImproperlyConfigured( '{0} is missing the permission_required attribute. Define {0}.permission_required, or override ' '{0}.get_permission_required().'.format(self.__class__.__name__) ) if isinstance(self.permission_required, str): perms = (self.permission_required,) else: perms = self.permission_required return perms def has_permission(self): """ Override this method to customize the way permissions are checked. """ perms = self.get_permission_required() return self.request.user.has_perms(perms) def dispatch(self, request, *args, **kwargs): if not self.has_permission(): return self.handle_no_permission() return super().dispatch(request, *args, **kwargs)
classmethodのas_viewがdispatchを呼び出す事になっているが、このdispatchを呼び出す前にプロパティのパーミッションを取得(get_permission_required)して、 Userオブジェクトのメソッドであるhas_permでパーミッションがあるかどうかをチェックする(has_permission)。このフラグをmixinのdispatch内で判定し、Trueであれば、各Viewのget, postメソッドにつないでいく仕組みになる。
まだ、どうやってas_viewとdispatchの間にこのロジックを差し込む原因があるのか調べきれていない。おそらくas_view()になんか書いてあるのか???