Syntax for Models
การกำหนดค่าโมเดล (CONF) ควรมีอย่างน้อยสี่ส่วน:
[request_definition]
,[policy_definition]
,[policy_effect]
, และ[matchers]
หากโมเดลใช้การควบคุมการเข้าถึงตามบทบาท (RBAC), ก็ควรจะรวมส่วน
[role_definition]
ด้วยการกำหนดค่าโมเดล (CONF) สามารถมีคอมเมนต์ได้ คอมเมนต์เริ่มต้นด้วยสัญลักษณ์
#
, และทุกอย่างหลังจากสัญลักษณ์#
จะถูกคอมเมนต์ออก
คำนิยามคำขอ
ส่วน [request_definition]
กำหนดอาร์กิวเมนต์ในฟังก์ชัน e.Enforce(...)
[request_definition]
r = sub, obj, act
ในตัวอย่างนี้, sub
, obj
, และ act
แทนสามส่วนหลักของการเข้าถึง: ผู้เรียกร้อง (entity ที่เข้าถึง), วัตถุ (ทรัพยากรที่ถูกเข้าถึง), และการกระทำ (วิธีการเข้าถึง) อย่างไรก็ตาม, คุณสามารถกำหนดรูปแบบคำขอของคุณเองได้ ตัวอย่างเช่น, คุณสามารถใช้ sub, act
หากคุณไม่จำเป็นต้องระบุทรัพยากรเฉพาะ, หรือ sub, sub2, obj, act
หากคุณมีผู้เรียกร้องสองคน
คำนิยามนโยบาย
ส่วน [policy_definition]
เป็นคำนิยามสำหรับนโยบาย มันกำหนดความหมายของนโยบาย ตัวอย่างเช่น, เรามีโมเดลต่อไปนี้:
[policy_definition]
p = sub, obj, act
p2 = sub, act
และเรามีนโยบายต่อไปนี้ (หากอยู่ในไฟล์นโยบาย):
p, alice, data1, read
p2, bob, write-all-objects
แต่ละบรรทัดในนโยบายเรียกว่ากฎนโยบาย แต่ละกฎนโยบายเริ่มต้นด้วย ประเภทนโยบาย
, เช่น 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 |
subjectPriority(p.eft) | ลำดับความสำคัญตามบทบาท | Subject-Priority |
Matchers
[matchers]
เป็นการนิยามสำหรับตัวจับคู่นโยบาย ตัวจับคู่เป็นนิพจน์ที่กำหนดวิธีการประเมินกฎนโยบายเทียบกับคำขอ
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
ตัวจับคู่ด้านบนเป็นตัวจับคู่ที่ง่ายที่สุดและหมายความว่า subject, object และ action ในคำขอควรตรงกับที่อยู่ในกฎนโยบาย
ตัวดำเนินการทางคณิตศาสตร์เช่น +, -, *, /
และตัวดำเนินการทางตรรกะเช่น &&, ||, !
สามารถใช้ในตัวจับคู่ได้
ลำดับของนิพจน์ในตัวจับคู่
ลำดับของนิพจน์สามารถส่งผลกระทบต่อประสิทธิภาพได้อย่างมาก ดูตัวอย่างต่อไปนี้เพื่อข้อมูลเพิ่มเติม:
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
Multiple Section Types
หากคุณต้องการนิยามนโยบายหลายอย่างหรือตัวจับคู่หลายอย่าง คุณสามารถใช้ p2
หรือ m2
เป็นตัวอย่าง จริงๆแล้ว ทั้งสี่ส่วนที่กล่าวถึงข้างต้นสามารถใช้หลายประเภทได้ และไวยากรณ์คือ r
ตามด้วยตัวเลข เช่น r2
หรือ e2
โดยค่าเริ่มต้น สี่ส่วนนี้ควรสอดคล้องกันทีละหนึ่ง ตัวอย่างเช่น ส่วน r2
ของคุณจะใช้ตัวจับคู่ m2
เพื่อจับคู่กับนโยบาย p2
เท่านั้น
คุณสามารถส่ง 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;
}
}
นี่คือตัวอย่างการใช้งาน โปรดดูที่ model และ policy คำขอมีดังนี้:
- 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
Special Grammar
คุณยังสามารถใช้ตัวดำเนินการ "in" ซึ่งเป็นตัวดำเนินการเพียงตัวเดียวที่มีชื่อเป็นข้อความ ตัวดำเนินการนี้ตรวจสอบอาร์เรย์ทางด้านขวาเพื่อดูว่ามีค่าที่เท่ากับค่าทางด้านซ้ายหรือไม่ ความเท่าเทียมกำหนดโดยใช้ตัวดำเนินการ == และไลบรารีนี้ไม่ตรวจสอบประเภทของค่าระหว่างกัน ตราบใดที่สองค่าสามารถแปลงเป็น 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"}})
Expression Evaluator
การประเมินตัวจับคู่ใน Casbin ได้รับการดำเนินการโดยตัวประเมินนิพจน์ในแต่ละภาษา Casbin รวมพลังของพวกเขาเพื่อให้ได้ภาษา PERM ที่เป็นเอกภาพ นอกเหนือจากไวยากรณ์ของโมเดลที่นำเสนอที่นี่ ตัวประเมินนิพจน์เหล่านี้อาจมีฟังก์ชันเพิ่มเติมที่อาจไม่ได้รับการสนับสนุนโดยภาษาหรือการดำเนินการอื่น โปรดระมัดระวังเมื่อใช้ฟังก์ชันนี้
ตัวประเมินนิพจน์ที่ใช้โดยการดำเนินการ Casbin แต่ละครั้งมีดังนี้:
Implementation | Language | Expression Evaluator |
---|---|---|
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 หรือตัวประเมินนิพจน์โดยตรงเพื่อขอคำแนะนำในการเร่งความเร็วของประสิทธิภาพ สำหรับรายละเอียดเพิ่มเติม โปรดดูที่ส่วน Benchmarks