Casbin
ที่เก็บข้อมูลอย่างเป็นทางการ: casbin/casbin: An authorization library that supports access control models like ACL, RBAC, ABAC in Golang (github.com)
เอกสารอย่างเป็นทางการ: ภาพรวม | Casbin
TIP
บทความนี้เป็นเพียงบทความเบื้องต้นเกี่ยวกับ Casbin หากต้องการเรียนรู้เพิ่มเติมอย่างละเอียดโปรดไปที่เว็บไซต์ทางการ
บทนำ
ในระบบ โปรแกรมเมอร์ Backend ต้องรับผิดชอบการจัดการสิทธิ์ของ API ซึ่งต้องใช้การทำงานจำนวนมาก หากแต่ละโปรเจกต์ต้องเขียนระบบควบคุมสิทธิ์เองจะเสียเวลาเป็นอย่างมาก บริษัทใหญ่ที่มีทรัพยากรมากกว่ามักจะ傾向ที่จะพัฒนาระบบควบคุมสิทธิ์ของตัวเอง แต่บริษัทขนาดเล็กและกลางส่วนใหญ่ไม่สามารถรับต้นทุนการพัฒนาได้ ดังนั้นเฟรมเวิร์กควบคุมสิทธิ์แบบ Open Source ในตลาดจึงเป็นตัวเลือกแรกของพวกเขา Casbin เป็นไลบรารีควบคุมการเข้าถึงแบบ Open Source ที่มีประสิทธิภาพสูง พัฒนาด้วยภาษา Go และรองรับภาษาหลักอื่นๆ ด้วย
ควรทราบว่า Casbin เป็นเพียงเฟรมเวิร์กควบคุมการเข้าถึงเท่านั้น รับผิดชอบเฉพาะการควบคุมการเข้าถึง ไม่รับผิดชอบตรรกะการตรวจสอบสิทธิ์การเข้าถึง เก็บเฉพาะความสัมพันธ์การแมประหว่างผู้ใช้และบทบาท รองรับแบบจำลองการควบคุมการเข้าถึงต่อไปนี้:
- ACL (Access Control List, รายการควบคุมการเข้าถึง)
- ACL ที่มี Superuser
- ACL ที่ไม่มีผู้ใช้: มีประโยชน์เป็นพิเศษสำหรับระบบที่ไม่มีการตรวจสอบสิทธิ์หรือการเข้าสู่ระบบของผู้ใช้
- ACL ที่ไม่มีทรัพยากร: บางสถานการณ์อาจมุ่งเน้นเฉพาะประเภทของทรัพยากร ไม่ใช่ทรัพยากรเดี่ยว เช่น สิทธิ์
write-article,read-logเป็นต้น ไม่ควบคุมการเข้าถึงบทความหรือบันทึกเฉพาะ - RBAC (Role-Based Access Control, การควบคุมการเข้าถึงตามบทบาท)
- RBAC ที่รองรับบทบาททรัพยากร: ผู้ใช้และทรัพยากรสามารถมีบทบาท (หรือกลุ่ม) ได้พร้อมกัน
- RBAC ที่รองรับโดเมน/ผู้เช่า: ผู้ใช้สามารถตั้งค่าชุดบทบาทที่แตกต่างกันสำหรับโดเมน/ผู้เช่าที่แตกต่างกัน
- ABAC (Attribute-Based Access Control, การควบคุมการเข้าถึงตามคุณลักษณะ): รองรับการใช้ syntax sugar เช่น
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บรรทัดนี้เพียงอธิบายว่าจะเขียน policy อย่างไร ไม่ใช่การกำหนดนโยบายที่แท้จริง ด้านล่างนี้เป็นตัวอย่าง policy ที่เฉพาะเจาะจง
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 ไม่สามารถใช้ตัวอักษรอื่นแทนได้ หลังจากนั้นคือกฎการจับคู่ที่สอดคล้องกัน ด้านบนนี้เป็นนิพจน์บูลีนอย่างง่าย หมายความว่าหากฟิลด์ทั้งหมดของคำขอที่ส่งเข้ามาตรงกับฟิลด์ของกฎนโยบายก็จะจับคู่ได้ แน่นอนว่ามันอาจเป็น wildcard หรือนิพจน์ปกติที่มีพลังการแสดงออกมากกว่า
นอกจากนี้ matcher ยังรองรับ syntax 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 จะออกแบบ syntax สำหรับผลลัพธ์นโยบายดังกล่าว แต่การดำเนินการในปัจจุบันใช้ผลลัพธ์นโยบายที่ hardcode ไว้ พวกเขาไม่คิดว่าความยืดหยุ่นนี้จำเป็นมากนัก จนถึงขณะนี้คุณต้องใช้ผลลัพธ์นโยบายที่มีอยู่ในตัว ไม่สามารถกำหนดเองได้ ผลลัพธ์นโยบายที่รองรับมีดังนี้
| นิยาม Policy effect | ความหมาย | ตัวอย่าง |
|---|---|---|
| some(where (p.eft == allow)) | allow-override | ACL, RBAC, ฯลฯ |
| !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
นิยามทั้งสี่ข้างต้นสามารถกำหนดได้หลายรายการ syntax คือ
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 มีสิทธิ์อ่าน data1, bob มีสิทธิ์เขียน data2 ดังนั้นในคำขอที่ส่งเข้ามา
alice, data1, readหมายความว่า alice ต้องการดำเนินการอ่านกับ data1
bob, data1, readหมายความว่า bob ต้องการดำเนินการอ่านกับ 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_groupg2 นิยามกลุ่มบทบาททรัพยากร มอบทรัพยากรให้กับบทบาทต่างๆ และกำหนดความสัมพันธ์ระหว่างบทบาทผู้ใช้กับบทบาททรัพยากร
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 มีสิทธิ์อ่าน data1
g, alice, admin, domain1กำหนดว่า alice สังกัด domain1 มีบทบาท admin
