303 lines
7.3 KiB
Markdown
303 lines
7.3 KiB
Markdown
# Implementasi Hak Akses di Sidebar & Pages
|
|
|
|
## Overview
|
|
|
|
Sistem hak akses sekarang sudah terintegrasi dengan sidebar dan page routing. Menu sidebar akan otomatis difilter berdasarkan pages yang user miliki akses, dan middleware akan mencegah akses langsung ke pages yang tidak diizinkan.
|
|
|
|
## Komponen yang Terbuat
|
|
|
|
### 1. Composable `useHakAkses()` ([composables/useHakAkses.ts](composables/useHakAkses.ts))
|
|
|
|
Menyediakan fungsi untuk mengecek akses user ke halaman:
|
|
|
|
```typescript
|
|
const { getAllowedPages, hasPageAccess, hasAnyPageAccess } = useHakAkses();
|
|
|
|
// Get all pages user can access
|
|
const pages = await getAllowedPages();
|
|
|
|
// Check single page
|
|
const canAccess = await hasPageAccess('/dashboard');
|
|
|
|
// Check multiple pages
|
|
const hasAccess = await hasAnyPageAccess(['/antrean/all', '/dashboard']);
|
|
```
|
|
|
|
### 2. Sidebar dengan Filter ([components/layout/full/vertical-sidebar/VerticalSidebar.vue](components/layout/full/vertical-sidebar/VerticalSidebar.vue))
|
|
|
|
- ✅ Auto load allowed pages saat mount
|
|
- ✅ Filter menu items berdasarkan allowed pages
|
|
- ✅ Hanya tampilkan section yang punya minimal 1 menu visible
|
|
- ✅ Log allowed pages untuk debugging
|
|
|
|
### 3. Middleware Protection ([middleware/checkPageAccess.ts](middleware/checkPageAccess.ts))
|
|
|
|
- ✅ Cek akses user ke page yang dituju
|
|
- ✅ Redirect ke dashboard atau first allowed page jika tidak punya akses
|
|
- ✅ Skip check untuk public pages (login, register)
|
|
|
|
## Cara Kerja
|
|
|
|
### Flow Akses User:
|
|
|
|
1. **Login** → User mendapat roles dari Keycloak
|
|
2. **Auto-sync** → Roles di-sync ke `hakAkses.json`
|
|
3. **Aktifkan hakAkses** → Admin mengaktifkan hakAkses dan set pages di UI
|
|
4. **Load Sidebar** → Sidebar fetch allowed pages dan filter menu
|
|
5. **Navigation** → Middleware check setiap navigation ke page baru
|
|
|
|
### Data Structure:
|
|
|
|
```json
|
|
// hakAkses.json
|
|
{
|
|
"id": "uuid",
|
|
"namaHakAkses": "manage-account", // nama role dari Keycloak
|
|
"status": "aktif", // harus aktif untuk memberikan akses
|
|
"pages": [
|
|
"/dashboard",
|
|
"/antrean/all",
|
|
"/setting/hak-akses"
|
|
]
|
|
}
|
|
```
|
|
|
|
## Penggunaan
|
|
|
|
### 1. Mengaktifkan Middleware di Page
|
|
|
|
Ada 2 cara:
|
|
|
|
#### Option A: Per Page (Recommended)
|
|
```vue
|
|
<script setup lang="ts">
|
|
definePageMeta({
|
|
middleware: ['auth', 'check-page-access']
|
|
});
|
|
</script>
|
|
```
|
|
|
|
#### Option B: Global di nuxt.config.ts
|
|
```typescript
|
|
export default defineNuxtConfig({
|
|
router: {
|
|
middleware: ['auth', 'check-page-access']
|
|
}
|
|
})
|
|
```
|
|
|
|
### 2. Manual Check di Component
|
|
|
|
Untuk show/hide content berdasarkan akses:
|
|
|
|
```vue
|
|
<script setup lang="ts">
|
|
const { hasPageAccess } = useHakAkses();
|
|
const canAccessSettings = ref(false);
|
|
|
|
onMounted(async () => {
|
|
canAccessSettings.value = await hasPageAccess('/setting/hak-akses');
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<v-btn v-if="canAccessSettings" to="/setting/hak-akses">
|
|
Settings
|
|
</v-btn>
|
|
</template>
|
|
```
|
|
|
|
### 3. Conditional Rendering dengan Allowed Pages
|
|
|
|
```vue
|
|
<script setup lang="ts">
|
|
const { getAllowedPages } = useHakAkses();
|
|
const allowedPages = ref<string[]>([]);
|
|
|
|
onMounted(async () => {
|
|
allowedPages.value = await getAllowedPages();
|
|
});
|
|
|
|
const canManageUsers = computed(() =>
|
|
allowedPages.value.includes('/setting/user')
|
|
);
|
|
</script>
|
|
|
|
<template>
|
|
<div v-if="canManageUsers">
|
|
<!-- User management content -->
|
|
</div>
|
|
</template>
|
|
```
|
|
|
|
## Mengelola Hak Akses
|
|
|
|
### 1. Menambah Pages ke Role
|
|
|
|
Di halaman **Hak Akses** (`/setting/hak-akses`):
|
|
|
|
1. Pilih role yang ingin diedit
|
|
2. Set status menjadi **Aktif**
|
|
3. Pilih pages yang boleh diakses
|
|
4. Simpan
|
|
|
|
### 2. Format Path di Pages Array
|
|
|
|
Path harus **exact match** dengan route path:
|
|
|
|
✅ **Benar:**
|
|
- `/dashboard`
|
|
- `/antrean/all`
|
|
- `/setting/hak-akses`
|
|
|
|
❌ **Salah:**
|
|
- `dashboard` (tanpa slash)
|
|
- `/dashboard/` (dengan trailing slash)
|
|
- `/antrean/*` (wildcard tidak support)
|
|
|
|
### 3. Testing Hak Akses
|
|
|
|
1. Login dengan user yang punya role tertentu
|
|
2. Cek console log: `Allowed pages for user: [...]`
|
|
3. Sidebar akan otomatis filter
|
|
4. Coba akses URL langsung yang tidak ada di allowed pages
|
|
5. Harus redirect ke dashboard atau first allowed page
|
|
|
|
## Troubleshooting
|
|
|
|
### Menu Tidak Muncul di Sidebar
|
|
|
|
**Penyebab:**
|
|
1. Status hakAkses masih **tidak aktif**
|
|
2. Path di pages array tidak match dengan `to` di sidebarItem.ts
|
|
3. User tidak punya role yang sesuai
|
|
|
|
**Solusi:**
|
|
```typescript
|
|
// Check di console
|
|
const { getAllowedPages } = useHakAkses();
|
|
const pages = await getAllowedPages();
|
|
console.log('Allowed pages:', pages);
|
|
|
|
// Check role user
|
|
const { getRoles } = useRoles();
|
|
console.log('User roles:', getRoles());
|
|
```
|
|
|
|
### User Bisa Akses Page Tanpa Permission
|
|
|
|
**Penyebab:**
|
|
- Middleware belum ditambahkan di page
|
|
|
|
**Solusi:**
|
|
```vue
|
|
<script setup lang="ts">
|
|
definePageMeta({
|
|
middleware: ['auth', 'check-page-access'] // Tambahkan ini
|
|
});
|
|
</script>
|
|
```
|
|
|
|
### Error "You do not have access to any pages"
|
|
|
|
**Penyebab:**
|
|
- Semua hakAkses user statusnya **tidak aktif**
|
|
- Pages array kosong di semua hakAkses user
|
|
|
|
**Solusi:**
|
|
1. Buka `/setting/hak-akses`
|
|
2. Edit hakAkses yang sesuai dengan role user
|
|
3. Set status **Aktif**
|
|
4. Tambahkan minimal `/dashboard` di pages
|
|
5. Simpan
|
|
|
|
## Best Practices
|
|
|
|
### 1. Default Pages untuk Semua Role
|
|
|
|
Pastikan semua role aktif punya akses ke:
|
|
- `/dashboard` - Landing page setelah login
|
|
|
|
### 2. Hierarchical Access
|
|
|
|
Jika user punya akses ke child path, berikan juga akses ke parent:
|
|
```json
|
|
{
|
|
"pages": [
|
|
"/setting", // Parent
|
|
"/setting/hak-akses", // Child
|
|
"/setting/user" // Child
|
|
]
|
|
}
|
|
```
|
|
|
|
### 3. Testing Multiple Roles
|
|
|
|
Test dengan user yang punya multiple roles untuk pastikan combine pages works:
|
|
|
|
```typescript
|
|
// User dengan role: ['admin', 'dokter']
|
|
// hakAkses admin: ['/dashboard', '/setting/user']
|
|
// hakAkses dokter: ['/dashboard', '/antrean/all']
|
|
// Result: ['/dashboard', '/setting/user', '/antrean/all'] ✅
|
|
```
|
|
|
|
### 4. Sync Status
|
|
|
|
Selalu pastikan:
|
|
1. ✅ Role di Keycloak
|
|
2. ✅ Auto-sync ke hakAkses.json saat login
|
|
3. ✅ Status diaktifkan di UI hak akses
|
|
4. ✅ Pages ditambahkan
|
|
|
|
## API Endpoints
|
|
|
|
### Get All Hak Akses
|
|
```typescript
|
|
const response = await $fetch('/api/hak-akses');
|
|
// Returns all hakAkses with their pages
|
|
```
|
|
|
|
### Get User Allowed Pages (Client Side)
|
|
```typescript
|
|
const { getAllowedPages } = useHakAkses();
|
|
const pages = await getAllowedPages();
|
|
// Returns only pages from active hakAkses that match user's roles
|
|
```
|
|
|
|
## Security Notes
|
|
|
|
1. **Server-side validation** diperlukan untuk API calls yang sensitive
|
|
2. Middleware hanya **client-side protection** - jangan andalkan untuk security critical operations
|
|
3. Gunakan `requireRole()` atau `requireAnyRole()` di server API handlers untuk real protection
|
|
4. Pages array di hakAkses.json bisa diedit manual, tapi lebih baik via UI
|
|
|
|
## Example: Complete Setup
|
|
|
|
```typescript
|
|
// 1. User login dengan role: 'manage-account'
|
|
|
|
// 2. Auto-sync creates/updates hakAkses.json:
|
|
{
|
|
"namaHakAkses": "manage-account",
|
|
"status": "tidak aktif", // Default
|
|
"pages": []
|
|
}
|
|
|
|
// 3. Admin edit via UI:
|
|
{
|
|
"namaHakAkses": "manage-account",
|
|
"status": "aktif", // ← Changed
|
|
"pages": [
|
|
"/dashboard",
|
|
"/antrean/all",
|
|
"/setting/hak-akses"
|
|
] // ← Added
|
|
}
|
|
|
|
// 4. User refresh/re-login:
|
|
// - Sidebar shows: Dashboard, Antrean > Semua, Settings > Hak Akses
|
|
// - Can access: /dashboard, /antrean/all, /setting/hak-akses
|
|
// - Cannot access: /antrean/list-kategori, /setting/user (will redirect)
|
|
```
|