Django REST Frameworkで、 django-rulesを使ってみた

前回、Djangodjango-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の追加

前回同様、

  • システム管理者は閲覧可能
  • 管理職も閲覧可能
  • 一般は、自分と同じ部署のみ閲覧可能

とします。

DRFdjango-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向けの設定を追加します。

なお、DRFによるAPIも認証後に認可判定を行います。

今回はBrowsable APIで動作確認するため、確認が楽なCookie認証にしておきます。

INSTALLED_APPS = [
    # 自作アプリ
    'api.apps.ApiConfig',

    # DRF
    'rest_framework',
]

# DRF向け
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
    ]
}

 

動作確認

システム管理者

アクセス可能です。

f:id:thinkAmi:20200426220800p:plain:w400

 

管理職

アクセス可能です。

f:id:thinkAmi:20200426220829p:plain:w400

 

一般 (製造部)

アクセス可能です。

f:id:thinkAmi:20200426220846p:plain:w400

 

一般 (営業部)

アクセスできず、403が表示されています。

f:id:thinkAmi:20200426220909p:plain:w400

 

ソースコード

GitHubに追加しました。 api ディレクトリあたりが今回のファイルです。
https://github.com/thinkAmi-sandbox/django-rules-sample