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
تمثل الثلاثي الكلاسيكي للوصول: الكائن الفاعل (الكيان الذي يقوم بالوصول)، الكائن الموصول إليه (المورد المُتاح)، والفعل (طريقة الوصول). ومع ذلك، يمكنك تخصيص تنسيق الطلب الخاص بك. على سبيل المثال، يمكنك استخدام 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
(المعروف أيضًا بالسماح بالتجاوز). p.eft
هو التأثير لسياسة، ويمكن أن يكون إما allow
أو deny
. هو اختياري، والقيمة الافتراضية هي allow
. بما أننا لم نحدده أعلاه، فإنه يستخدم القيمة الافتراضية.
مثال آخر لتأثير السياسة هو:
[policy_effect]
e = !some(where (p.eft == deny))
هذا يعني أنه إذا لم يكن هناك قواعد سياسة مطابقة لـ deny
، فإن التأثير النهائي هو allow
(المعروف أيضًا بالمنع بالتجاوز). some
يعني أن هناك قاعدة سياسة مطابقة واحدة. any
يعني أن جميع قواعد السياسة المطابقة (غير مستخدم هنا). يمكن حتى ربط تأثير السياسة بالتعبيرات المنطقية:
[policy_effect]
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
هذا يعني أنه يجب أن يكون هناك على الأقل قاعدة سياسة مطابقة واحدة لـ allow
، ولا يمكن أن يكون هناك أي قاعدة سياسة مطابقة لـ deny
. لذلك، بهذه الطريقة، يتم دعم كل من التفويضات المسموح بها والممنوعة، والمنع يتجاوز.
على الرغم من أننا صممنا بناء جملة تأثير السياسة كما هو موضح أعلاه، فإن التنفيذات الحالية تستخدم فقط تأثيرات سياسة محددة مسبقًا. وذلك لأننا وجدنا أنه لا توجد حاجة كبيرة لهذا المستوى من المرونة. لذا الآن، يجب عليك استخدام أحد تأثيرات السياسة المدمجة بدلاً من تخصيص تأثير خاص بك.
تأثيرات السياسة المدمجة المدعومة هي:
تأثير السياسة | المعنى | مثال |
---|---|---|
some(where (p.eft == allow)) | السماح بالتجاوز | ACL, RBAC, إلخ. |
!some(where (p.eft == deny)) | المنع بالتجاوز | المنع بالتجاوز |
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]
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
كأمثلة. في الواقع، يمكن استخدام جميع الأقسام الأربعة المذكورة أعلاه بأنواع متعددة، والصياغة هي 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;
}
}
إليك مثال على الاستخدام. يرجى الرجوع إلى النموذج والسياسة. الطلب كالتالي:
- 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"، وهو العامل الوحيد الذي يحمل اسمًا نصيًا. يتحقق هذا العامل من المصفوفة على الجانب الأيمن لمعرفة ما إذا كانت تحتوي على قيمة مساوية للقيمة على الجانب الأيسر. يتم تحديد المساواة باستخدام عامل ==، ولا يتحقق هذا المكتبة من الأنواع بين القيم. طالما يمكن تحويل قيمتين إلى 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 أو مقيم التعبير مباشرةً للحصول على نصيحة بشأن تسريع الأداء. لمزيد من التفاصيل، يرجى الرجوع إلى قسم Benchmarks.