Lewati ke konten utama

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)
tip

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.

catatan

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 KebijakanArtiContoh
some(where (p.eft == allow))izinkan-overrideACL, RBAC, dll.
!some(where (p.eft == deny))tolak-menggantikanTolak-menggantikan
beberapa(di mana (p.eft == izinkan)) && !beberapa(di mana (p.eft == tolak))izinkan-dan-tolakIzinkan-dan-tolak
prioritas(p.eft) || tolakprioritasPrioritas
prioritasSubjek(p.eft)prioritas berdasarkan peranSubjek-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:

EnforceContext{"r2","p2","e2","m2"}
type EnforceContext struct {
RType string
PType string
EType string
MType string
}

Berikut adalah contoh penggunaannya. Silakan merujuk ke model dan kebijakan. Permintaannya adalah sebagai berikut:

// 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

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:

ImplementasiBahasaEvaluator Ekspresi
CasbinGolanghttps://github.com/Knetic/govaluate
jCasbinJavahttps://github.com/killme2008/aviator
Node-CasbinNode.jshttps://github.com/donmccurdy/expression-eval
PHP-CasbinPHPhttps://github.com/symfony/expression-language
PyCasbinPythonhttps://github.com/danthedeckie/simpleeval
Casbin.NETC#https://github.com/davideicardi/DynamicExpresso
Casbin4DDelphihttps://github.com/casbin4d/Casbin4D/tree/master/SourceCode/Common/Third%20Party/TExpressionParser
casbin-rsRusthttps://github.com/jonathandturner/rhai
casbin-cppC++https://github.com/ArashPartow/exprtk
catatan

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.