Files
web-antrean/docs/hakakses implementation_plan_v2.md
T

10 KiB

Integrasi Hak Akses (Permissions) di UI beserta Konfigurasi CRUD

Hasil Pengecekan Kesiapan Project

  1. Navigasi (Menu/Routing): Sudah tersedia di stores/navItems1.ts (defaultNavItems). Array ini akan menjadi sumber daftar halaman di dialog "Edit Hak Akses" — dengan catatan penting di bagian struktur data (lihat poin 2 di bawah).
  2. Library Komponen: Vuetify 3 sudah tersedia, tinggal memasang <v-checkbox> / <v-switch> di tabel dialog Edit Hak Akses.
  3. Mock Backend API: server/api/hak-akses/index.ts sudah bisa menahan format data CRUD yang akan dikirim dari UI.

Karena API backend sesungguhnya belum siap, kita pakai Nuxt Local API (Mock Backend) sebagai Backend-for-Frontend sementara, dengan arsitektur toggling API sejak awal agar migrasi ke backend asli mulus.

⚠️ Catatan penting: Toggle mock/real API ini menyelesaikan masalah sumber data, bukan masalah keamanan. Semua pengecekan v-permission dan middleware di bawah ini berjalan di client — lihat bagian Keamanan sebelum dianggap selesai.


Bagan Alur Sistem Hak Akses (Permission Flow)

flowchart TD
    User([Pengguna]) --> Login(Berhasil Login via Keycloak)
    Login --> Auth[useAuth.ts: Dapat Data Roles & Groups]
    Auth --> PermStore[permissionStore.ts memuat Permissions]

    PermStore --> CheckFlag{USE_MOCK_API ?}

    CheckFlag -- TRUE --> MockAPI[Nuxt Local API: /api/hak-akses]
    CheckFlag -- FALSE --> RealAPI[Real Backend: /api/v1/permission]

    MockAPI --> FetchFail{Fetch gagal?}
    RealAPI --> FetchFail
    FetchFail -- Ya --> DenyDefault[Deny-by-default: anggap tanpa izin]
    FetchFail -- Tidak --> PermState(Permission State Disimpan)

    PermState --> Router[Vue Router Middleware - client]
    PermState --> ServerCheck[Server Middleware/Plugin - SSR guard]
    PermState --> Sidebar[Sidebar Navigation]

    Router -- canAccess: False --> Deny[Redirect ke Error/403]
    Router -- canAccess: True --> Page[Buka Halaman]
    ServerCheck -- canAccess: False --> Deny

    Sidebar -- canView: False --> HideMenu[Sembunyikan Menu]

    Page --> Directive[v-permission directive pada Komponen]
    Directive -- canDelete: False --> HideBtn[Tombol Hapus Hilang/Disabled]
    Directive -- canAdd: True --> ShowBtn[Tombol Tambah Tampil]

    Page -.-> BackendGuard[[Backend API tetap validasi ulang izin]]

Proposed Changes

1. Seamless Backend Migration Strategy (Persiapan API Asli)

  • stores/permissionStore.ts dan composable pemanggil membaca konfigurasi runtime (useRuntimeConfig()).
  • Tambahkan flag USE_MOCK_PERMISSION_API: true.
  • Saat backend siap, cukup ubah NUXT_PUBLIC_USE_MOCK_PERMISSION_API=false di .env.
  • Tambahan — contract test: buat satu skema/interface TypeScript (idealnya divalidasi dengan zod) yang dipakai bersama oleh mock API dan dipakai untuk memvalidasi response real API nanti. Ini memastikan klaim "tinggal ganti flag" benar-benar teruji, bukan asumsi.

2. Upgrade Local API Mock (server/api/hak-akses/index.ts)

  • Perbarui data/mock/hakAkses.json agar field hakAksesMenu berisi boolean: canAccess, canView, canAdd, canEdit, canDelete.
  • Perubahan struktur — pakai key stabil, bukan label: setiap entri hakAksesMenu menyimpan menuKey (mengacu ke key/routeName unik di navItems1.ts), bukan name (label tampilan). Label bisa berubah/di-rename tanpa memutus mapping izin.
{
  "menuKey": "master-klinik-ruang",
  "name": "Master Klinik Ruang",
  "canAccess": true,
  "canView": true,
  "canAdd": false,
  "canEdit": false,
  "canDelete": false
}
  • Validasi payload masuk dengan skema (zod) di endpoint mock, supaya struktur tidak diam-diam berubah antara UI dan backend.

3. Memperbarui Setting/HakAkses.vue (UI Konfigurasi CRUD)

  • Dialog Edit melooping navItems1.ts, menampilkan matriks checkbox untuk canAccess, canView, canAdd, canEdit, canDelete, dikunci ke menuKey masing-masing.
  • Payload POST mengarah ke API Mock/Real sesuai flag.
  • Aturan precedence eksplisit (harus didefinisikan sebelum coding, karena ada role, group, dan isGroupBased sekaligus):
    1. Jika user punya override individual (role-based) → pakai itu.
    2. Jika tidak ada override individual dan isGroupBased: true → pakai izin dari group.
    3. Jika keduanya tidak ada → deny-by-default.
    • Tuliskan aturan ini sebagai komentar di permissionStore.ts, bukan hanya di dokumen, supaya tidak jadi sumber bug tersembunyi saat logic berkembang.

4. Vue Custom Directive v-permission

  • File baru plugins/permission.ts untuk registrasi directive.
  • Dukungan penggunaan:
    • Single: <v-btn v-permission="'canEdit'">Edit</v-btn>
    • Multiple (AND): <v-btn v-permission="['canEdit', 'canDelete']">...</v-btn>
  • Mode hide vs disable: tambahkan modifier, misal v-permission:disable="'canDelete'", agar tombol bisa di-disable dengan tooltip ("Anda tidak punya izin") alih-alih hilang total tanpa penjelasan — pilih sesuai konteks UX per halaman.

5. stores/permissionStore.ts (State Management)

  • Mengambil data izin dari Local/Real API saat login.
  • Menyimpan state global.
  • Deny-by-default: jika fetch permission gagal (network error, token expired), state dianggap "tanpa izin sama sekali", bukan default terbuka.
  • Refresh strategy: tentukan apakah perubahan hak akses oleh admin berlaku langsung (polling/refetch berkala) atau baru berlaku setelah re-login. Pilih salah satu secara eksplisit dan dokumentasikan, jangan dibiarkan implisit.

6. Middleware & Dynamic Sidebar

  • Client middleware (middleware/permissions.ts): mencegat rute jika canAccess false.
  • Server-side guard: karena project ini SSR (Nuxt), tambahkan pengecekan di server middleware/plugin juga — bukan hanya client — untuk mencegah flash of unauthorized content (halaman sempat ter-render sebelum redirect).
  • Sidebar (stores/navItems1.ts): filter menu berdasarkan canView, dikunci ke menuKey.

7. Keamanan Backend (wajib, non-negotiable)

  • v-permission dan middleware di atas adalah UX, bukan kontrol akses sesungguhnya — keduanya berjalan di client dan bisa dilewati siapa saja yang memanggil API langsung.
  • Backend API asli wajib memvalidasi ulang setiap permission di server berdasarkan identitas user dari token, tidak pernah mempercayai payload/izin yang dikirim dari frontend.

Verification Plan

1. Verifikasi API Lokal via cURL

Pastikan API Lokal mampu membaca dan menyimpan JSON berformat CRUD dengan menuKey:

curl -X POST "http://localhost:3000/api/hak-akses" \
  -H "Content-Type: application/json" \
  -d '{
    "role": "admin",
    "group": "LOKET",
    "namaTipeUser": "Admin Loket",
    "isGroupBased": true,
    "hakAksesMenu": [
      {
        "menuKey": "master-klinik-ruang",
        "name": "Master Klinik Ruang",
        "canAccess": true,
        "canView": true,
        "canAdd": false,
        "canEdit": false,
        "canDelete": false
      }
    ]
  }'

2. Manual UI Verification

  1. Buka Setting > Hak Akses, atur hak untuk Role tertentu (centang canView, matikan canDelete).
  2. Login sebagai akun dengan Role tersebut.
  3. Buka halaman target.
  4. Validasi: Halaman terbuka, tombol "Delete" tersembunyi/disabled sesuai mode v-permission.
  5. Validasi fetch gagal: Simulasikan permission API error (mis. matikan endpoint sementara) → pastikan sistem deny-by-default, bukan default terbuka.
  6. Validasi kesiapan API asli: Cek logika precedence dan toggle flag di permissionStore.ts.

3. Automated Testing (baru)

  • Unit test untuk permissionStore.ts: precedence role vs group, deny-by-default saat fetch gagal, evaluasi canAccess/canView/dst.
  • Contract test: bandingkan skema response Mock API vs Real API (setelah Real API tersedia) menggunakan skema TypeScript/zod yang sama, untuk memastikan switch flag benar-benar tanpa perubahan kode lain.
  • Directive test: pastikan v-permission menyembunyikan/disable elemen dengan benar untuk kombinasi single dan multiple permission.

Strategi Selama Backend Asli Belum Tersedia

Karena tim belum bisa mengimplementasikan validasi izin di server sungguhan, mock API diperlakukan sebagai kontrak (contract-first), bukan sekadar penyimpanan data sementara. Tujuannya: frontend sudah teruji terhadap semua skenario yang nanti jadi tanggung jawab backend asli, dan tidak perlu dirombak saat migrasi.

1. Mock API mengikuti skema, bukan menerima apa saja

  • Definisikan interface TypeScript / skema zod untuk request dan response hak-akses sekarang, bukan menunggu backend asli.
  • Mock API menolak (400) payload yang tidak sesuai skema.
  • Skema ini menjadi kontrak yang wajib dipatuhi backend asli nanti — perbedaan struktur akan ketahuan lewat contract test, bukan saat production.

2. Simulasikan tanggung jawab yang nanti dipegang backend

  • Tambahkan endpoint mock /api/hak-akses/check yang bisa mensimulasikan penolakan server (403) karena user tidak punya izin — supaya UI dan middleware sudah teruji menangani penolakan dari server, bukan hanya dari state client.
  • Simulasikan juga kegagalan fetch (delay/error 500) untuk memverifikasi deny-by-default benar-benar berjalan.

3. Tandai eksplisit bagian yang "sementara tidak aman"

Beri komentar TODO(security) di titik-titik yang wajib diperkuat saat backend asli terpasang, contoh:

// TODO(security): saat backend asli terpasang, endpoint ini WAJIB
// memvalidasi ulang permission dari token JWT/session di server,
// jangan percaya payload role/group yang dikirim dari client.

Ini mencegah asumsi keliru saat handoff bahwa "karena UI sudah mengatur tampilan sesuai izin, backend tidak perlu memvalidasi ulang".

4. Definition of Done — Migrasi ke Backend Asli

Checklist ini harus tercentang semua sebelum flag USE_MOCK_PERMISSION_API dimatikan (false) di production:

  • Endpoint real API memvalidasi permission berdasarkan identitas dari token (JWT/session), bukan dari body request yang dikirim client.
  • Response real API lolos contract test terhadap skema yang sama dengan mock API (field, tipe data, struktur hakAksesMenu identik).
  • Skenario penolakan server (403) dan fetch gagal (500/timeout) sudah diuji terhadap real API, tidak hanya terhadap mock.
  • Ada audit log untuk setiap perubahan hak akses (siapa mengubah, kapan, dari-ke apa) — direkomendasikan mengingat ini fitur kontrol akses.
  • Rate limiting pada endpoint pengubahan hak akses, untuk mencegah penyalahgunaan.
  • Semua komentar TODO(security) di kode sudah diselesaikan atau dipindahkan menjadi tiket tersendiri yang dilacak.