Skip to content

Perm-v2: Gán phân quyền cho tính năng mới

Tài liệu này mô tả luồng chuẩn để cấp quyền cho:

  • tính năng mới
  • module mới
  • table/query/mutation mới
  • Hasura action mới

Trong hệ thống hiện tại, quyền được kiểm tra qua 2 lớp:

  1. perm-v2 trong auth middleware
  2. Hasura metadata permission

Chỉ cần thiếu 1 trong 2 lớp thì tính năng vẫn không dùng được.

Hiểu Nhanh Về Cơ Chế

Flow hiện tại:

account_role -> role_module(module_id, portal, actions) -> auth allow-list -> Hasura table/action/function permission

Các điểm quan trọng:

  • Auth middleware kiểm tra theo root GraphQL operation
  • Một request GraphQL chỉ cần có 1 root operation chưa được map là cả request bị fail
  • Có quyền menu không đồng nghĩa với có quyền gọi API
  • Có Hasura permission không đồng nghĩa với qua được perm-v2

Khi Nào Cần Dùng Tài Liệu Này

Áp dụng khi:

  • thêm màn hình mới
  • thêm table query/mutation mới
  • thêm Hasura action mới
  • thêm module mới trong phân quyền động
  • màn hình đã được gán role nhưng vẫn bị permission denied

Bước 1: Xác Định Tính Năng Thuộc Module Nào

Trước tiên cần chốt rõ:

  • tính năng này thuộc module nào
  • tính năng này chạy ở portal nào: admin, crm, pos, staff, ...
  • tính năng này cần action nào: access, create, update, delete, approve, payment, view_all

Nếu đã có module sẵn:

  • dùng lại module_id hiện có

Nếu là module mới:

  • thêm record vào public.module
  • thêm các action vào public.module_permission_action
  • thêm mapping operation vào services/auth/server/config.go

Bước 2: Liệt Kê Toàn Bộ Root GraphQL Operation

Đây là bước dễ bị sót nhất.

Cần liệt kê toàn bộ root operation mà màn hình thực tế gọi, ví dụ:

  • group
  • group_member
  • group_role
  • module_permission_action
  • role_module
  • updateRoleModuleShowSumcardData

Lưu ý:

  • không chỉ liệt kê table chính
  • cần liệt kê cả các root query/mutation phụ trong cùng request
  • nếu frontend gửi 1 request có nhiều root operation, chỉ cần 1 root chưa được map là cả request fail

Bước 3: Map Root Operation Vào Perm-v2

File chính:

  • services/auth/server/config.go

Hiện tại có 3 cách mở operation:

3.1. Common Operations

Thêm vào CommonQueryMutationList khi operation đó được phép cho mọi user phù hợp với flow chung của hệ thống.

Dùng cho các root operation thật sự mang tính dùng chung.

3.2. User Operations

Thêm vào UserQueryMutationList khi operation đó dành cho user đã xác thực nhưng không nhất thiết phải gắn vào 1 module nghiệp vụ cụ thể.

3.3. Module Operations

Thêm vào ModuleOperationMapping[module_id][action] khi operation thuộc về một module cụ thể.

Đây là cách ưu tiên cho feature nghiệp vụ.

Ví dụ:

  • operation quản lý role, role_module, account_role thường nên gắn vào internal_configuration
  • operation liên quan đến khách hàng, CRM, đơn hàng thường nên gắn vào customer_management

Bước 4: Mở Hasura Metadata Permission

Sau khi request đi qua auth middleware, nó mới tới Hasura.

Vì vậy cần mở đúng metadata ở lớp Hasura:

  • table: services/controller/metadata/databases/.../tables/*.yaml
  • function: services/controller/metadata/databases/.../functions/*.yaml
  • action: services/controller/metadata/actions.yaml

Cần kiểm tra đúng loại permission:

  • select_permissions
  • insert_permissions
  • update_permissions
  • delete_permissions
  • action permissions

Lưu ý:

  • perm-v2 pass nhưng Hasura chưa mở thì vẫn fail
  • Hasura mở rồi nhưng auth config chưa map thì vẫn fail

Bước 5: Nếu Là Module Mới, Tạo Action Cho Module

Nếu module mới chưa có action trong UI phân quyền:

  • thêm action vào public.module_permission_action

Thông thường sẽ có:

  • access
  • create
  • update
  • delete
  • approve
  • payment
  • view_all

Nếu thiếu các action này thì UI phân quyền và dữ liệu role_module.actions sẽ không đầy đủ.

Bước 6: Cấp Quyền Cho Role

Cấp quyền bằng role_module.

Các trường quan trọng:

  • role_id
  • module_id
  • portal
  • actions

Ví dụ:

sql
INSERT INTO public.role_module (role_id, module_id, portal, actions, priority)
VALUES (
  'it_leader',
  'internal_configuration',
  'admin',
  ARRAY['access', 'update'],
  0
);

Nếu role đã có row rồi thì cập nhật actions hoặc metadata, không nên insert trùng bừa.

Bước 7: Gán Role Cho User

Gán role cho user qua account_role.

sql
INSERT INTO public.account_role (account_id, role_id)
VALUES ('USER_ID', 'ROLE_ID');

Nếu chỉ muốn đúng 1 user có quyền đó:

  • tạo role riêng
  • cấp role_module cho role đó
  • gán role vào user

Bước 8: Kiểm Tra Cache

Đây là điểm rất hay gây nhầm.

  • thay đổi role_module thường được auth service clear cache
  • thay đổi account_role có thể chưa làm auth cache được clear ngay

Nếu vừa gán role xong mà vẫn chưa ăn:

  • đợi cache hết hạn
  • hoặc clear cache auth/Redis theo quy trình nội bộ

Bước 9: Test Bằng Account Thật

Không nên chỉ test bằng admin hoặc account siêu quyền.

Cần test bằng đúng account:

  • portal đúng
  • role đúng
  • đi vào đúng màn hình thật

Nên test 3 mức:

  1. request GraphQL thật của màn hình
  2. auth log để xem bị chặn ở root operation nào
  3. Hasura response để xác định fail ở metadata hay fail từ auth middleware

Nếu Là Từng Loại Thay Đổi Thì Cần Làm Gì

A. Thêm Module Mới

Cần làm:

  1. thêm public.module
  2. thêm public.module_permission_action
  3. thêm ModuleOperationMapping trong services/auth/server/config.go
  4. mở Hasura metadata cho table/action/function liên quan
  5. cấp role_module
  6. gán account_role

B. Thêm Table Query/Mutation Mới

Cần làm:

  1. xác định root operation tên gì
  2. map root đó vào auth config
  3. mở Hasura metadata permission cho table
  4. test bằng request thật

C. Thêm Hasura Action Mới

Cần làm:

  1. thêm action vào services/controller/metadata/actions.yaml
  2. thêm schema vào services/controller/metadata/actions.graphql
  3. set đúng permissions
  4. map tên mutation/action vào auth config
  5. test bằng role thật

Cách Debug Khi “Đã Có Quyền Rồi Mà Vẫn Không Chạy”

Kiểm tra theo đúng thứ tự này:

  1. Feature này thuộc module nào?
  2. Root operation nào đang được gọi thực tế?
  3. Root operation đó đã được map trong services/auth/server/config.go chưa?
  4. Hasura table/action/function permission đã mở chưa?
  5. User đã có account_role đúng chưa?
  6. Role đã có role_module đúng portalactions chưa?
  7. Có đang dính cache auth không?

Các Lỗi Hay Gặp

1. View query group nhưng thực ra fail do group_role hoặc group_member

Rất nhiều màn hình không chỉ query 1 root.

Ví dụ 1 request có thể gọi cùng lúc:

  • group
  • group_member
  • group_role
  • module_permission_action

Nếu chỉ mở group mà chưa mở các root còn lại thì cả view vẫn fail.

2. Có Hasura permission nhưng vẫn permission denied

Nguyên nhân thường là auth middleware chưa map root operation đó vào perm-v2.

3. Có role_module nhưng vẫn không chạy

Thường là do:

  • sai portal
  • thiếu action cần thiết
  • user chưa được gán role đúng
  • đang dùng cache cũ

4. Nghĩ Rằng Module Cha Tự Động Ăn Module Con

Hiện tại auth check theo operation mapping, không check đệ quy theo parent_id.

Nghĩa là:

  • có module cha không đồng nghĩa tự động được tất cả operation bên dưới
  • vẫn phải map đúng root operation vào module tương ứng

Checklist Copy-paste Cho Team

Trước khi merge feature có permission mới, kiểm tra:

  • [ ] Feature thuộc module nào?
  • [ ] Chạy ở portal nào?
  • [ ] Cần action nào?
  • [ ] Frontend gọi những root GraphQL operation nào?
  • [ ] Đã map hết các root đó trong services/auth/server/config.go chưa?
  • [ ] Đã mở Hasura metadata permission chưa?
  • [ ] Nếu là action mới: đã khai báo trong actions.yamlactions.graphql chưa?
  • [ ] Đã cấp role_module cho role cần dùng chưa?
  • [ ] Đã gán account_role cho user test chưa?
  • [ ] Đã test bằng account thật chưa?
  • [ ] Đã kiểm tra cache auth chưa?

Gợi Ý Quy Trình Thực Tế Khi Làm Feature Mới

Nên làm theo thứ tự này:

  1. Viết ra danh sách root operation của màn hình
  2. Map các operation đó vào perm-v2
  3. Mở Hasura metadata
  4. Cấp role_module
  5. Gán role vào user test
  6. Test request thật
  7. Nếu fail thì đọc auth log để xem denied_operation

Làm theo thứ tự này sẽ nhanh hơn rất nhiều so với việc sửa từng chỗ một cách mò mẫm.