Bỏ qua đến nội dung chính

Syntax for Models

  • Một cấu hình mô hình (CONF) nên có ít nhất bốn phần: [request_definition], [policy_definition], [policy_effect], và [matchers].

  • Nếu một mô hình sử dụng Kiểm soát truy cập dựa trên vai trò (RBAC), nó cũng nên bao gồm phần [role_definition].

  • Một cấu hình mô hình (CONF) có thể chứa các chú thích. Các chú thích bắt đầu bằng ký hiệu #, và mọi thứ sau ký hiệu # sẽ được chú thích.

Định nghĩa yêu cầu

Phần [request_definition] định nghĩa các đối số trong hàm e.Enforce(...).

[request_definition]
r = sub, obj, act

Trong ví dụ này, sub, obj, và act đại diện cho bộ ba truy cập cổ điển: chủ thể (thực thể truy cập), đối tượng (tài nguyên được truy cập), và hành động (phương thức truy cập). Tuy nhiên, bạn có thể tùy chỉnh định dạng yêu cầu của riêng mình. Ví dụ, bạn có thể sử dụng sub, act nếu bạn không cần chỉ định một tài nguyên cụ thể, hoặc sub, sub2, obj, act nếu bạn có hai thực thể truy cập.

Định nghĩa Chính sách

[policy_definition] là định nghĩa cho một chính sách. Nó định nghĩa ý nghĩa của chính sách. Ví dụ, chúng ta có mô hình sau:

[policy_definition]
p = sub, obj, act
p2 = sub, act

Và chúng ta có chính sách sau (nếu trong một tệp chính sách):

p, alice, data1, read
p2, bob, write-all-objects

Mỗi dòng trong một chính sách được gọi là một quy tắc chính sách. Mỗi quy tắc chính sách bắt đầu bằng một loại chính sách, chẳng hạn như p hoặc p2. Nó được sử dụng để khớp với định nghĩa chính sách nếu có nhiều định nghĩa. Chính sách trên thể hiện ràng buộc sau đây. Ràng buộc có thể được sử dụng trong bộ so khớp.

(alice, data1, read) -> (p.sub, p.obj, p.act)
(bob, write-all-objects) -> (p2.sub, p2.act)
mẹo

Các phần tử trong một quy tắc chính sách luôn được coi là chuỗi. Nếu bạn có bất kỳ câu hỏi nào về điều này, vui lòng tham khảo thảo luận tại: https://github.com/casbin/casbin/issues/113

Hiệu ứng Chính sách

[policy_effect] là định nghĩa cho hiệu ứng chính sách. Nó xác định xem yêu cầu truy cập có nên được chấp thuận hay không nếu nhiều quy tắc chính sách khớp với yêu cầu. Ví dụ, một quy tắc cho phép và quy tắc khác từ chối.

[policy_effect]
e = some(where (p.eft == allow))

Hiệu ứng chính sách trên có nghĩa là nếu có bất kỳ quy tắc chính sách khớp nào của cho phép, hiệu ứng cuối cùng là cho phép (còn được gọi là cho phép-ghi đè). p.eft là hiệu ứng cho một chính sách, và nó có thể là cho phép hoặc từ chối. Nó là tùy chọn, và giá trị mặc định là allow. Vì chúng ta không chỉ định nó ở trên, nó sử dụng giá trị mặc định.

Một ví dụ khác cho hiệu ứng chính sách là:

[policy_effect]
e = !some(where (p.eft == deny))

Điều này có nghĩa là nếu không có quy tắc chính sách nào phù hợp với deny, thì kết quả cuối cùng là allow (còn được gọi là deny-override). some có nghĩa là tồn tại ít nhất một quy tắc chính sách phù hợp. any có nghĩa là tất cả các quy tắc chính sách phù hợp (không được sử dụng ở đây). Hiệu ứng chính sách thậm chí có thể được kết nối với các biểu thức logic:

[policy_effect]
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))

Điều này có nghĩa là phải có ít nhất một quy tắc chính sách phù hợp với allow, và không thể có bất kỳ quy tắc chính sách phù hợp với deny. Do đó, theo cách này, cả phân quyền allow và deny đều được hỗ trợ, và deny ghi đè.

ghi chú

Mặc dù chúng tôi thiết kế cú pháp của hiệu ứng chính sách như trên, những triển khai hiện tại chỉ sử dụng các hiệu ứng chính sách được mã hóa cứng. Điều này là bởi vì chúng tôi đã phát hiện rằng không cần quá nhiều sự linh hoạt ở cấp độ đó. Vì vậy, bây giờ bạn phải sử dụng một trong những hiệu ứng chính sách được tích hợp sẵn thay vì tùy chỉnh riêng của bạn.

Các hiệu ứng chính sách được tích hợp hỗ trợ bao gồm:

Hiệu ứng Chính sáchÝ nghĩaVí dụ
some(where (p.eft == allow))cho phép-ghi đèACL, RBAC, v.v.
!some(where (p.eft == deny))từ chối-ghi đèTừ chối-ghi đè
some(where (p.eft == allow)) && !some(where (p.eft == deny))cho phép-và-từ chốiCho phép-và-từ chối
ưu tiên(p.eft) || từ chốiưu tiênƯu tiên
ưu tiên chủ thể(p.eft)ưu tiên dựa trên vai tròChủ đề-Ưu tiên

Bộ so khớp

[matchers] là định nghĩa cho bộ so khớp chính sách. Các bộ so khớp là các biểu thức định nghĩa cách các quy tắc chính sách được đánh giá so với yêu cầu.

[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act

Bộ so khớp trên đây là đơn giản nhất và có nghĩa là chủ thể, đối tượng và hành động trong một yêu cầu nên khớp với những thứ trong một quy tắc chính sách.

Các toán tử số học như +, -, *, / và toán tử logic như &&, ||, ! có thể được sử dụng trong bộ so khớp.

Thứ tự của các biểu thức trong bộ so khớp

Thứ tự của các biểu thức có thể ảnh hưởng rất lớn đến hiệu suất. Hãy xem xét ví dụ sau để biết thêm chi tiết:

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
}

Thời gian thực thi có thể rất dài, lên đến 6 giây.

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

Tuy nhiên, nếu chúng ta điều chỉnh thứ tự của các biểu thức trong matchers và đặt các biểu thức tốn thời gian như các hàm phía sau, thời gian thực thi sẽ rất ngắn.

Thay đổi thứ tự của các biểu thức trong matchers trong ví dụ trên thành:

[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

Nhiều Loại Phần

Nếu bạn cần nhiều định nghĩa chính sách hoặc nhiều matchers, bạn có thể sử dụng p2 hoặc m2 làm ví dụ. Thực tế, tất cả bốn phần đã đề cập ở trên đều có thể sử dụng nhiều loại, và cú pháp là r theo sau bởi một số, chẳng hạn như r2 hoặc e2. Theo mặc định, bốn phần này nên tương ứng một-một. Ví dụ, phần r2 của bạn sẽ chỉ sử dụng matcher m2 để khớp với các chính sách p2.

Bạn có thể truyền một EnforceContext làm tham số đầu tiên của phương thức enforce để chỉ định các loại. EnforceContext được định nghĩa như sau:

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

Đây là một ví dụ sử dụng. Vui lòng tham khảo mô hìnhchính sách. Yêu cầu như sau:

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

Ngữ pháp đặc biệt

Bạn cũng có thể sử dụng toán tử "in", đây là toán tử duy nhất có tên dạng văn bản. Toán tử này kiểm tra mảng ở phía bên phải để xem nó có chứa một giá trị bằng với giá trị ở phía bên trái hay không. Sự bằng nhau được xác định bằng cách sử dụng toán tử ==, và thư viện này không kiểm tra các kiểu giữa các giá trị. Miễn là hai giá trị có thể được ép kiểu thành interface{} và vẫn có thể được kiểm tra sự bằng nhau với ==, chúng sẽ hoạt động như mong đợi. Lưu ý rằng bạn có thể sử dụng một tham số cho mảng, nhưng nó phải là một []interface{}.

Cũng tham khảo rbac_model_matcher_using_in_op, keyget2_model, và keyget_model.

Ví dụ:

[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"}})

Bộ Đánh Giá Biểu Thức

Việc đánh giá matcher trong Casbin được thực hiện bởi các bộ đánh giá biểu thức trong mỗi ngôn ngữ. Casbin tích hợp sức mạnh của chúng để cung cấp ngôn ngữ PERM thống nhất. Ngoài cú pháp mô hình được cung cấp ở đây, các bộ đánh giá biểu thức này có thể cung cấp thêm chức năng mà có thể không được hỗ trợ bởi một ngôn ngữ hoặc triển khai khác. Vui lòng cẩn thận khi sử dụng chức năng này.

Các bộ đánh giá biểu thức được sử dụng bởi mỗi triển khai Casbin như sau:

Triển khaiNgôn ngữBộ Đánh Giá Biểu Thức
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
ghi chú

Nếu bạn gặp phải vấn đề về hiệu suất với Casbin, nguyên nhân có thể là do hiệu quả thấp của bộ đánh giá biểu thức. Bạn có thể đề cập vấn đề này đến Casbin hoặc trực tiếp đến bộ đánh giá biểu thức để được tư vấn về việc tăng tốc hiệu suất. Để biết thêm chi tiết, vui lòng tham khảo phần Benchmarks.