Casbin
المستودع الرسمي: casbin/casbin: An authorization library that supports access control models like ACL, RBAC, ABAC in Golang (github.com)
الوثائق الرسمية: نظرة عامة | Casbin
TIP
هذه المقالة تعتبر مقدمة لـ Casbin، إذا كنت تريد فهمًا أكثر تفصيلاً يرجى زيارة الموقع الرسمي للتعلم.
مقدمة
في نظام ما، يحتاج مبرمجو الواجهة الخلفية إلى المسؤولية عن إدارة صلاحيات API، وهذا يتطلب الكثير من العمل، فإذا كان كل مشروع يحتاج إلى كتابة إطار صلاحيات من الصفر، فسيضيع الكثير من الوقت. تميل الشركات الكبرى التي لديها موارد بشرية ومادية أكثر إلى تطوير إطار صلاحيات خاص بها، لكن معظم الشركات الصغيرة والمتوسطة لا تستطيع تحمل تكلفة التطوير هذه، لذلك أصبحت أطر الصلاحيات مفتوحة المصدر في السوق خيارها الأول. Casbin هو مكتبة تحكم في الوصول مفتوحة المصدر وفعالة، مطورة بلغة Go، وتدعم أيضًا اللغات الرئيسية الأخرى.
تجدر الإشارة إلى أن Casbin هو مجرد إطار تحكم في الوصول، وهو مسؤول فقط عن التحكم في الوصول، أما منطق مصادقة الوصول فليس مسؤولية Casbin، فهو يخزن فقط علاقة التعيين بين المستخدمين والأدوار. وهو يدعم نماذج التحكم في الوصول التالية:
- ACL (قائمة التحكم في الوصول)
- ACL مع مستخدم خارق
- ACL بدون مستخدمين: مفيد بشكل خاص للأنظمة التي لا تحتوي على مصادقة هوية أو تسجيل دخول مستخدم.
- ACL بدون موارد: في بعض السيناريوهات قد يستهدف فقط نوع المورد، وليس موردًا فرديًا، مثل صلاحيات
write-articleوread-log. ولا يتحكم في الوصول إلى مقال أو سجل محدد. - RBAC (التحكم في الوصول المبني على الأدوار)
- RBAC مع أدوار الموارد: يمكن للمستخدمين والموارد أن يكون لديهم أدوار (أو مجموعات) في نفس الوقت.
- RBAC مع النطاقات/المستأجرين: يمكن للمستخدمين تعيين مجموعات أدوار مختلفة لنطاقات/مستأجرين مختلفين.
- ABAC (التحكم في الوصول المبني على الخصائص): يدعم استخدام السكر النحوي مثل
resource.Ownerللحصول على خصائص العناصر. - RESTful: يدعم المسارات مثل
/res/*و/res/: idوطرق HTTP مثلGETوPOSTوPUTوDELETE. - الأولوية للرفض: يدعم الترخيص بالسماح والرفض، والرفض له الأولوية على السماح.
- الأولوية: تُحدد أولوية قواعد السياسة بالترتيب الزمني، مثل قواعد جدار الحماية.
مبدأ العمل
في Casbin، يتم تجريد نموذج التحكم في الوصول كملف تكوين مبني على PERM، حيث يشير PERM إلى Policy (السياسة) و Effect (التأثير) و Request (الطلب) و Matcher (المطابقة)، وعند تعديل آلية الترخيص في المشروع، يكفي تعديل ملف التكوين ببساطة. محتوى ملف تكوين Model الطبيعي كالتالي:
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.actهذا أبسط نموذج تحكم في الوصول ACL.
السياسة
في ملف التكوين، جزء تعريف السياسة هو
[policy_definition]
p = sub, obj, actp تعني policy، ولا يمكن استبدالها بأحرف أخرى، و sub تعني subject، وهو موضوع السياسة، و obj تعني object، وهو كائن السياسة، و act تعني action، وهو الإجراء.
p = sub, obj, actيمكن أن يكون هناك حقل رابع eft، وإذا حُذف فالافتراض هو أن eft يساوي allow.
p=sub, obj, act, eftهذا السطر من التعريف يصف فقط كيفية كتابة السياسة، وليس تعريف سياسة محدد. فيما يلي مثال على سياسة محددة
p, jojo, cake, eatp تعني أن هذا تعريف قاعدة سياسة، و jojo هو موضوع السياسة، و cake هو كائن السياسة، و eat هو الإجراء، والمعنى الكامل هو أن الموضوع jojo يمكنه تنفيذ الإجراء eat على الكائن cake. قواعد السياسة المحددة لن تظهر في ملف النموذج، بل سيكون هناك ملف policy مخصص أو قاعدة بيانات لتخزين السياسات.
الطلب
في ملف التكوين، جزء تعريف الطلب هو
[request_definition]
r = sub, obj, actr تعني request، ولا يمكن استبدالها بأحرف أخرى، و sub تعني subject، وهو موضوع الطلب، و obj تعني object، وهو كائن الطلب، و act تعني action، وهو إجراء الطلب. في الحالات العادية تكون أسماء حقول تعريف الطلب متطابقة مع أسماء حقول تعريف السياسة. جزء الطلب ليس مسؤولية casbin، بل يقرر المطور نفسه ما هو موضوع الطلب وما هو كائن الطلب، ويحتاج casbin فقط إلى المسؤولية عن التحكم في الوصول بناءً على الحقول المُمررة.
المطابقة
في ملف التكوين، جزء تعريف المطابقة هو
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.actm تعني matcher، ولا يمكن استبدالها بأحرف أخرى، وما يليها هو قواعد المطابقة المقابلة، وما سبق هو تعبير بولياني بسيط، ومعناه أن جميع حقول الطلب المُمررة تطابق تمامًا حقول قاعدة السياسة، وبالطبع يمكن أن يكون أيضًا محارف بدل أو تعبيرًا عاديًا أقوى تعبيرًا.
بالإضافة إلى ذلك، يدعم matcher صيغة in، على سبيل المثال
[matchers]
m = r.sub in ("root","admin")يمكن أيضًا أن يكون
[matchers]
m = r.sub.Name in (r.obj.Admins)e.Enforce(Sub{Name: "alice"}, Obj{Name: "a book", Admins: []interface{}{"alice", "bob"}})عند إجراء المطابقة، لا يقوم Casbin بإجراء فحص النوع، بل يعاملها كـ interface ويجري فحص == لمعرفة إذا كانت متساوية.
التأثير
جزء تعريف التأثير يجري حكمًا تركيبيًا منطقيًا مرة أخرى على نتيجة المطابقة، في ملف التكوين، جزء تعريف التأثير هو
[policy_effect]
e = some(where (p.eft == allow))e تعني effect، ولا يمكن استبدالها بأحرف أخرى. محدد الكمية some يحكم إذا كانت هناك قاعدة سياسة واحدة تُرضي المطابق. محدد الكمية any يحكم إذا كانت جميع قواعد السياسة تُرضي المطابق.
some(where (p.eft == allow))هذه القاعدة تعني أنه إذا كانت هناك نتيجة allow واحدة في نتائج المطابقة، فإن النتيجة النهائية تكون allow.
e = !some(where (p.eft == deny))هذه القاعدة تعني أنه طالما لا توجد نتيجة deny في نتائج المطابقة، فإن النتيجة النهائية تكون allow.
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))هذه القاعدة تعني أنه في نتائج المطابقة، إذا كانت هناك نتيجة allow واحدة ولا توجد نتيجة deny، فإن النتيجة النهائية تكون allow.
رغم أن Casbin صمم صيغة تأثيرات السياسة أعلاه، إلا أن التنفيذ الحالي يستخدم فقط تأثيرات السياسة المُشفرة. يعتبرون أن هذه المرونة ليست ضرورية جدًا. حتى الآن يجب عليك استخدام تأثيرات السياسة المدمجة، ولا يمكنك تخصيصها، وتأثيرات السياسة المدمجة المدعومة كالتالي.
| تعريف Policy effect | المعنى | مثال |
|---|---|---|
| some(where (p.eft == allow)) | allow-override | ACL, RBAC, etc. |
| !some(where (p.eft == deny)) | deny-override | رفض الكتابة فوق |
| some(where (p.eft == allow)) && !some(where (p.eft == deny)) | allow-and-deny | السماح والرفض |
| priority(p.eft) || deny | priority | الأولوية |
| subjectPriority(p.eft) | الأولوية المبنية على الدور | أولوية الموضوع |
TIP
التعريفات الأربعة أعلاه يمكن تعريف عدة منها، الصيغة هي
type+number، مثلr2وp2وe2وm2.ملف النموذج يمكن أن يحتوي على تعليقات، باستخدام الرمز
#للتعليق.
مثال
فيما يلي مثال يوضح عملية عمل ملف النموذج. أولًا نُعرِّف ملف نموذج ACL بسيط كالتالي
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.actملف Policy كالتالي
p, alice, data1, read
p, bob, data2, writeكيفية استخلاص الموضوع والكائن والإجراء يُحدده منطق الأعمال، وهذا ليس مهمًا لذا حُذف. فيما يلي عرض للطلبات المُمررة بأبسط طريقة، كالتالي
alice, data1, read
bob, data1, read
alice, data2, write
bob, data2, writeفي ملف policy تم تعريف أن alice لديها صلاحية إجراء عملية read على data1، و bob لديه صلاحية إجراء عملية write على data2، ففي الطلبات المُمررة
alice, data1, readتعني أن alice تريد إجراء عملية read على data1،
bob, data1, readتعني أن bob يريد إجراء عملية read على data1، وباقيها مماثل. فالنتيجة النهائية هي
true
false
false
trueهذا أبسط مثال ACL، ويمكنك تحرير واختبار الأمثلة عبر الإنترنت في موقع Casbin الرسمي، انتقل إلى Casbin editor للتجربة.
RBAC
RBAC (Role-Based-Access-Controll)، التحكم في الوصول المبني على الأدوار، مقارنةً بنموذج ACL سيكون هناك [role definition] إضافي، فيما يلي نموذج RBAC بسيط
[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حيث تعريف الدور كالتالي
[role_definition]
g = _, _g تعني group، ولا يمكن استبدالها بأحرف أخرى، وتدعم إنشاء عدة باستخدام طريقة type+number، و _ هو عنصر نائب، يُشير إلى عدد المعاملات. بشكل عام، في Policy، g عادةً تكون بالتنسيق التالي
g, alice, data2_admin
g, mike, data1_admin
g, data1_admin data2_adminalice تُشير إلى الموضوع، و data2_admin تُشير إلى الدور، وبشكل صارم يتعامل casbin معهما كسلاسل نصية، كيفية فهم معناهما واستخدامهما تعتمد على المطور.
g, alice, data2_adminتعني أن alice لديها دور data2_admin
g, mike, data1_adminتعني أن mike لديه دور data1_admin
g, data1_admin data2_adminتعني أن دور data1_admin لديه دور data2_admin، وهذه علاقة وراثة بين الأدوار.
نموذج أدوار الموارد
يضيف نموذج أدوار الموارد g2 جديد، كتعريف لدور الموارد، وتعريف النموذج كالتالي
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
g2 = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && g2(r.obj, p.obj) && r.act == p.actمثال تعريف Policy كالتالي
p, alice, data1, read
p, bob, data2, write
p, data_group_admin, data_group, write
g, alice, data_group_admin
g2, data1, data_group
g2, data2, data_groupحيث عرَّف g2 مجموعة أدوار الموارد، وأسند الموارد إلى أدوار مختلفة، وفي نفس الوقت حدد علاقة المستخدمين بين أدوار المستخدمين وأدوار الموارد.
p, data_group_admin, data_group, writeهذه السياسة تُعرِّف أن المستخدم الذي لديه دور data_group_admin يمكنه إجراء عملية كتابة على الموارد التي لديها دور data_group.
نموذج النطاق متعدد المستأجرين
[request_definition]
r = sub, dom, obj, act
[policy_definition]
p = sub, dom, obj, act
[role_definition]
g = _, _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.actيضيف نموذج النطاق متعدد المستأجرين حقل dom إضافي مقارنةً بنموذج RBAC التقليدي، ليدل على النطاق الذي ينتمي إليه الموضوع. مثال Policy كالتالي
p, admin, domain1, data1, read
p, admin, domain1, data1, write
p, admin, domain2, data2, read
p, admin, domain2, data2, write
g, alice, admin, domain1
g, bob, admin, domain2على سبيل المثال
p, admin, domain1, data1, readتُعرِّف أن الموضوع admin الذي ينتمي إلى النطاق domain1 لديه صلاحية إجراء عملية read على data1
g, alice, admin, domain1تُعرِّف أن alice تنتمي إلى domain1 ولديها دور admin
