Syntax for Models
Konfigurasi model (CONF) harus memiliki setidaknya empat bagian:
[request_definition]
,[policy_definition]
,[policy_effect]
, dan[matchers]
.Jika sebuah model menggunakan Kontrol Akses Berbasis Peran (RBAC), model tersebut juga harus menyertakan bagian
[role_definition]
.Konfigurasi model (CONF) dapat berisi komentar. Komentar dimulai dengan simbol
#
, dan semua yang ada setelah simbol#
akan diabaikan.
Definisi permintaan
Bagian [request_definition]
mendefinisikan argumen dalam fungsi e.Enforce(...)
.
[request_definition]
r = sub, obj, act
Dalam contoh ini, sub
, obj
, dan act
mewakili triple akses klasik: subjek (entitas yang mengakses), objek (sumber daya yang diakses), dan aksi (metode akses). Namun, Anda dapat menyesuaikan format permintaan Anda sendiri. Misalnya, Anda dapat menggunakan sub, act
jika Anda tidak perlu menentukan sumber daya tertentu, atau sub, sub2, obj, act
jika Anda memiliki dua entitas yang mengakses.
Definisi Kebijakan
[policy_definition]
adalah definisi untuk sebuah kebijakan. Ini mendefinisikan makna dari kebijakan tersebut. Misalnya, kami memiliki model berikut:
[policy_definition]
p = sub, obj, act
p2 = sub, act
Dan kami memiliki kebijakan berikut (jika dalam file kebijakan):
p, alice, data1, read
p2, bob, write-all-objects
Setiap baris dalam kebijakan disebut sebagai aturan kebijakan. Setiap aturan kebijakan dimulai dengan tipe kebijakan
, seperti p
atau p2
. Ini digunakan untuk mencocokkan definisi kebijakan jika ada beberapa definisi. Kebijakan di atas menunjukkan pengikatan berikut. Pengikatan ini dapat digunakan dalam matcher.
(alice, data1, read) -> (p.sub, p.obj, p.act)
(bob, write-all-objects) -> (p2.sub, p2.act)
Elemen-elemen dalam aturan kebijakan selalu dianggap sebagai string
. Jika Anda memiliki pertanyaan tentang ini, silakan merujuk pada diskusi di: https://github.com/casbin/casbin/issues/113
Efek Kebijakan
[policy_effect]
adalah definisi untuk efek kebijakan. Ini menentukan apakah permintaan akses harus disetujui jika beberapa aturan kebijakan cocok dengan permintaan tersebut. Misalnya, satu aturan mengizinkan dan yang lain menolak.
[policy_effect]
e = some(where (p.eft == allow))
Efek kebijakan di atas berarti bahwa jika ada aturan kebijakan yang cocok dengan allow
, efek akhirnya adalah allow
(juga dikenal sebagai allow-override). p.eft
adalah efek untuk kebijakan, dan dapat berupa allow
atau deny
. Ini bersifat opsional, dan nilai defaultnya adalah allow
. Karena kita tidak menentukannya di atas, maka nilai default yang digunakan.
Contoh lain untuk efek kebijakan adalah:
[policy_effect]
e = !some(where (p.eft == deny))
Ini berarti jika tidak ada aturan kebijakan yang cocok dengan deny
, efek akhirnya adalah allow
(juga dikenal sebagai deny-override). some
berarti ada setidaknya satu aturan kebijakan yang cocok. any
berarti semua aturan kebijakan yang cocok (tidak digunakan di sini). Efek kebijakan bahkan dapat dihubungkan dengan ekspresi logika:
[policy_effect]
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
Ini berarti harus ada setidaknya satu aturan kebijakan yang cocok dengan allow
, dan tidak boleh ada aturan kebijakan yang cocok dengan deny
. Oleh karena itu, dengan cara ini, baik otorisasi allow maupun deny didukung, dan deny menggantikan.
Meskipun kami merancang sintaks efek kebijakan seperti di atas, implementasi saat ini hanya menggunakan efek kebijakan yang dikodekan secara keras. Hal ini karena kami menemukan bahwa tidak ada kebutuhan yang besar untuk tingkat fleksibilitas tersebut. Jadi untuk saat ini, Anda harus menggunakan salah satu efek kebijakan bawaan alih-alih menyesuaikan sendiri.
Efek kebijakan bawaan yang didukung adalah:
Efek Kebijakan | Arti | Contoh |
---|---|---|
some(where (p.eft == allow)) | izinkan-override | ACL, RBAC, dll. |
!some(where (p.eft == deny)) | tolak-menggantikan | Tolak-menggantikan |
beberapa(di mana (p.eft == izinkan)) && !beberapa(di mana (p.eft == tolak)) | izinkan-dan-tolak | Izinkan-dan-tolak |
prioritas(p.eft) || tolak | prioritas | Prioritas |
prioritasSubjek(p.eft) | prioritas berdasarkan peran | Subjek-Prioritas |
Pemadanan (Matchers)
[matchers]
adalah definisi untuk pemadanan kebijakan. Pemadanan adalah ekspresi yang mendefinisikan bagaimana aturan kebijakan dievaluasi terhadap permintaan.
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
Pemadanan di atas adalah yang paling sederhana dan berarti bahwa subjek, objek, dan tindakan dalam permintaan harus sesuai dengan yang ada dalam aturan kebijakan.
Operator aritmatika seperti +, -, *, /
dan operator logika seperti &&, ||, !
dapat digunakan dalam pemadanan.
Urutan ekspresi dalam pemadanan
Urutan ekspresi dapat sangat mempengaruhi kinerja. Lihat contoh berikut untuk detail lebih lanjut:
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
}
Waktu penegakan mungkin sangat lama, hingga 6 detik.
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
Namun, jika kita menyesuaikan urutan ekspresi dalam matchers dan menempatkan ekspresi yang lebih memakan waktu seperti fungsi di belakang, waktu eksekusi akan menjadi sangat singkat.
Mengubah urutan ekspresi dalam matchers pada contoh di atas menjadi:
[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
Beberapa Jenis Bagian
Jika Anda memerlukan beberapa definisi kebijakan atau beberapa matchers, Anda dapat menggunakan p2
atau m2
sebagai contoh. Sebenarnya, keempat bagian yang disebutkan di atas dapat menggunakan beberapa jenis, dan sintaksnya adalah r
diikuti dengan angka, seperti r2
atau e2
. Secara default, keempat bagian ini seharusnya berkorespondensi satu-ke-satu. Misalnya, bagian r2
Anda hanya akan menggunakan matcher m2
untuk mencocokkan kebijakan p2
.
Anda dapat meneruskan EnforceContext
sebagai parameter pertama dari metode enforce
untuk menentukan jenis-jenisnya. EnforceContext
didefinisikan sebagai berikut:
- 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;
}
}
Berikut adalah contoh penggunaannya. Silakan merujuk ke model dan kebijakan. Permintaannya adalah sebagai berikut:
- 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
Tata Bahasa Khusus
Anda juga bisa menggunakan operator "in", yang merupakan satu-satunya operator dengan nama teks. Operator ini memeriksa array di sisi kanan untuk melihat apakah mengandung nilai yang sama dengan nilai di sisi kiri. Kesamaan ditentukan dengan menggunakan operator ==, dan perpustakaan ini tidak memeriksa tipe antara nilai-nilai tersebut. Selama dua nilai dapat diubah menjadi interface{} dan masih dapat diperiksa kesamaannya dengan ==, mereka akan berperilaku seperti yang diharapkan. Perhatikan bahwa Anda dapat menggunakan parameter untuk array, tetapi itu harus berupa []interface{}.
Juga merujuk ke rbac_model_matcher_using_in_op, keyget2_model, dan keyget_model.
Contoh:
[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"}})
Evaluator Ekspresi
Evaluasi pencocokan di Casbin diimplementasikan oleh evaluator ekspresi dalam setiap bahasa. Casbin mengintegrasikan kekuatan mereka untuk menyediakan bahasa PERM yang terpadu. Selain sintaks model yang disediakan di sini, evaluator ekspresi ini mungkin menawarkan fungsionalitas tambahan yang mungkin tidak didukung oleh bahasa atau implementasi lain. Harap berhati-hati saat menggunakan fungsionalitas ini.
Evaluator ekspresi yang digunakan oleh setiap implementasi Casbin adalah sebagai berikut:
Implementasi | Bahasa | Evaluator Ekspresi |
---|---|---|
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 |
Jika Anda menemukan masalah performa dengan Casbin, hal ini kemungkinan disebabkan oleh efisiensi yang rendah dari evaluator ekspresi. Anda dapat menghadapi masalah tersebut kepada Casbin atau evaluator ekspresi secara langsung untuk mendapatkan saran tentang mempercepat performa. Untuk detail lebih lanjut, silakan merujuk ke bagian Benchmarks.