前回、Djangoで django-rules
を使ってみました。
django-rulesを使って、オブジェクトレベルの認可判定をViewとテンプレートでそれぞれ実装してみた - メモ的な思考的な
READMEには、Django REST Framework(以降、DRF)でも、 django-rulesが使えるとの記述がありました。
Permissions in Django Rest Framework | dfunckt/django-rules: Awesome Django authorization, without the database
このコメントによると、2019年8月に機能追加されたようです。
DRFの場合は ModelViewSet
で使えるとあったため試してみました。
目次
環境
なお、検証用のアプリは前回のソースコードに追加していきます。
実装
今回、 ReadOnlyModelViewSet
を使ったViewSetに対し、django-rulesが使えるかを確認します。
Django REST Frameworkアプリの作成
api
というアプリを作成します。
$ pip install djangorestframework $ python manage.py startapp api
rules.pyの追加
前回同様、
- システム管理者は閲覧可能
- 管理職も閲覧可能
- 一般は、自分と同じ部署のみ閲覧可能
とします。
DRFで django-rules
を使う場合、
rules.add_perm('myapp.same_section', is_same_section | is_admin | is_manager)
とadd_permする必要はなく、認可するかどうかの関数のみを用意します。
そのため、上記と同じ意味を持つ関数 can_view()
を用意します。
@rules.predicate def can_view(user, obj): return is_same_section(user, obj) or is_admin(user) or is_manager(user)
モデルの追加
前回はViewにパーミッションを追加しました。
DRFで使う場合にはモデルにパーミッションの追加が必要です。
Meta
クラスの中に rules_permissions
を追加し、設定を行います。
Djangoのデフォルトでは、一覧(ListView)を除く、
- view
- add
- change
- delete
のパーミッションが用意されています。
デフォルトの権限 | Djangoの認証システムを使用する | Django ドキュメント | Django
そのため、今回は rules_permissions
のキーとして view
を、値には先ほど定義した rules.py にある can_view
を指定します。
また、モデルの継承元を RulesModel
にします。django-rulesのドキュメントには、状況に応じて継承するクラスを変えるよう記載があります。
from rules.contrib.models import RulesModel class DrfNews(RulesModel): title = models.CharField(max_length=50, verbose_name='タイトル') content = models.CharField(max_length=100, verbose_name='内容') section = models.ForeignKey('accounts.Section', on_delete=models.PROTECT) class Meta: rules_permissions = { 'view': can_view }
シリアライザの追加
ふつうのシリアライザを用意します。
class NewsSerializer(serializers.ModelSerializer): class Meta: model = DrfNews fields = '__all__'
ViewSetの追加
django-rulesはViewSetで使えます。そのため、ViewSetを追加します。
ViewSetで使う場合には、 AutoPermissionViewSetMixin
も継承します。
class NewsReadOnlyModelViewSet(AutoPermissionViewSetMixin, ReadOnlyModelViewSet): queryset = DrfNews.objects.all() serializer_class = NewsSerializer
なお、 ReadOnlyModelViewSet
以外にも、 ModelViewSet
や、Mixinを減らしたViewSetでも同様に使えます。
class NewsRetrieveModelViewSet(AutoPermissionViewSetMixin, mixins.RetrieveModelMixin, GenericViewSet): queryset = DrfNews.objects.all() serializer_class = NewsSerializer
urls.pyの追加
こちらもふつうのViewSet向けのurls.pyとなります。
router = routers.SimpleRouter() router.register('news', NewsReadOnlyModelViewSet) router.register('retrieve', NewsRetrieveModelViewSet) urlpatterns = [ path('', include(router.urls)), ]
settings.pyの追加
DRF向けの設定を追加します。
今回はBrowsable APIで動作確認するため、確認が楽なCookie認証にしておきます。
INSTALLED_APPS = [ # 自作アプリ 'api.apps.ApiConfig', # DRF 'rest_framework', ] # DRF向け REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.SessionAuthentication', ] }
動作確認
システム管理者
アクセス可能です。
管理職
アクセス可能です。
一般 (製造部)
アクセス可能です。
一般 (営業部)
アクセスできず、403が表示されています。
ソースコード
GitHubに追加しました。 api
ディレクトリあたりが今回のファイルです。
https://github.com/thinkAmi-sandbox/django-rules-sample