Lewati ke konten

Model Keamanan

Catera memberlakukan otorisasi di dua lapis independen: lapis frontend (mencegah navigasi ke halaman yang tidak relevan) dan lapis database (mencegah mutasi data lewat Row Level Security dan RPC, terlepas dari apa yang ditampilkan UI). Keduanya harus diasumsikan bisa dilewati sendiri- sendiri — UI tidak boleh jadi satu-satunya penjaga.

Role kanonik Alias lama yang dipetakan
pemilik owner, admin
manager staff
kasir —
produksi staff_produksi
kurir driver
staf —
gudang pengemasan

Pemetaan alias ke role kanonik didefinisikan dua kali dengan kontrak yang sama — di frontend (normalizeUserRole(), src/lib/roles.ts) dan di database (permission_normalized_role(), supabase/migrations/0075_role_permissions.sql) — agar keputusan akses konsisten walau diambil dari sisi mana pun.

RouteGuard menolak render halaman jika role (setelah dinormalisasi) tidak ada di daftar ROUTE_ACCESS untuk path tersebut, dan mengarahkan pengguna ke halaman landing sesuai rolenya. Ini murni soal pengalaman navigasi — mencegah pengguna “nyasar” ke halaman yang tidak relevan dengan tugasnya. Lihat matriks lengkapnya di Matriks Peran & Hak Akses.

Lapis 2 — Row Level Security & RPC di Postgres

Section titled “Lapis 2 — Row Level Security & RPC di Postgres”

Setiap tabel sensitif mengaktifkan enable row level security. Alih-alih menulis kondisi role berulang di tiap policy, Catera memusatkan pengecekan ke fungsi security definer yang dipanggil dari dalam policy maupun dari RPC mutasi:

  • is_owner() — benar jika profil aktif dengan role pemilik/owner/admin.
  • is_active_user() — benar jika profil berstatus active dan role bukan pending.
  • can_manage_<domain>() — satu fungsi per domain (mis. can_manage_hr(), can_manage_finance(), can_manage_inventory()) yang mengecek role mana saja yang boleh menulis ke domain itu. RPC mutasi domain tersebut memanggil fungsi ini di awal dan menolak dengan raise exception jika gagal — bukan mengandalkan RLS policy saja.

Pola ini membuat hak akses tetap berlaku walau request datang langsung ke Supabase API, bukan lewat UI aplikasi.

Lapis tambahan — override permission per-role (dikonfigurasi Owner)

Section titled “Lapis tambahan — override permission per-role (dikonfigurasi Owner)”

Di atas role kanonik, ada satu lapis konfigurasi halus: public.role_permission_overrides menyimpan toggle boolean per-role (mis. menonaktifkan satu kemampuan spesifik untuk role kasir tanpa mengubah kode). Hanya pemilik yang bisa membaca/mengubahnya, lewat RPC:

  • permission_my_overrides() — role yang sedang login membaca override-nya sendiri.
  • permission_all_overrides() / permission_save_overrides() — khusus pemilik, untuk melihat dan mengubah override semua role.
  • Setiap perubahan otomatis tercatat di public.permission_audit_logs (siapa, kapan, field apa yang berubah dari nilai apa ke apa), dibaca lewat permission_logs_list().

User Auth baru otomatis mendapat baris profiles berstatus pending lewat trigger on_auth_user_created — tidak ada role aktif sampai pemilik/manager menyetujuinya secara manual. Status blocked memutus akses tanpa menghapus riwayat data milik user tersebut.


Sumber: supabase/migrations/0002_identity_and_rls.sql, supabase/migrations/0007_harden_profile_authorization.sql, supabase/migrations/0032_employee_attendance_domain.sql, supabase/migrations/0075_role_permissions.sql, src/lib/roles.ts, src/lib/role-access.ts, src/components/RouteGuard.tsx.