URD: Phân quyền
| Module | Phiên bản | Cập nhật |
|---|---|---|
CORE-02 | v0.8 | 2026-07-15 |
Module Phân quyền làm gì
Xác định cách kiểm soát truy cập vào tính năng và dữ liệu. Phân quyền cho quản trị viên gán vai trò cho người dùng, cấp quyền cho các vai trò đó, giới hạn vai trò trong phạm vi một organizer hoặc merchant cụ thể, và làm cho mọi truy vấn được lọc tự động theo đúng phạm vi mà người dùng yêu cầu được phép xem.
Toàn bộ chạy trên một mô hình RBAC dựa trên độ ưu tiên của Casbin, có phạm vi (domain) theo từng merchant.
Sáu ý cần hiểu trước
Đây là phần quan trọng nhất của tài liệu. Hiểu sáu ý dưới đây là hiểu 80% module.
Xuyên suốt phần này ta dùng một ví dụ duy nhất. Chị Hoa là Owner của organizer Cà phê Sáu Béo, có 3 merchant: Sáu Béo Quận 1 (trụ sở), Sáu Béo Thủ Đức, Sáu Béo Gò Vấp. Anh Tuấn là thu ngân ở Quận 1. Chị Mai là nhân viên phục vụ ở Thủ Đức.
1. Quyền được cấp theo cụm, không theo từng nút bấm
Cách nghĩ tự nhiên là: có bao nhiêu nút thì cấp bấy nhiêu quyền. Hệ thống không làm vậy. Toàn hệ có khoảng 755 thao tác có thể cấp quyền, chia thành 14 nhóm nghiệp vụ: Bán hàng, Kho, Sản phẩm, Thanh toán, Tài chính, Bảng giá, Hóa đơn, Thuế, Sổ công nợ, Giấy phép, Tài khoản, Chăm sóc khách, Cảnh báo, Hỗ trợ. Nếu cấp lẻ từng thao tác thì vai trò Owner sẽ phải mang 755 dòng quyền.
Thay vào đó hệ thống cấp một dòng cho cả một cụm, rồi tự nở ra. Nở theo hai chiều.
Chiều thứ nhất là cây tài nguyên: nhóm nghiệp vụ chứa các đối tượng, đối tượng chứa các thao tác. Cấp trên nhóm Bán hàng là tự động phủ Đơn hàng, Dòng hàng trong đơn, Ca làm việc, và mọi thao tác bên dưới chúng.
Chiều thứ hai là thang hành động: một mức cao thỏa mãn mọi mức thấp hơn nó.
| Mức được cấp | Bao gồm những gì |
|---|---|
| Toàn quyền (manage) | Đọc + Ghi + Thực thi. Cao nhất, phủ hết. |
| Ghi (write) | Tạo, Sửa, Xóa |
| Đọc (read) | Chỉ xem, không đổi được gì |
| Thực thi (execute) | Chạy một thao tác nghiệp vụ (chốt ca, phát hành hóa đơn) mà không phải là ghi dữ liệu thường |
Ghép hai chiều lại: vai trò Owner của chị Hoa chỉ mang đúng 14 dòng quyền - mỗi nhóm nghiệp vụ một dòng ở mức Toàn quyền. Vai trò Thu ngân của anh Tuấn mang ít hơn nhiều:
| Nhóm nghiệp vụ | Mức của Thu ngân | Nghĩa là |
|---|---|---|
| Bán hàng | Toàn quyền | Mở đơn, sửa đơn, chốt đơn, chốt ca |
| Thanh toán | Ghi | Nhận tiền, ghi giao dịch |
| Kho | Đọc | Xem còn bao nhiêu hàng, không được nhập/xuất |
| Sản phẩm | Đọc | Xem danh mục món, không sửa được giá |
| Hóa đơn | Đọc | Xem trạng thái hóa đơn của đơn vừa bán |
Anh Tuấn không có dòng nào cho Bảng giá, Thuế, Sổ công nợ, Tài khoản. Không có dòng nghĩa là không có quyền. Không cần viết ra một dòng "cấm".
2. Quyền hiệu lực gộp từ nhiều nguồn, và một lệnh cấm luôn thắng một lệnh cho phép
Quyền của một người đến từ hai nguồn, cộng lại:
- Quyền theo vai trò - anh Tuấn là Thu ngân, nên anh có mọi thứ vai trò Thu ngân có.
- Quyền cấp thẳng cho cá nhân - chị Hoa cấp riêng cho anh Tuấn quyền Đọc trên Bảng giá, dù vai trò Thu ngân không có.
Hai nguồn gộp lại rồi khử trùng: một quyền có ở cả hai nguồn thì chỉ tính một lần. Khi màn hình hỏi "anh Tuấn thực sự được làm gì", hệ thống trả về tập đã gộp này. Có thể hỏi tách riêng "chỉ phần cấp thẳng" hoặc "chỉ phần thừa hưởng từ vai trò" khi cần soi ai đã cấp gì cho ai.
Nhưng gộp không phải là cộng dồn thuần túy. Một dòng quyền không chỉ mang mức, nó còn mang hiệu lực: cho phép hoặc cấm. Và luật là: cấm thắng cho phép, bất kể dòng nào rộng hơn.
Đây là chỗ phản trực giác nhất của cả module. Nó tồn tại để khoét một lỗ nhỏ trong một quyền lớn, thay vì phải bỏ quyền lớn đi rồi liệt kê lại từng thao tác nhỏ.
Ba ví dụ đang chạy thật trong hệ thống:
| Ai | Dòng cho phép (rộng) | Dòng cấm (hẹp) | Kết quả thật |
|---|---|---|---|
| Chị Mai (Nhân viên) | Ghi trên cả nhóm Bán hàng | Cấm Ghi trên Ca làm việc | Mai mở đơn, sửa đơn được, nhưng không mở/chốt được ca - chỉ xem ca |
| Anh Tuấn (Thu ngân) | Toàn quyền trên nhóm Bán hàng | Cấm Toàn quyền trên Điều chỉnh ca | Tuấn chốt ca bình thường, nhưng không sửa được số liệu ca đã chốt - chỉ Owner sửa |
| Chị Hoa (Owner) | Toàn quyền trên nhóm Tài khoản | Cấm Ghi trên Danh mục quyền gốc | Hoa xem được danh sách quyền để đi cấp cho nhân viên, nhưng không tự chế ra quyền mới - đó là việc của hệ thống |
Chú ý ví dụ cuối: ngay cả Owner - người cao nhất trong một organizer - vẫn bị cấm ở vài chỗ. "Owner có toàn quyền" là một câu nói sai.
Ngoại lệ duy nhất: ba vai trò nội bộ Super Admin, Admin, Operator của đội vận hành không đi qua bộ máy này. Họ được cho qua thẳng, không xét quyền, không xét cấm, không bị lọc dữ liệu. Vì thế họ cũng không cần một dòng quyền nào cả.
3. Bậc ưu tiên: không ai đụng được vai trò ngang hoặc trên mình
Mỗi vai trò mang một con số ưu tiên. Con số này là cái cầu thang của cả hệ.
| Vai trò | Ưu tiên |
|---|---|
| Super Admin | 999 |
| Admin | 900 |
| Operator | 600 |
| Owner | 500 |
| Cashier (Thu ngân) | 110 |
| Employee (Nhân viên) | 100 |
| Customer (Khách) | 10 |
| Guest (Khách vãng lai) | 1 |
Vai trò tự tạo chỉ được nằm trong khoảng 100 tới 499 - tức luôn thấp hơn Owner, luôn cao hơn hoặc bằng Nhân viên. Không ai tạo được một vai trò tự chế ngang hàng Owner.
Luật chặn có một câu: muốn tạo, sửa, xóa, hay cấp phát một vai trò thì số ưu tiên của vai trò đó phải THẤP HƠN HẲN số ưu tiên cao nhất của chính mình. Ngang bằng cũng không được.
Chị Hoa (Owner, 500) muốn tạo vai trò "Quản lý merchant" ưu tiên 300 cho quản lý ở Thủ Đức: được. Chị đặt ưu tiên 500 cho vai trò đó: bị chặn. Chị muốn sửa vai trò Operator (600) của đội vận hành: bị chặn. Chị muốn gán vai trò Owner cho người khác trong organizer của mình: cũng bị chặn, vì Owner ngang chị.
Một chi tiết dễ hụt: hệ thống lấy số cao nhất trong các vai trò của người đang thao tác. Nếu một người vừa là Nhân viên (100) vừa là Quản lý merchant (300) thì trần của họ là 300, không phải 100.
Bậc ưu tiên còn quyết định nhìn thấy gì: khi chị Hoa mở màn chọn vai trò để gán cho nhân viên, danh sách chỉ hiện các vai trò dưới 500. Vai trò Operator, Admin, Super Admin thậm chí không xuất hiện trong danh sách.
4. Trần cấp phát: không cho được thứ mình không có
Bậc ưu tiên mới chỉ trả lời "được đụng vào vai trò nào". Nó chưa trả lời "được nhét quyền gì vào vai trò đó". Đây là hai khóa khác nhau và cả hai đều phải mở.
Luật thứ hai: mức bạn cấp cho người khác không được cao hơn mức bạn đang có trên chính tài nguyên đó.
| Mức mình đang có | Mức mình được phép cấp cho người khác |
|---|---|
| Toàn quyền | Đọc, Ghi, Thực thi, Toàn quyền (tất cả) |
| Ghi | Chỉ Ghi |
| Đọc | Chỉ Đọc |
| Thực thi | Chỉ Thực thi |
Quay lại Sáu Béo. Chị Hoa có Toàn quyền trên nhóm Kho, nên chị cấp mức nào trên Kho cho vai trò "Quản lý merchant" cũng được.
Nhưng giả sử chị Hoa cấp cho vai trò "Quản lý merchant" mức Đọc trên Kho. Anh quản lý Thủ Đức mang vai trò đó, và anh muốn tạo tiếp một vai trò "Phụ kho" (ưu tiên 200 - thấp hơn 300 của anh, nên qua được khóa ưu tiên) rồi cấp cho nó mức Ghi trên Kho. Bị chặn. Anh chỉ có Đọc, anh chỉ được phát ra Đọc. Anh không thể tự nâng cấp mình bằng cách tạo một vai trò mạnh hơn mình rồi tự gán vào.
Khóa này chỉ đọc các quyền anh có qua vai trò. Quyền cấp thẳng cho cá nhân không làm trần của anh cao lên - đó là lựa chọn cố ý, thà chặn nhầm còn hơn mở nhầm. Và ba vai trò nội bộ của đội vận hành thì bỏ qua khóa này, như mọi thứ khác.
5. Đứng ở merchant nào thì chỉ thấy merchant đó
Đây là ý hay bị hiểu sai nhất, và hiểu sai thì lộ dữ liệu.
Chị Hoa sở hữu cả 3 merchant. Trực giác nói: chị mở màn Đơn hàng ra thì phải thấy đơn của cả 3. Không phải.
Mỗi request lên hệ thống đều mang theo merchant đang đứng - đúng một merchant, do màn hình đang chọn. Bộ lọc dữ liệu chỉ lọc theo đúng merchant đó. Chị Hoa đang đứng ở Quận 1 thì màn Đơn hàng chỉ ra đơn của Quận 1. Muốn xem Thủ Đức thì phải đổi merchant đang xem trên thanh chọn, rồi màn hình nạp lại.
Quyền của chị và merchant chị đang đứng là hai thứ tách rời:
| Câu hỏi | Trả lời bằng |
|---|---|
| Chị Hoa được làm gì? | Các dòng quyền của vai trò Owner (điểm 1) |
| Chị Hoa được làm ở đâu? | Danh sách merchant chị là thành viên |
| Lần bấm này chị đang làm ở merchant nào? | Merchant đang đứng, đi kèm từng request |
Các dòng quyền của Owner, Thu ngân, Nhân viên không ghim vào một merchant nào cả. Chúng có hiệu lực ở bất kỳ merchant nào mà người đó là thành viên. Nghĩa là ranh giới thật không nằm ở dòng quyền - nó nằm ở danh sách thành viên.
Nên: anh Tuấn là thành viên của Quận 1, không phải thành viên của Gò Vấp. Vai trò Thu ngân của anh cho anh mở đơn - nhưng chỉ ở Quận 1. Nếu anh đổi merchant đang đứng sang Gò Vấp, quyền Thu ngân của anh không theo sang, vì anh không phải thành viên ở đó.
Một điểm nhỏ nhưng quan trọng khi test: khi ai đó gọi thẳng một bản ghi ngoài phạm vi bằng mã của nó, hệ thống trả "không tìm thấy", chứ không trả "không có quyền". Cố ý như vậy - trả lời "không có quyền" là đã vô tình xác nhận bản ghi đó có tồn tại.
Có một cửa nằm ngoài luật này: các quyền dành cho khách vãng lai (người chưa đăng nhập, chưa có merchant nào) được đánh dấu toàn hệ. Nhờ vậy màn đăng ký organizer mới, tra cứu gói dịch vụ, tra mã số thuế vẫn chạy được ở thời điểm chưa hề tồn tại một merchant nào để mà đứng vào.
6. Owner lan xuống merchant mới bằng cách chép quyền vào, không phải tự suy ra
Chị Hoa mở merchant thứ 4: Sáu Béo Bình Thạnh. Chị vào quản lý được ngay, không ai phải cấp gì thêm. Câu hỏi là: hệ thống làm điều đó bằng cách nào?
Trực giác nói: hệ thống "hiểu" rằng Bình Thạnh nằm dưới organizer Cà phê Sáu Béo, và Hoa là Owner của organizer, nên suy ra Hoa có quyền ở Bình Thạnh. Không phải vậy. Hệ thống không suy ra gì cả.
Thứ thật sự xảy ra: ngay tại giây phút Bình Thạnh được tạo, hệ thống quét ngược lại, tìm mọi người vừa mang vai trò Owner vừa là thành viên của organizer Cà phê Sáu Béo - ra chị Hoa - rồi ghi thêm một dòng thành viên mới: "Hoa là thành viên của Bình Thạnh". Dòng đó là thật, nằm trong dữ liệu, đếm được.
Hệ quả phải nhớ, vì nó là nguồn của phần lớn lỗi phân quyền khó hiểu:
- Việc chép chỉ chạy một lần, lúc tạo merchant. Ai được phong Owner sau khi các merchant đã tồn tại thì không tự nhiên có mặt ở các merchant cũ - phải thêm thành viên cho từng merchant.
- Muốn cắt quyền của một Owner tại một merchant, phải gỡ đúng dòng thành viên của merchant đó. Gỡ ở organizer không làm các dòng merchant biến mất.
- Số dòng thành viên nở theo số merchant. Một organizer 30 merchant thì mỗi Owner mang 30 dòng.
Ba luật của điểm 3, 4, 5 và cơ chế chép này giải thích được gần như mọi câu "tại sao người này không bấm được nút kia" mà đội hỗ trợ nhận hằng ngày.
Module có những gì
Bảy nhóm feature. Bấm vào mã AREA để sang tab Chi tiết đọc yêu cầu của nhóm đó.
AREA | Tên | Trạng thái |
|---|---|---|
ROLE | Vai trò cố định | Một phần |
CROLE | Vai trò tùy chỉnh | Một phần |
PERM | Danh mục quyền | Đã dựng |
GRANT | Cấp / Thu hồi | Một phần |
EFF | Quyền hiệu lực & Phạm vi | Một phần |
HIER | Phân cấp Tài nguyên, Hành động & Domain | Một phần |
DECL | Khai báo quyền | Một phần |
1. Định nghĩa
| Thuật ngữ | Định nghĩa |
|---|---|
| Vai trò (Role) | Một mức truy cập có tên kèm độ ưu tiên dạng số. SYSTEM (cố định) hoặc CUSTOM (do người dùng tạo). |
| Quyền (Permission) | Một hành động có tên trên một tài nguyên, được định danh bằng một mã duy nhất toàn cục. |
| Cấp quyền (policy) | Một bản ghi cấp một quyền cho một vai trò hoặc trực tiếp cho một người dùng. |
| Thành viên (group) | Một bản ghi liên kết người dùng với vai trò, người dùng với org/merchant, hoặc vai trò với org/merchant. |
| Quyền hiệu lực | Tập hợp toàn bộ quyền mà một người dùng nắm giữ: cấp trực tiếp + quyền kế thừa qua vai trò. |
| Vai trò có phạm vi | Một vai trò tùy chỉnh được gắn vào một organizer hoặc merchant cụ thể, giới hạn nơi nó áp dụng. |
| Domain | Merchant mà một bản ghi cấp quyền áp dụng bên trong, được chọn theo từng request qua header merchant đang hoạt động. |
| Bỏ qua kiểm tra cho vai trò hệ thống | Super Admin, Admin, và Operator bỏ qua mọi việc lọc dữ liệu và nắm giữ mọi quyền. |
| Mở rộng HQ-owner | Một Owner tại merchant trụ sở tự động vươn tới mọi merchant cùng organizer của organizer đó. |
| Leo thang đặc quyền | Quản lý một vai trò ngang hoặc cao hơn độ ưu tiên của chính người thực hiện - luôn bị chặn. |
2. Mô hình Khái niệm
Chỉ mang tính khái niệm - mô hình dữ liệu chính sách đầy đủ nằm trong tài liệu RBAC cho lập trình viên.
3. Feature
ROLE - Vai trò cố định Một phần
Đặc tả: PRD-ROLE-001 (chung cho
ROLEvàCROLE) · 10 yêu cầu · 2 kịch bản nghiệm thu
Tám vai trò hệ thống được seed sẵn ngay lúc khởi động, xếp theo một độ ưu tiên dạng số cố định. Ba vai trò cao nhất - Super Admin, Admin, Operator - thấy toàn bộ dữ liệu, không phân biệt organizer hay merchant. Owner chỉ thấy organizer của chính mình và các merchant của nó; Cashier và Employee chỉ thấy các merchant mà họ được gán vào. Việc lọc này chạy tự động trên mọi thao tác liệt kê và đếm, và không ai ghi đè được bằng bộ lọc tự khai hay bằng cách gọi thẳng một bản ghi ngoài phạm vi bằng ID.
| Mã | Ưu tiên | Yêu cầu | Trạng thái |
|---|---|---|---|
URD-ROLE-001 | Bắt buộc | Cung cấp tám vai trò cố định được seed lúc khởi động: Super Admin, Admin, Operator, Owner, Cashier, Employee, Customer, Guest. | Một phần |
URD-ROLE-002 | Bắt buộc | Vai trò cố định (SYSTEM) không thể chỉnh sửa hoặc xóa. | Đã dựng |
URD-ROLE-003 | Bắt buộc | Mỗi vai trò có một độ ưu tiên dạng số xác định vị trí của nó trong phân cấp. | Đã dựng |
URD-ROLE-004 | Bắt buộc | Super Admin, Admin, và Operator bỏ qua mọi việc lọc dữ liệu và nắm giữ mọi quyền. | Đã dựng |
URD-ROLE-005 | Bắt buộc | Owner chỉ thấy organizer của chính mình và các merchant của nó. | Đã dựng |
URD-ROLE-006 | Bắt buộc | Employee (và Cashier) chỉ thấy các merchant mà họ được gán vào. | Đã dựng |
URD-ROLE-007 | Bắt buộc | Mọi thao tác liệt kê và đếm đều được lọc theo phạm vi của người dùng yêu cầu. | Đã dựng |
URD-ROLE-008 | Bắt buộc | Việc lọc tự động không thể bị ghi đè bằng bộ lọc do người dùng cung cấp hoặc truy cập trực tiếp theo ID. | Đã dựng |
URD-ROLE-009 | Nên có | Một Owner tại merchant trụ sở vươn tới mọi merchant cùng organizer của organizer đó. | Đã dựng |
URD-ROLE-010 | Bắt buộc | Vai trò Owner được tự gán cho người dùng khi họ tạo organizer của mình. | Đã dựng |
AC-ROLE-01: Lọc dữ liệu theo vai trò
| Cho trước | Khi | Thì |
|---|---|---|
| Người dùng có các vai trò khác nhau | Họ gọi cùng một endpoint | Super Admin/Admin/Operator thấy tất cả; Owner thấy org của mình; Employee/Cashier thấy các merchant được gán |
| Bất kỳ truy vấn liệt kê/đếm nào | Được chạy bởi một người dùng có phạm vi | Việc lọc là tự động và không thể bị ghi đè |
| Một người dùng có phạm vi cung cấp bộ lọc rộng hơn hoặc một ID ngoài phạm vi | Khi gửi request | Bộ lọc bị bỏ qua / truy cập bị từ chối |
AC-ROLE-02: Bảo vệ vai trò cố định
| Cho trước | Khi | Thì |
|---|---|---|
| Bất kỳ vai trò nào trong tám vai trò hệ thống | Người dùng cố chỉnh sửa hoặc xóa nó | Thao tác bị từ chối |
| Độ ưu tiên của một vai trò cố định | Người dùng cố thay đổi nó | Bị từ chối - độ ưu tiên là bất biến |
CROLE - Vai trò tùy chỉnh Một phần
Đặc tả: PRD-ROLE-001 (chung cho
ROLEvàCROLE) · 8 yêu cầu · 2 kịch bản nghiệm thu
Quản trị viên và Owner tạo các vai trò riêng với tên tùy chỉnh, độ ưu tiên, và phạm vi organizer/merchant tùy chọn - độ ưu tiên luôn phải thấp hơn độ ưu tiên hiện có của chính người tạo. Định danh vai trò được hệ thống tự sinh từ độ ưu tiên và tên, không ai tự gõ. Xóa một vai trò tùy chỉnh bị chặn khi còn người dùng gán vào nó; xóa thành công thì dọn theo mọi quyền đã cấp và liên kết phạm vi của vai trò đó, trong cùng một lần thao tác.
| Mã | Ưu tiên | Yêu cầu | Trạng thái |
|---|---|---|---|
URD-CROLE-001 | Bắt buộc | Người dùng được ủy quyền có thể tạo vai trò tùy chỉnh với tên i18n, độ ưu tiên, và phạm vi tùy chọn. | Một phần |
URD-CROLE-002 | Bắt buộc | Định danh vai trò được tự động tạo từ độ ưu tiên + tên và là duy nhất trong phạm vi của nó. | Đã dựng |
URD-CROLE-003 | Bắt buộc | Độ ưu tiên của vai trò mới phải thấp hơn nghiêm ngặt so với độ ưu tiên cao nhất của người tạo. | Một phần |
URD-CROLE-004 | Bắt buộc | Vai trò tùy chỉnh có thể được giới hạn phạm vi vào một organizer hoặc merchant cụ thể. | Một phần |
URD-CROLE-005 | Bắt buộc | Vai trò tùy chỉnh có thể được cập nhật (tên, mô tả, độ ưu tiên). | Đã dựng |
URD-CROLE-006 | Bắt buộc | Không thể xóa một vai trò khi vẫn còn người dùng được gán vào nó. | Đã dựng |
URD-CROLE-007 | Bắt buộc | Xóa một vai trò sẽ xóa lan (cascade) các bản ghi cấp quyền và liên kết phạm vi của nó. | Đã dựng |
URD-CROLE-008 | Nên có | Một Owner chỉ có thể tạo các vai trò giới hạn phạm vi trong organizer hoặc các merchant của chính mình. | Đã dựng |
AC-CROLE-01: Tạo vai trò tùy chỉnh
| Cho trước | Khi | Thì |
|---|---|---|
| Admin hoặc Owner | Tạo một vai trò với độ ưu tiên + tên | Vai trò được tạo với type CUSTOM, định danh được tự động tạo |
| Độ ưu tiên ≥ của chính người tạo | Khi cố tạo | Bị từ chối (leo thang đặc quyền) |
| Có cung cấp phạm vi org/merchant | Khi tạo vai trò | Liên kết phạm vi được tạo |
| Cùng độ ưu tiên + tên trong cùng phạm vi | Khi cố tạo | Bị từ chối (trùng định danh) |
AC-CROLE-02: Xóa vai trò
| Cho trước | Khi | Thì |
|---|---|---|
| Vai trò tùy chỉnh không có người dùng | Admin xóa | Vai trò + bản ghi cấp quyền + liên kết phạm vi được soft-delete |
| Vai trò tùy chỉnh có người dùng được gán | Admin xóa | Bị chặn - phải gỡ gán người dùng trước |
| Vai trò hệ thống | Admin xóa | Bị từ chối - vai trò cố định là bất biến |
PERM - Danh mục quyền Đã dựng
Đặc tả: PRD-PERM-001 (chung cho
PERMvàGRANT) · 5 yêu cầu · 1 kịch bản nghiệm thu
Quản trị viên duy trì danh mục quyền: mỗi quyền là một hành động trên một tài nguyên, được mã hóa duy nhất toàn hệ thống, kèm tên và mô tả song ngữ. Một quyền đang có bản ghi cấp đang hoạt động thì không thể bị gỡ bỏ - phải thu hồi các bản ghi cấp trước.
| Mã | Ưu tiên | Yêu cầu | Trạng thái |
|---|---|---|---|
URD-PERM-001 | Bắt buộc | Admin có thể tạo quyền với mã, hành động, phạm vi, và đối tượng (subject) duy nhất. | Đã dựng |
URD-PERM-002 | Bắt buộc | Mã quyền phải duy nhất toàn cục. | Đã dựng |
URD-PERM-003 | Bắt buộc | Tên và mô tả quyền hỗ trợ i18n. | Đã dựng |
URD-PERM-004 | Bắt buộc | Admin có thể cập nhật và xóa quyền. | Đã dựng |
URD-PERM-005 | Bắt buộc | Một quyền có bản ghi cấp đang hoạt động thì không thể bị xóa. | Đã dựng |
AC-PERM-01: Tính toàn vẹn của danh mục quyền
| Cho trước | Khi | Thì |
|---|---|---|
| Admin định nghĩa một quyền | Cung cấp mã, hành động, phạm vi, và đối tượng duy nhất | Quyền được tạo |
| Một mã trùng lặp | Khi cố tạo | Bị từ chối - mã là duy nhất toàn cục |
| Một quyền có bản ghi cấp đang hoạt động | Admin xóa | Bị chặn - phải thu hồi các bản ghi cấp trước |
GRANT - Cấp / Thu hồi Một phần
Đặc tả: PRD-PERM-001 (chung cho
PERMvàGRANT) · 7 yêu cầu · 1 kịch bản nghiệm thu
Quản trị viên gắn hoặc gỡ quyền trên một vai trò, và gán hoặc gỡ gán vai trò cho người dùng. Mọi thao tác đều bất biến (idempotent): cấp lại một quyền đã có bị bỏ qua, không sinh dòng trùng. Bộ bảo vệ chống leo thang đặc quyền chạy trên mọi thao tác cấp/thu hồi, chặn ngay khi thao tác chạm tới một vai trò ngang hoặc cao hơn độ ưu tiên của chính người thực hiện.
| Mã | Ưu tiên | Yêu cầu | Trạng thái |
|---|---|---|---|
URD-GRANT-001 | Bắt buộc | Admin có thể cấp một hoặc nhiều quyền cho một vai trò. | Một phần |
URD-GRANT-002 | Bắt buộc | Admin có thể thu hồi một hoặc nhiều quyền từ một vai trò. | Một phần |
URD-GRANT-003 | Bắt buộc | Cấp một quyền đã được cấp là bất biến (bỏ qua, không trùng lặp). | Một phần |
URD-GRANT-004 | Bắt buộc | Admin có thể cấp và thu hồi vai trò cho/từ người dùng. | Một phần |
URD-GRANT-005 | Bắt buộc | Bộ bảo vệ chống leo thang đặc quyền được thực thi trên mọi thao tác cấp/thu hồi. | Một phần |
URD-GRANT-006 | Bắt buộc | Admin có thể xem mọi quyền đã cấp cho một vai trò. | Một phần |
URD-GRANT-007 | Bắt buộc | Admin có thể xem mọi người dùng được gán vào một vai trò và mọi vai trò được gán cho một người dùng. | Một phần |
AC-GRANT-01: Cấp quyền
| Cho trước | Khi | Thì |
|---|---|---|
| ID vai trò + quyền | Admin cấp | Các bản ghi cấp được tạo, trả về số lượng |
| Quyền đã được cấp | Admin cấp lại | Bỏ qua (idempotent), trả về số lượng bỏ qua |
| Độ ưu tiên người thực hiện ≤ độ ưu tiên vai trò | Admin cấp | Bị từ chối (leo thang đặc quyền) |
EFF - Quyền hiệu lực & Phạm vi Một phần
Đặc tả: PRD-EFF-001 · 4 yêu cầu · 1 kịch bản nghiệm thu
Bất kỳ ai cũng có thể hỏi một người dùng thực sự được phép làm gì: hợp đã loại trùng của các bản ghi cấp trực tiếp và các bản ghi cấp kế thừa qua vai trò, và họ thuộc về những organizer và merchant nào. Một bản ghi cấp chỉ được phân giải trong domain merchant đang hoạt động, được chọn theo từng request.
| Mã | Ưu tiên | Yêu cầu | Trạng thái |
|---|---|---|---|
URD-EFF-001 | Bắt buộc | Tính quyền hiệu lực là hợp của các bản ghi cấp trực tiếp + kế thừa qua vai trò (đã loại trùng). | Đã dựng |
URD-EFF-002 | Nên có | Truy vấn hỗ trợ bộ lọc chế độ: direct, inherit, hoặc cả hai. | Đã dựng |
URD-EFF-003 | Bắt buộc | Truy xuất mọi organizer và mọi merchant mà một người dùng thuộc về. | Đã dựng |
URD-EFF-004 | Bắt buộc | Một bản ghi cấp chỉ được phân giải trong domain merchant đang hoạt động được chọn theo từng request. | Một phần |
AC-EFF-01: Quyền hiệu lực
| Cho trước | Khi | Thì |
|---|---|---|
| Người dùng có vai trò + cấp trực tiếp | Truy vấn hiệu lực | Hợp của trực tiếp + kế thừa, đã loại trùng |
| Chế độ = direct | Truy vấn | Chỉ trả về các bản ghi cấp trực tiếp |
| Chế độ = inherit | Truy vấn | Chỉ trả về các bản ghi cấp kế thừa qua vai trò |
HIER - Phân cấp Tài nguyên, Hành động & Domain Một phần
Đặc tả: PRD-HIER-001 (chung cho
HIERvàDECL) · 9 yêu cầu · 2 kịch bản nghiệm thu
Các quyền tạo thành một cây tài nguyên × bậc thang hành động × cây domain, nên một bản ghi cấp thô (ví dụ Sale:manage) thay thế nhiều bản ghi cấp phẳng - bao phủ mọi đối tượng, thao tác, và merchant bên dưới nó. Một vai trò trụ sở/quản lý vươn tới mọi merchant của organizer mình qua một bản ghi cấp giới hạn phạm vi organizer, thay vì cần một hàng thành viên theo từng merchant - phần vươn theo phạm vi organizer này còn đang xây dựng.
| Mã | Ưu tiên | Yêu cầu | Trạng thái |
|---|---|---|---|
URD-HIER-001 | Bắt buộc | Một bản ghi cấp CÓ THỂ nhắm vào một tài nguyên ở bất kỳ cấp nào - module, đối tượng, hoặc thao tác - và tự động bao phủ mọi hậu duệ. | Đã dựng |
URD-HIER-002 | Bắt buộc | Tài nguyên thao tác lồng dưới đối tượng của nó theo mã có dấu chấm (SaleOrder.refund ⊂ SaleOrder) mà không cần cấu hình thêm. | Đã dựng |
URD-HIER-003 | Bắt buộc | Một đối tượng CÓ THỂ gom lên một module để một bản ghi cấp cấp module bao phủ mọi đối tượng trong nó (cấp trên Sale ⇒ SaleOrder, SaleOrderItem, …). | Đã dựng |
URD-HIER-004 | Nên có | Một thực thể con CÓ THỂ lồng dưới một thực thể cha bất kể cách đặt tên (SaleOrderItem ⊂ SaleOrder) để bản ghi cấp của cha bao phủ con. | Chưa dựng |
URD-HIER-005 | Bắt buộc | Hành động tạo thành một bậc thang - manage ⊃ {write, read, execute}, write ⊃ {create, update, delete} - một hành động được cấp rộng hơn thỏa mãn mọi yêu cầu hẹp hơn. | Đã dựng |
URD-HIER-006 | Bắt buộc | Một route yêu cầu một hành động cơ bản (read/create/update/delete/execute); một bản ghi cấp CÓ THỂ dùng bất kỳ tầng nào kể cả manage/write. | Đã dựng |
URD-HIER-007 | Bắt buộc | Một bản ghi cấp giới hạn phạm vi vào một Organizer áp dụng cho mọi merchant dưới nó; một bản ghi cấp giới hạn vào một Merchant chỉ áp dụng tại đó. | Chưa dựng |
URD-HIER-008 | Bắt buộc | Một vai trò trụ sở / quản lý vươn tới mọi merchant của organizer của nó thông qua một bản ghi cấp giới hạn phạm vi organizer - mà không cần một hàng thành viên theo từng merchant. | Chưa dựng |
URD-HIER-009 | Nên có | Một bản ghi cấp CÓ THỂ ghim vào SYSTEM_WIDE (mọi nơi) hoặc ANY_MEMBER (mọi merchant đã tham gia). | Đã dựng |
AC-HIER-01: Bản ghi cấp thô bao phủ hậu duệ (ví dụ manage)
| Cho trước | Khi | Thì |
|---|---|---|
Một vai trò được cấp (SaleOrder, manage) | Một request read SaleOrder.refund | Được phép - manage ⊃ read (bậc thang) và SaleOrder.refund ⊂ SaleOrder (dấu chấm) |
Một vai trò được cấp (Sale, manage) (module) | Bất kỳ thao tác nào trên SaleOrderItem | Được phép - SaleOrderItem ⊂ Sale (gom lên module) |
Một vai trò chỉ được cấp (SaleOrder, read) | Một request create SaleOrder | Bị từ chối - read không bao phủ create |
AC-HIER-02: Phạm vi domain (organizer vs merchant / HQ)
| Cho trước | Khi | Thì |
|---|---|---|
Một vai trò được cấp trên Organizer_9 | Một request trong bất kỳ Merchant nào dưới organizer 9 | Được phép qua g3 - không cần thành viên theo từng merchant |
Một vai trò chỉ được cấp trên Merchant_7 | Một request trong Merchant_8 | Bị từ chối - phạm vi là một merchant đơn lẻ |
| Một người dùng HQ/quản lý (bản ghi cấp giới hạn phạm vi organizer) | Hành động trên bất kỳ merchant cùng organizer nào | Được phép - vươn tới mọi merchant của organizer |
DECL - Khai báo quyền Một phần
Đặc tả: PRD-HIER-001 (chung cho
HIERvàDECL) · 5 yêu cầu · 1 kịch bản nghiệm thu
Các quyền, bậc thang hành động, và phân cấp tài nguyên/domain được khai báo trong code với boilerplate tối thiểu và được seed một cách bất biến, nên một controller mới chỉ cần một lệnh gọi helper và các mã đã gỡ bỏ được đối soát loại đi tự động.
| Mã | Ưu tiên | Yêu cầu | Trạng thái |
|---|---|---|---|
URD-DECL-001 | Bắt buộc | Các quyền, bậc thang hành động, và các cạnh phân cấp tài nguyên/domain được khai báo trong code và seed một cách bất biến. | Một phần |
URD-DECL-002 | Bắt buộc | Khai báo các thao tác + node tài nguyên + hành động cơ bản của một controller chỉ cần boilerplate tối thiểu (một lệnh gọi helper). | Đã dựng |
URD-DECL-003 | Bắt buộc | Gán vai trò→quyền được khai báo dưới dạng một ma trận nhỏ (ví dụ OWNER → Sale:manage), không phải liệt kê theo từng thao tác. | Đã dựng |
URD-DECL-004 | Bắt buộc | Việc seed đối soát các mã đã gỡ bỏ một cách bất biến - không tích tụ quyền/bản ghi cấp cũ. | Đã dựng |
URD-DECL-005 | Có thể | Bộ công cụ được xây dựng trong nx-seller trước, với lộ trình rõ ràng để chuyển vào IGNIS sau này. | Đã dựng |
AC-DECL-01: Khai báo với boilerplate tối thiểu
| Cho trước | Khi | Thì |
|---|---|---|
| Một controller mới | Tác giả khai báo tài nguyên + các thao tác của nó trong một lệnh gọi helper | Các quyền + đặc tả authorize của route được tạo ra mà không cần liệt kê CRUD |
| Dàn hành động + các cạnh phân cấp | Migration chạy | Các cạnh g5/g4/g3 được seed bất biến; chạy lại không thay đổi gì |
| Một mã quyền bị gỡ khỏi khai báo | Migration chạy | Quyền cũ + các bản ghi cấp của nó được đối soát loại đi |
4. Ràng buộc
| Mã | Ràng buộc |
|---|---|
C-01 | Tám vai trò cố định được seed lúc khởi động và là bất biến. |
C-02 | Không ai có thể tạo hoặc quản lý một vai trò ngang hoặc cao hơn độ ưu tiên của chính mình. |
C-03 | Mã quyền là duy nhất toàn cục. |
C-04 | Các thao tác cấp quyền là bất biến (idempotent). |
C-05 | Mọi bản ghi đều được soft-delete, không bao giờ bị xóa vật lý. |
C-06 | Session token là stateless - thay đổi vai trò/quyền có hiệu lực ở lần đăng nhập kế tiếp. |
C-07 | Mọi thao tác đều yêu cầu xác thực. |
C-08 | Một bản ghi cấp được phân giải trong một domain merchant đang hoạt động cho mỗi request. |
5. Loại trừ
Phân quyền cố ý không làm những việc sau.
| Mã | Việc không làm |
|---|---|
N-01 | Quyền glob/regex tùy ý (ví dụ sales.*) - truy cập thô đến từ cây tài nguyên + hành động manage (HIER), không phải glob. |
N-02 | Quyền theo thời gian hoặc theo ca. |
N-03 | Log kiểm toán quyền. |
N-04 | Chuyển đổi vai trò đang hoạt động theo từng merchant. |
Được thay thế bởi
HIER: các phi mục tiêu trước đây "quyền dạng wildcard", "phân nhóm quyền / nhóm hiển thị trên UI" (nay là gom lên module), và "mẫu / gói vai trò" (nay là một ma trận vai trò→quyền thô) được thay thế bằng phân cấp tài nguyên/hành động/domain.
Lịch sử phiên bản
| Ngày | Người | Thay đổi | Phiên bản |
|---|---|---|---|
| 2026-02-26 | P. Do - Product Owner | User story ban đầu | v0.1 |
| 2026-04-16 | Product | Cấu trúc lại theo định dạng URD | v0.3 |
| 2026-05-29 | Product | Chuyển sang quy ước module-docs; đối soát với RBAC Casbin theo từng merchant (8 vai trò, độ ưu tiên, domain); đặt lại khóa các vùng (ROLE/CROLE/PERM/GRANT/EFF) | v0.4 |
| 2026-06-04 | Product | Bổ sung vùng Phân cấp Tài nguyên/Hành động/Domain (HIER) + Khai báo quyền (DECL) cho increment resource-action-hierarchy (PRD-HIER-001); thay thế các phi mục tiêu wildcard/categories/bundles | v0.5 |
| 2026-06-04 | Claude (AI pair) | Tổ chức lại theo tính năng (Feature Spine); mỗi tính năng mang yêu cầu + tiêu chí chấp nhận của riêng nó | v0.6 |
| 2026-07-06 | P. Nguyen | Nhận gộp định nghĩa vai trò từ URD User Management (khu bên đó tách thành MORG); thêm URD-ROLE-010 (Owner tự gán khi tạo organizer); cột ưu tiên đổi sang chữ đầy đủ, bọc backtick mã yêu cầu; điền liên kết PRD cho ROLE/CROLE/PERM/GRANT/EFF | v0.7 |
| 2026-07-15 | Claude (AI pair) | Chuyển sang định dạng trình bày chuẩn Kho: DocTabs ba tab (Tổng quan / Chi tiết / Lịch sử), bảng yêu cầu 4 cột kèm badge trạng thái theo từng dòng, ::: details AC-<CODE>-NN cho tiêu chí nghiệm thu; gộp §3 Phạm vi (đã cũ) vào Loại trừ; không đổi nội dung yêu cầu hay quyết định đã chốt | v0.8 |