| name | awcms-abac-guard |
| description | Terapkan kontrol akses RBAC+ABAC default-deny plus RLS pada endpoint/service AWCMS. Gunakan pada setiap endpoint non-public dan setiap akses data tenant-scoped. Menegakkan default deny, deny-overrides-allow, decision log, dan tenant isolation sesuai doc 03 & 10. |
AWCMS — ABAC Guard & Tenant Isolation
Ikuti docs/awcms/03_srs_detail_per_modul.md, docs/awcms/10_template_kode_coding_standard.md, dan docs/awcms/17_default_seed_rbac_abac.md (matriks role→permission & default ABAC policy). Mekanisme RLS/tenant context konkret: docs/awcms/16_backend_data_access_integration.md.
Prinsip
- Default deny — tidak ada policy yang mengizinkan = tolak.
- Deny overrides allow — satu deny mengalahkan semua allow.
- RLS tetap wajib walau ABAC sudah cek (defense in depth).
- Akses ditolak yang high-risk → catat di decision log.
- UI hiding bukan kontrol utama; backend tetap validasi.
- Archive/restore/purge soft delete default deny sampai permission eksplisit tersedia.
Bentuk request/decision
type AccessRequest = {
moduleKey: string;
activityCode: string;
action:
| "read"
| "create"
| "update"
| "delete"
| "post"
| "cancel"
| "approve"
| "export"
| "send"
| "configure"
| "analyze"
| "assign"
| "restore"
| "purge"
| "retry"
| "sync"
| "enable"
| "disable"
| "check"
| "publish"
| "schedule"
| "archive"
| "verify"
| "set_primary"
| "connect"
| "disconnect"
| "preview"
| "release"
| "replay"
| "manage"
| "revoke"
| "override"
| "reject"
| "retire"
| "reassign"
| "force_decide"
| "merge"
| "commit"
| "rollback"
| "void"
| "reclassify"
| "reserve"
| "rebuild";
resourceType?: string;
resourceId?: string;
resourceAttributes?: Record<string, unknown>;
environmentAttributes?: Record<string, unknown>;
};
type AccessDecision = {
allowed: boolean;
reason: string;
decisionId?: string;
matchedPolicy?: string;
};
Prosedur
flowchart LR
Req[Request] --> Ctx[Tenant context + SET app.current_tenant_id] --> Eval[Evaluate ABAC] --> D{allowed?}
D -- Tidak --> Log[Decision log jika high-risk] --> Deny[403 ACCESS_DENIED]
D -- Ya --> Next[Lanjut ke validasi/service]
Aturan implementasi
- Guard HANYA pada
action yang DI-SEED di awcms_permissions. Katalog
permission disemai lewat migrasi sql/* (mis. sql/005 untuk access_control
= read/assign/configure, office_management = read/create/update).
Owner role di-grant SELURUH baris awcms_permissions saat bootstrap
(platform-bootstrap.ts SELECT id FROM awcms_permissions), dan jalur e2e =
migrasi → POST /setup/initialize TANPA module permission-sync di antaranya.
Jadi guard pada action yang tidak ter-seed men-DENY bahkan owner (403) —
dan ini LATENT: e2e admin env-gated sering ter-skip di CI kosong → hijau
padahal rusak. Sebelum menulis guard baru: pakai action yang sudah ter-seed
untuk aktivitas itu (mis. administrasi role/policy → configure, assign role →
assign), ATAU tambah action lewat migrasi seed baru (INSERT ... ON CONFLICT DO NOTHING; migrasi terapan immutable — jangan edit sql/005).
Deklarasi di module.ts permissions[] tidak cukup — itu bukan baris
katalog DB saat bootstrap (Issue #171). Cocokkan gate UI SSR dengan action
guard endpoint yang sama.
- Set tenant context di awal transaction:
SET app.current_tenant_id = ....
- Query tenant-scoped wajib filter
tenant_id (jangan hanya andalkan RLS).
- Role sistem (
is_system) itu invarian: tolak soft-delete, grant/revoke
permission, dan assign/unassign role sistem via API (mirror softDeleteRole),
dan jangan biarkan admin dinonaktifkan sampai tak ada admin aktif tersisa —
jika tidak, holder permission terdelegasi bisa eskalasi/mengunci tenant
(Issue #171 review).
- Query resource soft-deletable default
deleted_at IS NULL; includeDeleted, restore, dan purge wajib ABAC eksplisit.
- Contoh batas peran: operator ditolak akses pajak/export/assign role; cross-tenant selalu blocked.
tenantUserId/identityId berasal dari auth middleware, bukan header public mentah.
- Layar admin write-form memakai
sendJson/postJson/lockElement
(src/lib/ui/admin-form-client.ts, skill awcms-ui-screen) untuk
memanggil endpoint mutation — pastikan gate/permission yang dicek untuk
menampilkan tombol/form itu adalah action yang SAMA dan SUDAH ter-seed
di awcms_permissions (aturan di atas), bukan action yang "kelihatan
benar" tapi belum ter-seed — kalau salah, tombolnya tampil tapi request-nya
403 bahkan untuk owner.
Verifikasi (test)
- default deny; deny overrides allow; cashier limit; tax officer access; cross-tenant blocked; archive/restore denied tanpa permission; decision log tercatat.