Syntax for Models
モデル設定(CONF)は、少なくとも4つのセクションを持つべきです:
[request_definition]
、[policy_definition]
、[policy_effect]
、そして[matchers]
。モデルがロールベースのアクセス制御(RBAC)を使用する場合、
[role_definition]
セクションも含めるべきです。モデル設定(CONF)にはコメントを含めることができます。 コメントは
#
記号で始まり、#
記号以降のすべてがコメントアウトされます。
リクエスト定義
[request_definition]
セクションは、e.Enforce(...)
関数の引数を定義します。
[request_definition]
r = sub, obj, act
この例では、sub
、obj
、act
は、クラシックなアクセストリプルを表しています:主体(アクセスするエンティティ)、オブジェクト(アクセスされるリソース)、アクション(アクセス方法)。 しかし、自分のリクエスト形式をカスタマイズすることもできます。 例えば、特定のリソースを指定する必要がない場合はsub, act
を使用するか、2つのアクセスエンティティがある場合はsub, sub2, obj, act
を使用することができます。
ポリシー定義
[policy_definition]
はポリシーの定義です。 それはポリシーの意味を定義します。 例えば、以下のモデルがあります:
[policy_definition]
p = sub, obj, act
p2 = sub, act
そして、以下のポリシーがあります(ポリシーファイル内の場合):
p, alice, data1, read
p2, bob, write-all-objects
ポリシーの各行はポリシールールと呼ばれます。 各ポリシールールはpolicy type
、例えばp
やp2
で始まります。 それは複数の定義がある場合にポリシー定義と一致するために使用されます。 上記のポリシーは以下のバインディングを示しています。 バインディングはマッチャーで使用できます。
(alice, data1, read) -> (p.sub, p.obj, p.act)
(bob, write-all-objects) -> (p2.sub, p2.act)
ポリシールールの要素は常にstrings
として扱われます。 これについて質問がある場合は、以下の議論を参照してください:https://github.com/casbin/casbin/issues/113
ポリシー効果
[policy_effect]
はポリシー効果の定義です。 それは複数のポリシールールがリクエストに一致する場合、アクセスリクエストが承認されるべきかどうかを決定します。 例えば、一つのルールが許可し、もう一つが拒否します。
[policy_effect]
e = some(where (p.eft == allow))
上記のポリシー効果は、もしallow
のポリシールールが一つでも一致するなら、最終的な効果はallow
(allow-overrideとも呼ばれる)であることを意味します。 p.eft
はポリシーの効果で、allow
またはdeny
のいずれかになります。 それはオプションで、デフォルト値はallow
です。 上記では指定していないので、デフォルト値を使用します。
ポリシー効果の別の例は次のとおりです:
[policy_effect]
e = !some(where (p.eft == deny))
これは、deny
の一致するポリシー規則がない場合、最終的な効果はallow
(deny-overrideとも呼ばれる)であることを意味します。 some
は、一致するポリシー規則が一つ存在することを意味します。 any
は、すべての一致するポリシー規則を意味します(ここでは使用されていません)。 ポリシー効果は論理表現とも結びつけることができます:
[policy_effect]
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
これは、allow
の一致するポリシー規則が少なくとも一つ存在しなければならず、deny
の一致するポリシー規則が存在してはならないことを意味します。 したがって、この方法では、許可と拒否の両方の認証がサポートされており、拒否が優先されます。
ポリシー効果の構文を上記のように設計しましたが、現在の実装ではハードコードされたポリシー効果のみを使用しています。 これは、そのレベルの柔軟性があまり必要ないと判断したためです。 したがって、現時点では、独自のカスタマイズの代わりに組み込みのポリシー効果のいずれかを使用する必要があります。
サポートされている組み込みのポリシー効果は次のとおりです:
ポリシー効果 | 意味 | 例 |
---|---|---|
some(where (p.eft == allow)) | allow-override | ACL、RBACなど |
!some(where (p.eft == deny)) | deny-override | Deny-override |
some(where (p.eft == allow)) && !some(where (p.eft == deny)) | allow-and-deny | Allow-and-deny |
priority(p.eft) || deny | priority | Priority |
subjectPriority(p.eft) | 役割に基づく優先度 | Subject-Priority |
Matchers
[matchers]
はポリシーマッチャーの定義です。 マッチャーは、ポリシールールがリクエストに対してどのように評価されるかを定義する表現式です。
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
上記のマッチャーは最も単純なもので、リクエストの主体、オブジェクト、アクションがポリシールールのものと一致することを意味します。
マッチャーでは、算術演算子(+, -, *, /
)や論理演算子(&&, ||, !
)を使用できます。
マッチャー内の表現式の順序
表現式の順序はパフォーマンスに大きな影響を与えます。 詳細については、以下の例をご覧ください:
const rbac_models = `
[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
`
func TestManyRoles(t *testing.T) {
m, _ := model.NewModelFromString(rbac_models)
e, _ := NewEnforcer(m, false)
roles := []string{"admin", "manager", "developer", "tester"}
// 2500 projects
for nbPrj := 1; nbPrj < 2500; nbPrj++ {
// 4 objects and 1 role per object (so 4 roles)
for _, role := range roles {
roleDB := fmt.Sprintf("%s_project:%d", role, nbPrj)
objectDB := fmt.Sprintf("/projects/%d", nbPrj)
e.AddPolicy(roleDB, objectDB, "GET")
}
jasmineRole := fmt.Sprintf("%s_project:%d", roles[1], nbPrj)
e.AddGroupingPolicy("jasmine", jasmineRole)
}
e.AddGroupingPolicy("abu", "manager_project:1")
e.AddGroupingPolicy("abu", "manager_project:2499")
// With same number of policies
// User 'abu' has only two roles
// User 'jasmine' has many roles (1 role per policy, here 2500 roles)
request := func(subject, object, action string) {
t0 := time.Now()
resp, _ := e.Enforce(subject, object, action)
tElapse := time.Since(t0)
t.Logf("RESPONSE %-10s %s\t %s : %5v IN: %+v", subject, object, action, resp, tElapse)
if tElapse > time.Millisecond*100 {
t.Errorf("More than 100 milliseconds for %s %s %s : %+v", subject, object, action, tElapse)
}
}
request("abu", "/projects/1", "GET") // really fast because only 2 roles in all policies and at the beginning of the casbin_rule table
request("abu", "/projects/2499", "GET") // fast because only 2 roles in all policies
request("jasmine", "/projects/1", "GET") // really fast at the beginning of the casbin_rule table
request("jasmine", "/projects/2499", "GET") // slow and fails the only 1st time <<<< pb here
request("jasmine", "/projects/2499", "GET") // fast maybe due to internal cache mechanism
// same issue with non-existing roles
// request("jasmine", "/projects/999999", "GET") // slow fails the only 1st time <<<< pb here
// request("jasmine", "/projects/999999", "GET") // fast maybe due to internal cache mechanism
}
強制実行時間は非常に長く、最大6秒になることがあります。
go test -run ^TestManyRoles$ github.com/casbin/casbin/v2 -v
=== RUN TestManyRoles
rbac_api_test.go:598: RESPONSE abu /projects/1 GET : true IN: 438.379µs
rbac_api_test.go:598: RESPONSE abu /projects/2499 GET : true IN: 39.005173ms
rbac_api_test.go:598: RESPONSE jasmine /projects/1 GET : true IN: 1.774319ms
rbac_api_test.go:598: RESPONSE jasmine /projects/2499 GET : true IN: 6.164071648s
rbac_api_test.go:600: More than 100 milliseconds for jasmine /projects/2499 GET : 6.164071648s
rbac_api_test.go:598: RESPONSE jasmine /projects/2499 GET : true IN: 12.164122ms
--- FAIL: TestManyRoles (6.24s)
FAIL
FAIL github.com/casbin/casbin/v2 6.244s
FAIL
しかし、マッチャー内の表現式の順序を調整し、関数のような時間がかかる表現式を後ろに置くと、実行時間は非常に短くなります。
上記の例でマッチャー内の表現式の順序を次のように変更します:
[matchers]
m = r.obj == p.obj && g(r.sub, p.sub) && r.act == p.act
go test -run ^TestManyRoles$ github.com/casbin/casbin/v2 -v
=== RUN TestManyRoles
rbac_api_test.go:599: RESPONSE abu /projects/1 GET : true IN: 786.635µs
rbac_api_test.go:599: RESPONSE abu /projects/2499 GET : true IN: 4.933064ms
rbac_api_test.go:599: RESPONSE jasmine /projects/1 GET : true IN: 2.908534ms
rbac_api_test.go:599: RESPONSE jasmine /projects/2499 GET : true IN: 7.292963ms
rbac_api_test.go:599: RESPONSE jasmine /projects/2499 GET : true IN: 6.168307ms
--- PASS: TestManyRoles (0.05s)
PASS
ok github.com/casbin/casbin/v2 0.053s
複数のセクションタイプ
複数のポリシー定義やマッチャーが必要な場合、p2
やm2
を例に使用できます。 実際、上記の4つのセクションすべては複数のタイプを使用でき、構文はr
に続けて数字を付ける、例えばr2
やe2
です。 デフォルトでは、これら4つのセクションは一対一に対応するべきです。 例えば、あなたのr2
セクションは、p2
ポリシーにマッチするためにm2
マッチャーのみを使用します。
EnforceContext
をenforce
メソッドの最初のパラメータとして渡すことで、タイプを指定できます。 EnforceContext
は次のように定義されています:
- Go
- Node.js
- Java
EnforceContext{"r2","p2","e2","m2"}
type EnforceContext struct {
RType string
PType string
EType string
MType string
}
const enforceContext = new EnforceContext('r2', 'p2', 'e2', 'm2');
class EnforceContext {
constructor(rType, pType, eType, mType) {
this.pType = pType;
this.eType = eType;
this.mType = mType;
this.rType = rType;
}
}
EnforceContext enforceContext = new EnforceContext("2");
public class EnforceContext {
private String pType;
private String eType;
private String mType;
private String rType;
public EnforceContext(String suffix) {
this.pType = "p" + suffix;
this.eType = "e" + suffix;
this.mType = "m" + suffix;
this.rType = "r" + suffix;
}
}
ここに使用例があります。 モデルとポリシーを参照してください。 リクエストは次のとおりです:
- Go
- Node.js
- Java
// Pass in a suffix as a parameter to NewEnforceContext, such as 2 or 3, and it will create r2, p2, etc.
enforceContext := NewEnforceContext("2")
// You can also specify a certain type individually
enforceContext.EType = "e"
// Don't pass in EnforceContext; the default is r, p, e, m
e.Enforce("alice", "data2", "read") // true
// Pass in EnforceContext
e.Enforce(enforceContext, struct{ Age int }{Age: 70}, "/data1", "read") //false
e.Enforce(enforceContext, struct{ Age int }{Age: 30}, "/data1", "read") //true
// Pass in a suffix as a parameter to NewEnforceContext, such as 2 or 3, and it will create r2, p2, etc.
const enforceContext = new NewEnforceContext('2');
// You can also specify a certain type individually
enforceContext.eType = "e"
// Don't pass in EnforceContext; the default is r, p, e, m
e.Enforce("alice", "data2", "read") // true
// Pass in EnforceContext
e.Enforce(enforceContext, {Age: 70}, "/data1", "read") //false
e.Enforce(enforceContext, {Age: 30}, "/data1", "read") //true
// Pass in a suffix as a parameter to NewEnforceContext, such as 2 or 3, and it will create r2, p2, etc.
EnforceContext enforceContext = new EnforceContext("2");
// You can also specify a certain type individually
enforceContext.seteType("e");
// Don't pass in EnforceContext; the default is r, p, e, m
e.enforce("alice", "data2", "read"); // true
// Pass in EnforceContext
// TestEvalRule is located in https://github.com/casbin/jcasbin/blob/master/src/test/java/org/casbin/jcasbin/main/AbacAPIUnitTest.java#L56
e.enforce(enforceContext, new AbacAPIUnitTest.TestEvalRule("alice", 70), "/data1", "read"); // false
e.enforce(enforceContext, new AbacAPIUnitTest.TestEvalRule("alice", 30), "/data1", "read"); // true
特別な文法
また、テキスト名を持つ唯一の演算子である"in"演算子を使用することもできます。 この演算子は、右側の配列が左側の値と等しい値を含んでいるかどうかをチェックします。 等価性は==演算子を使用して判断され、このライブラリは値間の型をチェックしません。 2つの値がinterface{}にキャストでき、==で等価性をチェックできる限り、期待通りに動作します。 配列にパラメータを使用することもできますが、それは[]interface{}でなければなりません。
rbac_model_matcher_using_in_op、keyget2_model、keyget_modelも参照してください。
例:
[request_definition]
r = sub, obj
...
[matchers]
m = r.sub.Name in (r.obj.Admins)
e.Enforce(Sub{Name: "alice"}, Obj{Name: "a book", Admins: []interface{}{"alice", "bob"}})
表現式評価器
Casbinのマッチャー評価は、各言語の表現式評価器によって実装されています。 Casbinは、統一されたPERM言語を提供するために、彼らの力を統合します。 ここで提供されるモデル構文に加えて、これらの表現評価器は、他の言語や実装ではサポートされていない可能性のある追加の機能を提供することがあります。 この機能を使用する際には注意してください。
各Casbin実装で使用される表現評価器は次のとおりです:
実装 | 言語 | 表現評価器 |
---|---|---|
Casbin | Golang | https://github.com/Knetic/govaluate |
jCasbin | Java | https://github.com/killme2008/aviator |
Node-Casbin | Node.js | https://github.com/donmccurdy/expression-eval |
PHP-Casbin | PHP | https://github.com/symfony/expression-language |
PyCasbin | Python | https://github.com/danthedeckie/simpleeval |
Casbin.NET | C# | https://github.com/davideicardi/DynamicExpresso |
Casbin4D | Delphi | https://github.com/casbin4d/Casbin4D/tree/master/SourceCode/Common/Third%20Party/TExpressionParser |
casbin-rs | Rust | https://github.com/jonathandturner/rhai |
casbin-cpp | C++ | https://github.com/ArashPartow/exprtk |
Casbinでパフォーマンスの問題に遭遇した場合、それはおそらく式評価器の効率の低さが原因です。 パフォーマンスの向上についてのアドバイスを求めるために、問題をCasbinまたは式評価器に直接報告することができます。 詳細については、ベンチマークセクションを参照してください。