この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
What is Casbin?
Casbinとは、ACL/RBAC/ABAC などのアクセス制御モデルをサポートするAuthorizationライブラリです。
Go/Java/Javascript/RustなどからDelphiやElixirまで幅広く対応しています。
Features of Casbin
公式ある説明より主要な機能の特徴。
- ハイブリッドアクセス制御モデル
Casbin では、アクセス制御モデルは PERM メタモデル (Policy, Effect, Request, Matchers) に基づいて
configファイルで設定できる。
なので、認可の仕組みを変更するのも簡単。
- 柔軟なポリシーストレージ
メモリやファイルだけでなく、ポリシー設定はMySQL、Postgres、OracleからMongoDB、Redis、
Cassandra、AWS S3などなどいろいろなデータストアがサポートされてる。
詳しくはここを確認。
- ロールマネージャ
ロールマネージャは、CasbinのRBACロール階層(ユーザ-ロールマッピング)を管理するために使用される。
ロールマネージャは、CasbinのポリシールールやLDAP、Okta、Auth0、Azure ADなどの外部ソースから
ロールデータを取得可能。
Casbinロールマネージャの詳細はここ。
Casbin's Model definition
CasbinではPERM-Metaモデル(Policy、Effect、Request、Matcher)でのアクセス制御モデルを採用しており、
モデル定義はconfファイルに記述する。
まずはACL(アクセスコントロールリスト)を例に説明。
- Request
アクセスする際のリクエストに関する情報。
ACLモデルでは一般的に、
sub(実行者)、obj(対象)、act(アクセス方法)
を使う。
[request_definition]
r = {sub, obj, act}
- Policy
ポリシーの記述形式を定義するモデル。
例えばこのモデル定義で「data1 (obj) を alice (sub) が read (act) できる」
というPolicyを記述した場合、
p, alice, data1, read
となる。
ポリシーの記述形式は下記。
[policy_definition]
p = {sub, obj, action}
- Matchers
RequestとPolicyがどうやってマッチするか、それぞれで定義したモデルを参照し、
条件式を使って定義する。
↓のACLモデル定義では、アクセス判定要求の
sub、obj、actの全てが一致するポリシーを合致するものと判定される。
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
- Effect
Requestが複数のポリシーにマッチした場合、どうやって判定させるか定義する。
※Matcherの評価値は、eftフィールドで利用可能
↓のACLモデル定義では、Matcherの条件式で合致するポリシーのうちeftが
allowに設定されたものが1つでもある場合に許可判定となる。
[policy_effect]
e = some(where (p.eft == allow))
また、RBAC(ロールベールのアクセスコントロール)も使用可能。
ロールベースのアクセスコントロールとは、ユーザーなどに対してにロール(管理者などの役割)が
割当てられ、このロールに対して任意の機能に対する実行許可が与えられる。
これら以外にも、属性ベース(ABAC)やRESTfulの
アクセスコントロールについてもサポートしている。
Environment
今回はTypescriptでcasbinを動かしてみる。
- OS : MacOS 10.15.7
- node : v14.4.0
nodeはHomebrewでインストール済み。
Setup
まずはTypeScriptとcasbinをモジュールをインストール。
<br />% npm install --save casbin
% npm install --save-dev @types/node
% npm install --save-dev typescript
# tsc --init しておく
Make Example
まずはcasbinのモデル定義用confファイルをbasic_mode.confという名前で作成する。 Request,Policy,Matchers,Effectの定義をそれぞれ記述。
[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
次にポリシーファイルを用意。
aliceはdata1をreadでき、bobはdata2をwriteできます。
p, alice, data1, read
p, bob, data2, write
サンプルコードを記述。
//example.ts
const casbin = require('casbin');
//リソースへアクセスするユーザー
const sub = 'alice';
//アクセス対象のリソース
const obj = 'data1';
//リソースに対する操作
const act = 'read';
async function execute(){
const enforcer = await casbin.newEnforcer('basic_model.conf', 'basic_policy.csv');
const res = await enforcer.enforce(sub, obj, act);
if (res) {
// aliceのdata1に対するread操作は許可されている
console.log("ok");
} else {
// リクエストは拒否されている
console.log("ng");
}
}
execute();
モデル定義ファイルとポリシーファイルを読み込み、操作が許可されているかどうかチェックする。
プログラムをコンパイルして実行すると、アクセスが許可されていることがわかる。
% tsc example.ts
% node example.js
ok
ロールモデルも試してみる。
まずはrole_model.confを定義。
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
role_definitionはRBAC形式に対応するための記述で、ロールの記述形式を定義している。
サンプルを記述。
taroに対してロール割当をおこなったあと、権限のチェックをする。
async function execute2() {
const enforcer = await casbin.newEnforcer('role_model.conf', 'basic_policy.csv');
//ロール割り当て
await enforcer.addNamedGroupingPolicy("g", "taro", "role1");
//ポリシー割当
await enforcer.addNamedPolicy("p", "role1", "data1", "read");
const res = await enforcer.enforce("taro", "data1", "read");
if (res) {
console.log("role : ok");
} else {
console.log("role : ng");
}
//ユーザーのロール取得
const roles = await enforcer.getRolesForUser("taro");
console.log(roles);
}
execute2();
実行結果は以下。
% node example.js
role : ok
[ 'role1' ]
Summary
手軽にACLやRBAC形式のアクセス制御ができた。
ちなみに、fastify用のプラグインもあり、
簡単に組み込むこともできる。