sync/identity/2026-09-24-be-iam-role-policy.md · Xem trên GitHub

Phân quyền kiểu IAM: role + policy, API quản lý role/policy, API quyền của tôi

Đã đóng high breaking
Module
identity
Từ
BE
Người raise
ThaiThanhLuan
Ngày tạo
2026-09-24
Refs
IDENTITY-031, IDENTITY-032, IDENTITY-033, IDENTITY-034, WEWORK-031, WORKFLOW-024, REQUEST-023
Bên nhận
FE appliedMobile n/a

Tóm tắt

Quyền trong identity giờ được kiểm tra theo action (vd identity:user:create), lấy từ policy của các role mà user đang có, không còn so tên role owner/system_admin. Role system_admin và member được đổi tên thành "Quản trị hệ thống" và "Thành viên", nên mọi chỗ FE đang so tên role (useIsOrgAdmin, ADMIN_ROLES) sẽ sai: user system_admin cũ mất giao diện quản trị. FE cần chuyển sang API mới GET /users/me/permissions. Spec: specs/003-iam-role-policy/.

Chi tiết

  • Mỗi route identity gắn 1 action, bảng đầy đủ ở specs/003-iam-role-policy/contracts/action-route-map.md. Thiếu action → 403 common.permission.denied, body có thêm details: { action }.
  • Wework / Workflow / Request cũng kiểm tra action (WEWORK-031, WORKFLOW-024, REQUEST-023): mọi route của 3 service này giờ cần action tương ứng, thiếu thì 403 common.permission.denied. User đã chuyển đổi sang "Thành viên" / "Quản trị hệ thống" vẫn toàn quyền 3 service; user mới phải được gán role có policy tương ứng (vd WeworkMember, RequestUser). Luật nghiệp vụ cũ (người duyệt bước hiện tại, người tạo job...) giữ nguyên.
  • Role cho nhóm, bỏ quyền theo ứng dụng (IDENTITY-033): gán role cho nhóm, mọi thành viên thừa hưởng. Các API quyền ứng dụng cũ (/groups/:groupId/app-permissions/:appId, /users/:userId/app-permissions/:appId, .../effective) đã bị xoá (404). Thêm thành viên vào nhóm có role: người thao tác phải có đủ quyền của các role đó (403 identity.iam.escalation_denied), khách không vào được nhóm có role (409 identity.iam.guest_cannot_have_roles).
  • Việc cá nhân không cần action: /users/me, /users/me/permissions, 2FA, lịch sử đăng nhập, lịch làm việc của mình, thông báo của mình, ủy quyền, /organizations/me, /applications.
  • Role sau khi chuyển đổi dữ liệu:
    • owner: toàn quyền (policy AdministratorAccess, action *).
    • "Quản trị hệ thống" (trước là system_admin): toàn quyền identity + wework + workflow + request.
    • "Thành viên" (trước là member): xem danh bạ, nhóm, lịch làm việc; toàn quyền 3 phân hệ nghiệp vụ.
    • Tổ chức mới chỉ có role owner.
  • Không còn role mặc định: POST /users không tự gán member nữa. Chọn role ngay trong form tạo tài khoản (roleIds) hoặc gán sau (POST /users/:id/roles { roleId }). User không có role chỉ làm được việc cá nhân.
  • Quản lý role/policy (IDENTITY-032): tổ chức tự tạo role từ policy hệ thống hoặc policy tuỳ chỉnh. Không cấp được quyền mình không có (403 identity.iam.escalation_denied, details.missing), chỉ owner gán/gỡ owner (403 identity.iam.owner_only), luôn còn ít nhất 1 owner đang hoạt động (409 identity.iam.last_owner).
  • Claim roles trong access token vẫn còn (tên role) nhưng không còn quyết định quyền.
  • Lỗi domain có thể kèm details (object) bên cạnh code/message. Trường này chỉ xuất hiện khi có dữ liệu.

Contract (trước → sau)

Trước Sau
Endpoint mới — GET /users/me/permissions (mọi nhân viên đã đăng nhập; khách → 403)
Response mới — { data: { roles: RoleRef[], actions: string[] } }, actions đã mở rộng wildcard, sắp xếp
RoleRef — { id, name, key: 'owner' | null, source: 'direct' | 'group', groupId?, groupName? }
Response User { ..., roles: string[] } { ..., roles: string[], roleRefs: RoleRef[] }
POST /users tự gán role member body thêm roleIds?: string[]; không có thì không gán role nào
POST /users/:id/roles body { roleName }, trả string[] body { roleId }, trả RoleRef[] (role trực tiếp) — breaking
DELETE /users/:id/roles/:roleId — mới, trả RoleRef[]; 404 identity.user_role.not_assigned, 409 identity.iam.last_owner
GET/POST /roles, GET/PATCH/DELETE /roles/:id — mới (identity:role:*); Role { id, name, description, key, isSystem, policies[], userCount, groupCount }
GET /iam/actions — mới: danh mục action { code, service, description }[]
Policy hệ thống (GET /policies?type=system) — 19 policy, gồm 5 policy quản trị người dùng để ghép vào role: UserReadOnly, UserAdmin, UserRoleAdmin, GroupManagement, GuestManagement
GET/POST /policies, GET/PATCH/DELETE /policies/:id — mới (identity:policy:*); Policy { id, name, description, isSystem, key, actions[], staleActions[], roleCount }
PATCH /users/:id — khoá owner đang hoạt động cuối cùng → 409 identity.iam.last_owner
GET/POST /groups/:id/roles, DELETE /groups/:id/roles/:roleId — mới (identity:group:read / identity:group:assign-role), trả RoleRef[] (source: 'group'); role owner → 409
PUT/DELETE /groups/:groupId/app-permissions/:appId, PUT/DELETE /users/:userId/app-permissions/:appId, GET .../effective có xoá (404) — breaking
RoleRef trong roleRefs / /users/me/permissions chỉ direct thêm role thừa hưởng từ nhóm: source: 'group', groupId, groupName
403 thiếu quyền { statusCode: 403, message } (Nest mặc định) { statusCode: 403, code: 'common.permission.denied', message, details: { action } }
Tên role owner, system_admin, member owner, Quản trị hệ thống, Thành viên (+ role tổ chức tự tạo, từ IDENTITY-032)

Việc bên nhận cần làm

  • FE: thay useIsOrgAdmin / ADMIN_ROLES / rolesFromAccessToken bằng kiểm tra action từ GET /users/me/permissions (vd trang Audit log cần identity:audit-log:read, trang quyền ứng dụng cần identity:user:assign-role).
  • FE: gọi lại /users/me/permissions sau đăng nhập, sau khi refresh token, và khi tab focus lại (quyền có thể trễ tới ~30 giây sau khi admin đổi).
  • FE: hiển thị role từ roleRefs (tên có thể có dấu và khoảng trắng); không so sánh tên role cố định.
  • FE: xử lý 403 common.permission.denied bằng thông báo "không có quyền" (có thể đọc details.action), ở cả màn hình Wework / Workflow / Request; ẩn nút theo action (vd wework:project:create, request:template:create).
  • FE: form tạo tài khoản thêm chọn role (GET /roles → roleIds trong POST /users).
  • FE: đổi gán role sang POST /users/:id/roles { roleId }, thêm gỡ role DELETE /users/:id/roles/:roleId.
  • FE: bỏ trang/luồng quyền theo ứng dụng (AppPermissionsPage), thay bằng gán role cho nhóm (/groups/:id/roles).
  • FE: màn hình quản lý role (/roles) và policy (/policies, danh mục GET /iam/actions); hiển thị details.missing khi 403 identity.iam.escalation_denied; ẩn nút sửa/xoá với isSystem: true.

Cách kiểm tra

  1. Đăng nhập owner → GET /users/me/permissions → actions chứa đủ 60 action, roles[0].key === 'owner'.
  2. Tạo user mới rồi đăng nhập bằng user đó → GET /users → 403 common.permission.denied, details.action = 'identity:user:read'.
  3. Owner lấy id role "Thành viên" từ GET /roles, gán cho user đó (POST /users/:id/roles { roleId }) → GET /users của user đó trả 200 (trong ≤ 30 giây với service khác identity).
  4. User chỉ có IdentityFullAccess tạo role gắn WeworkFullAccess → 403 identity.iam.escalation_denied.

Thảo luận

[FE] 2026-09-25 @NinelXram — acked

Làm trên branch feat/fe-identity-invite-iam: thay useIsOrgAdmin/ADMIN_ROLES bằng GET /users/me/permissions, hiển thị roleRefs, gán role theo roleId, bỏ trang quyền ứng dụng (API đã xoá).

[FE] 2026-09-25 @NinelXram — applied

Commit e4e9c11 (branch feat/fe-identity-invite-iam). Đủ 3 việc + các breaking (roleId, xoá app-permissions, roleIds, details trong lỗi). Chưa làm UI quản lý role/policy (/roles, /policies) và gán role cho nhóm — sẽ làm thành task FE riêng.

[FE] 2026-09-25 @NinelXram — applied

Làm nốt phần còn lại trên branch feat/fe-iam-access-management (635757e..6a02533): trang Phân quyền /access (quản lý role + policy, cây chọn action từ /iam/actions, hiện details.missing khi escalation_denied, ẩn sửa/xoá với isSystem) và gán role cho nhóm (/groups/:id/roles). Nhóm theo role chưa tra được → note 2026-09-25-fe-role-groups-lookup.