Skip to content

PolicyDefinition cookbook — lưu grant theo Role / Merchant / direct Permission

Mẫu dữ liệu PolicyDefinition cụ thể cho mọi trường hợp (không chỉ happy case), kèm casbin line adapter sinh ra và kết quả enforce. Mô hình runtime xem Casbin Authorization.

Enforcer đọc gì

Các cột quan trọng: variant, subject_type, subject_id, target_type, target_id, domain, action, effect, deleted_at.

#Cạnhvariantsubject → targetÝ nghĩa domain
1User → RolegroupUserRolescope role theo merchant
2User → MerchantgroupUserMerchant(membership; không dùng domain)
3Role → PermissionpolicyRolePermissionbỏ qua (luôn emit *)
4User → PermissionpolicyUserPermissionscope direct-grant theo merchant

domain chứa merchant-id thô, NULL, hoặc * (adapter bọc merchant-id thành Merchant_<id>, giữ * raw). Cạnh Role → Merchant / Role → Organizer không được đọc — đừng dùng để scope.

Fixtures dùng bên dưới

Id tượng trưng (thay bằng UUID/snowflake thật):

TokenNghĩa
Umột user id
MA, MB, MCmerchant id
R_OWNER, R_EMP, R_GUESTrole id (500_organizer-owner, 100_employee, 001_guest)
P_FIND, P_DELETEpermission id (Product.find read / Product.deleteById delete)

action của grant phải bằng action của chính permission (Product.findread, Product.deleteByIddelete). effect mặc định allow.


Case 1 — Cấp permission cho role (quản theo Role)

Mục tiêu: role R_OWNER được Product.find. Domain không liên quan với role grant.

variantsubject_typesubject_idtarget_typetarget_iddomainactioneffect
policyRoleR_OWNERPermissionP_FINDNULLreadallow

Emit ra (chỉ khi có user thực sự giữ R_OWNER ở ≥1 domain):

p, Role_R_OWNER, *, Product.find, read, allow

1 row / (role, permission). Thu hồi = soft-delete row này.


Case 2 — Cho user là thành viên của merchant

Mục tiêu: U thuộc merchant MA. Cần cho NULL-domain projection (Case 4) và cho data-scoping.

variantsubject_typesubject_idtarget_typetarget_iddomainactioneffect
groupUserUMerchantMANULLNULLNULL

merchantIds = [MA]. Tự thân không emit line; kết hợp với Case 4.


Case 3 — Gán role cho user, scope MỘT merchant (quản theo Merchant)

Mục tiêu: U là owner chỉMA.

variantsubject_typesubject_idtarget_typetarget_iddomainactioneffect
groupUserURoleR_OWNERMANULLNULL

Emit (giả sử có grant Case 1):

g, User_U, Role_R_OWNER, Merchant_MA
p, Role_R_OWNER, *, Product.find, read, allow
  • enforce (User_U, Merchant_MA, Product.find, read)ALLOW
  • enforce (User_U, Merchant_MB, Product.find, read)DENY (không có g-line ở MB)

Case 4 — Gán role cho TẤT CẢ merchant của user

Mục tiêu: U là owner ở mọi merchant mình thuộc về.

Rows: membership (Case 2) cho từng merchant + MỘT role assignment domain NULL:

variantsubject_typesubject_idtarget_typetarget_iddomain
groupUserUMerchantMANULL
groupUserUMerchantMBNULL
groupUserURoleR_OWNERNULL

Emit:

g, User_U, Role_R_OWNER, Merchant_MA
g, User_U, Role_R_OWNER, Merchant_MB
p, Role_R_OWNER, *, Product.find, read, allow

ALLOW ở MA & MB; DENY ở MC. (NULL domain của role = "chiếu lên các membership của user".)


Case 5 — Role global (guest), pre-merchant

Mục tiêu: U onboard được khi chưa có merchant. Guest là role global (GLOBAL_ROLE_IDENTIFIERS) → adapter ép domain * bất kể domain của assignment (NULL OK).

variantsubject_typesubject_idtarget_typetarget_iddomain
groupUserURoleR_GUESTNULL
policyRoleR_GUESTPermission(Organizer.onBoarding)NULL

Emit:

g, User_U, Role_R_GUEST, *
p, Role_R_GUEST, *, Organizer.onBoarding, create, allow

ALLOW Organizer.onBoarding ở MỌI domain — kể cả placeholder pre-merchant Merchant_00000000-0000-0000-0000-000000000000.


Case 6 — Direct permission cho user (bỏ qua role)

6a. Scope một merchant:

variantsubject_typesubject_idtarget_typetarget_iddomainactioneffect
policyUserUPermissionP_FINDMAreadallow
p, User_U, Merchant_MA, Product.find, read, allow — ALLOW chỉ ở MA (khớp qua reflexive
g(User_U, User_U, dom) + keyMatch exact).

6b. Mọi merchant của user: domain = NULL + membership (Case 2) → mỗi membership một p, User_U, Merchant_<m>, ….

6c. Direct global: domain = *p, User_U, *, Product.find, read, allow — ALLOW mọi domain (dùng dè dặt).


Case 7 — DENY tường minh (override allow)

Mục tiêu:R_OWNER cho phép Product.deleteById, chặn nó với user U.

variantsubject_typesubject_idtarget_typetarget_iddomainactioneffect
policyUserUPermissionP_DELETEMAdeletedeny
p, User_U, Merchant_MA, Product.deleteById, delete, deny. Policy effect
some(allow) && !some(deny) → bất kỳ deny khớp đều thắng → DENY ở MA.

Pitfalls & các case không-happy

Tình huốngRow(s)Kết quả
Role domain NULL, KHÔNG membershiprow Case 4 nhưng thiếu Case 2role ra 0 domain → không g-line → vô hiệu. Lỗi hay gặp.
* trên role thường (owner/employee)group User→Role domain=*role khớp mọi merchant toàn hệ thống → vỡ isolation. Tuyệt đối không.
domain trên row Role→Permissionpolicy Role→Permission domain=MAdomain bị bỏ qua — emit *. KHÔNG scope. Để NULL.
Cạnh Role→Merchant / Role→Organizergroup Role→Merchantkhông được đọc → không tác dụng.
Direct perm domain NULL, không membershipCase 6 domain NULL, thiếu Case 2direct grant chiếu vào rỗng → vô hiệu.
Lệch actiongrant action=read cho permission *.deleteById (delete)matcher cần r.act == p.act → không bao giờ khớp. Đặt action = action của permission.
Grant bị soft-deleteset deleted_atbị loại (deletedAt IS NULL / INNER JOIN).
Role/Permission bị soft-deleterow trỏ tới role/perm đã xoáINNER JOIN loại cạnh → grant biến mất.
Row trùng (cùng domain)hai assignment giống hệtdedup in-memory → vô hại.
super-admin / admin / operator(không cần)always-allow bypass — mọi PolicyDefinition của họ bị bỏ qua khi enforce.
customer(không)không có quyền backend.

INSERT nhanh (psql) ví dụ

sql
-- Case 1: cấp Product.find cho role owner
INSERT INTO identity."PolicyDefinition"
  (id, variant, subject_type, subject_id, target_type, target_id, action, effect, domain)
VALUES (gen_random_uuid()::text, 'policy', 'Role', '<R_OWNER>', 'Permission', '<P_FIND>', 'read', 'allow', NULL);

-- Case 3: gán owner cho user U scope merchant MA (+ membership)
INSERT INTO identity."PolicyDefinition"
  (id, variant, subject_type, subject_id, target_type, target_id, domain)
VALUES
  (gen_random_uuid()::text, 'group', 'User', '<U>', 'Merchant', '<MA>', NULL),
  (gen_random_uuid()::text, 'group', 'User', '<U>', 'Role', '<R_OWNER>', '<MA>');

Trong luồng app nên dùng service/API policy-definition thay vì SQL thô — các INSERT này để hiểu/debug.

Xem thêm

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.