kakakakakku blog

Weekly Tech Blog: Keep on Learning!

Casbin 認可ポリシーを DynamoDB に保存できるアダプター「python-dycasbin」を試した

Casbin で認可ポリシーを保存する1番簡単な選択肢は CSV ファイルだけど,アダプターを使うと認可ポリシーをデータベースで管理できる👌今回は PyCasbin で,ドキュメントに載っている DynamoDB Adapter (python-dycasbin) を使って,Casbin 認可ポリシーを Amazon DynamoDB に保存してみた.試したログをまとめておく❗️

casbin.org

また LoadPolicy()SavePolicy() など,アダプターインタフェースを実装すればカスタムアダプターも実装できる👀

casbin.org

👾 requirements.txt

今回は PyCasbin と python-dycasbin の最新バージョンを使う👌

casbin==1.36.2
python-dycasbin==0.4.1

pypi.org

pypi.org

👾 model.conf

そして,Casbin Model はドキュメントにも載っている ACL (Access Control List) をそのまま使う👌

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act

casbin.org

👾 add-policy.py

まず,python-dycasbin を使って Amazon DynamoDB テーブルに Casbin 認可ポリシーを登録する.今回はサンプルとして Amazon S3 を参考に s3:PutObject アクションを制御することにした.

  • sub (kakakakakku) は obj1 (arn:aws:s3:::sample-bucket-1/*) に対して act (s3:PutObject) できる
  • sub (kakakakakku) は obj2 (arn:aws:s3:::sample-bucket-2/*) に対して act (s3:PutObject) できる

また Amazon DynamoDB は LocalStack を使ってローカル環境でアクセスできるようにしておく✅

import casbin
from python_dycasbin import adapter

adapter = adapter.Adapter(endpoint_url='http://localhost:4566')

e = casbin.Enforcer('model.conf', adapter, True)

sub = 'kakakakakku'
obj1 = 'arn:aws:s3:::sample-bucket-1/*'
obj2 = 'arn:aws:s3:::sample-bucket-2/*'
act = 's3:PutObject'

e.add_policy(sub, obj1, act)
e.add_policy(sub, obj2, act)

add-policy.py を実行すると,Adapter 経由で自動的に Amazon DynamoDB テーブル(デフォルトだと casbin_rule)が構築される.Adapter クラスの初期化 (__init__) で毎回テーブルを構築しようと試みるところは微妙に感じるし,テーブル設定として WCU / RCU が 10 で固定されているのも厳しい気がした😇オンデマンドキャパシティを選択するオプションがあっても良さそう.

github.com

登録した Casbin 認可ポリシーは Amazon DynamoDB テーブルでは以下のように登録されていた.

$ awslocal dynamodb scan --table-name casbin_rule
{
    "Items": [
        {
            "ptype": {
                "S": "p"
            },
            "v0": {
                "S": "kakakakakku"
            },
            "v1": {
                "S": "arn:aws:s3:::sample-bucket-1/*"
            },
            "id": {
                "S": "bf571b8e16cef131aeac79c15a800421"
            },
            "v2": {
                "S": "s3:PutObject"
            }
        },
        {
            "ptype": {
                "S": "p"
            },
            "v0": {
                "S": "kakakakakku"
            },
            "v1": {
                "S": "arn:aws:s3:::sample-bucket-2/*"
            },
            "id": {
                "S": "46808bf495552ff34293bdb9f8f0a9b2"
            },
            "v2": {
                "S": "s3:PutObject"
            }
        }
    ],
    "Count": 2,
    "ScannedCount": 2,
    "ConsumedCapacity": null
}

登録されたデータ(アイテム)は LocalStack Resource Browser でも確認できた👌

👾 enforce.py

今度は arn:aws:s3:::sample-bucket-1/*arn:aws:s3:::sample-bucket-2/*arn:aws:s3:::sample-bucket-3/* に対する s3:PutObject アクションの許可を確認する.

import casbin
from python_dycasbin import adapter

adapter = adapter.Adapter(endpoint_url='http://localhost:4566')

e = casbin.Enforcer('model.conf', adapter, True)

sub = 'kakakakakku'
obj1 = 'arn:aws:s3:::sample-bucket-1/*'
obj2 = 'arn:aws:s3:::sample-bucket-2/*'
obj3 = 'arn:aws:s3:::sample-bucket-3/*'
act = 's3:PutObject'

e.enforce(sub, obj1, act)
e.enforce(sub, obj2, act)
e.enforce(sub, obj3, act)

期待通りに True, True, False と認可判断ができていた❗️

2024-06-21 19:00:00,000 Request: kakakakakku, arn:aws:s3:::sample-bucket-1/*, s3:PutObject ---> True
2024-06-21 19:00:00,000 Request: kakakakakku, arn:aws:s3:::sample-bucket-2/*, s3:PutObject ---> True
2024-06-21 19:00:00,000 Request: kakakakakku, arn:aws:s3:::sample-bucket-3/*, s3:PutObject ---> False

ちなみに python-dycasbin の実装を読むと,Amazon DynamoDB テーブルに対して Scan を実行していて気になる💨Amazon DynamoDB テーブルに GSI (Global Secondary Index) を追加して,Scan ではなく Query を使うように変更したプルリクエストも出ているようだった.

👾 remove-policy.py

最後は arn:aws:s3:::sample-bucket-2/* に対する Casbin 認可ポリシーを削除してみる.

import casbin
from python_dycasbin import adapter

adapter = adapter.Adapter(endpoint_url='http://localhost:4566')

e = casbin.Enforcer('model.conf', adapter, True)

sub = 'kakakakakku'
obj2 = 'arn:aws:s3:::sample-bucket-2/*'
act = 's3:PutObject'

e.remove_policy(sub, obj2, act)

もう一度 s3:PutObject アクションの許可を確認したところ,期待通りに True, False, False と認可判断ができていた❗️

2024-06-21 19:00:00,000 Request: kakakakakku, arn:aws:s3:::sample-bucket-1/*, s3:PutObject ---> True
2024-06-21 19:00:00,000 Request: kakakakakku, arn:aws:s3:::sample-bucket-2/*, s3:PutObject ---> False
2024-06-21 19:00:00,000 Request: kakakakakku, arn:aws:s3:::sample-bucket-3/*, s3:PutObject ---> False

Amazon DynamoDB テーブルのデータ(アイテム)も削除されていた👌

$ awslocal dynamodb scan --table-name casbin_rule
{
    "Items": [
        {
            "ptype": {
                "S": "p"
            },
            "v0": {
                "S": "kakakakakku"
            },
            "v1": {
                "S": "arn:aws:s3:::sample-bucket-1/*"
            },
            "id": {
                "S": "bf571b8e16cef131aeac79c15a800421"
            },
            "v2": {
                "S": "s3:PutObject"
            }
        }
    ],
    "Count": 1,
    "ScannedCount": 1,
    "ConsumedCapacity": null
}