[CLAUDE] Infra: ẩn 5 nhóm menu (Hợp đồng + Danh mục + HRM/Office/Cá nhân) chỉ Admin thấy trên eoffice (S92)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m24s

- RevokeTemporarilyHiddenModulesAsync mở rộng predicate: +Contracts/Ct_*
  +Master/Suppliers/Projects/Departments/Catalog* (cộng dồn S58 Hrm*/Off*/Personal).
- Tắt 2 re-grant public cũ: SeedAllRolesHrmProfileReadPermissions (S65) +
  SeedAllRolesOfficeModulePermissions (S69). Revoke là bước permission CUỐI trong
  SeedAdminPermissionsAsync (ungated) → durable, re-ẩn mỗi restart prod (gotcha #75).
- Thu hẹp InReviewScope → chỉ Pe_* (Duyệt NCC giữ nguyên cho user thường;
  master-data GET = any-auth nên form PE vẫn chọn được Dự án/NCC/Hạng mục dù menu ẩn).
- 2 method grant giữ private static (reversibility) → 2 seed test cũ vẫn invoke cô-lập, pass.
- +AdminOnlyModulesRevokeTests (3 test-before-merge, security): non-Admin revoked 4 cờ /
  Admin untouched / PE preserved. Test 431→434 PASS (45 Domain + 389 Infra).
- Reviewer PASS (5 attack vector: seed-order/under-revoke/over-revoke/test-genuine/prod-impact).
  FYI: role CatalogManager (0 user hiện tại) cũng bị thu Danh mục — intended-by-spec.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-06-29 14:58:59 +07:00
parent f98a743074
commit 1408d4eb03
4 changed files with 223 additions and 8 deletions

View File

@ -1996,14 +1996,20 @@ public static class DbInitializer
// (+ root nhóm "Nhân sự") cho MỌI role — anh chốt public module Nhân sự cho
// user thường tra cứu hồ sơ. Dashboard NS (Hrm_Dashboard) + 6 catalog
// Hrm_Config* VẪN ẨN (revoke che). Read-only (chỉ CanRead).
await SeedAllRolesHrmProfileReadPermissionsAsync(db, roleManager, logger);
// [S92 2026-06-29] TẮT re-grant "Hồ sơ Nhân sự" public (S65) — anh chốt nhóm
// "Nhân sự" CHỈ Admin thấy. RevokeTemporarilyHiddenModulesAsync (chạy ngay trên,
// S92 mở rộng phủ thêm Contracts/Master/Catalogs) là bước permission CUỐI → thắng
// mọi grant. Bật lại module nào: bỏ comment call tương ứng + gỡ prefix khỏi revoke.
// await SeedAllRolesHrmProfileReadPermissionsAsync(db, roleManager, logger); // S65 — TẮT S92
// [S69 2026-06-17] GOLIVE Văn phòng số: mở Read+Create module Office (self-service)
// cho MỌI role — anh chốt "public văn phòng số cho all user eoffice". CHẠY SAU revoke
// để THẮNG (mirror S65). Allow-list 16 key (loại Off_PhongHop_Manage admin-CRUD +
// Off_AttendanceReport báo-cáo-riêng-tư + Off_ChamCong Cá-nhân — giữ ẩn). HRM (trừ
// Hồ sơ NS) + Personal VẪN ẩn (anh chỉ mở Office).
await SeedAllRolesOfficeModulePermissionsAsync(db, roleManager, logger);
// [S92 2026-06-29] TẮT re-grant "Văn phòng số" public (S69) — anh chốt nhóm
// "Văn phòng số" CHỈ Admin thấy.
// await SeedAllRolesOfficeModulePermissionsAsync(db, roleManager, logger); // S69 — TẮT S92
}
// [S57] Cấp CanRead (CHỈ xem) cho MỌI role trên menu HRM + Office + Master để mọi
@ -2027,10 +2033,12 @@ public static class DbInitializer
// số + Cá nhân với user thường (chưa golive các module này). Revoke tương ứng
// ở RevokeTemporarilyHiddenModulesAsync (chạy sau). Loại trừ tự nhiên (không
// match prefix): PeWf_* (ký tự thứ 3 'W' ≠ '_'), AwV2_*, Ct_*, Bg_*, Wf_*, System.
static bool InReviewScope(string key) =>
key.StartsWith("Catalog") || key == MenuKeys.Master ||
key == MenuKeys.Suppliers || key == MenuKeys.Projects || key == MenuKeys.Departments ||
key.StartsWith("Pe_");
// [S92 2026-06-29] Narrow: BỎ Master/Catalogs/Suppliers/Projects/Departments khỏi
// grant-all-roles (anh chốt "Danh mục" CHỈ Admin). GIỮ Pe_* (Duyệt NCC read+create
// cho mọi role — KHÔNG đụng). Master-data GET = any-auth (Suppliers/Projects/Catalogs
// controller class-[Authorize]) nên form PE vẫn chọn Dự án/NCC/Hạng mục dù menu ẩn.
// Row Master/Catalogs cũ trên prod được RevokeTemporarilyHiddenModulesAsync (dưới) gỡ.
static bool InReviewScope(string key) => key.StartsWith("Pe_");
// Phân biệt key Pe_* (read+create) vs read-only. Pe_* match cờ thứ-3 '_'
// → "PeWf_*"/"PeWorkflows" KHÔNG match (loại admin Designer).
@ -2128,7 +2136,14 @@ public static class DbInitializer
var rows = await db.Permissions
.Where(p => p.RoleId != adminRoleId
&& (p.MenuKey.StartsWith("Hrm") || p.MenuKey.StartsWith("Off")
|| p.MenuKey == MenuKeys.Personal)
|| p.MenuKey == MenuKeys.Personal
// [S92 2026-06-29] +Hợp đồng (Contracts + Ct_*) + Danh mục (Master +
// Suppliers + Projects + Departments + Catalog*) — anh chốt "chỉ Admin
// thấy". Pe_* (Duyệt NCC) KHÔNG nằm đây → module PE giữ nguyên cho user.
|| p.MenuKey == MenuKeys.Contracts || p.MenuKey.StartsWith("Ct_")
|| p.MenuKey == MenuKeys.Master || p.MenuKey == MenuKeys.Suppliers
|| p.MenuKey == MenuKeys.Projects || p.MenuKey == MenuKeys.Departments
|| p.MenuKey.StartsWith("Catalog"))
&& (p.CanRead || p.CanCreate || p.CanUpdate || p.CanDelete))
.ToListAsync();
@ -2144,7 +2159,8 @@ public static class DbInitializer
{
await db.SaveChangesAsync();
logger.LogInformation(
"Revoked {Count} permission rows — tạm ẩn HRM/Văn phòng số/Cá nhân khỏi non-Admin (S58)",
"Revoked {Count} permission rows — ẩn HRM/Văn phòng số/Cá nhân/Hợp đồng/Danh mục " +
"khỏi non-Admin (chỉ Admin thấy — S58 + S92)",
rows.Count);
}
}