commit
This commit is contained in:
221
docs/nav-rbac.md
Normal file
221
docs/nav-rbac.md
Normal file
@@ -0,0 +1,221 @@
|
||||
# Navigation and RBAC
|
||||
|
||||
## Overview
|
||||
|
||||
This repository uses an Auth.js-based access model with server-side authorization as the real security boundary.
|
||||
|
||||
Navigation filtering exists for usability only. It helps show the right menus to the right users, but it is not the thing that protects data. Real protection is enforced in:
|
||||
|
||||
- `src/proxy.ts`
|
||||
- page guards under `src/lib/auth/page-guards.ts`
|
||||
- session helpers under `src/lib/auth/session.ts`
|
||||
- route handlers under `src/app/api/**`
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### User
|
||||
|
||||
Authenticated person from Auth.js session.
|
||||
|
||||
### Organization
|
||||
|
||||
The active tenant/workspace from `session.user.activeOrganizationId`.
|
||||
|
||||
### Membership
|
||||
|
||||
The user's relation to the active organization. This is loaded server-side and includes:
|
||||
|
||||
- `role`
|
||||
- `permissions`
|
||||
|
||||
### Business Role
|
||||
|
||||
The UI and feature model mainly uses:
|
||||
|
||||
- `HRD`
|
||||
- `EMPLOYEE`
|
||||
|
||||
Business role is derived from session and membership state through `getBusinessRole()` in [src/lib/auth/roles.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/roles.ts).
|
||||
|
||||
## Security Boundaries
|
||||
|
||||
### 1. Proxy route protection
|
||||
|
||||
[src/proxy.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/proxy.ts) blocks unauthenticated access to:
|
||||
|
||||
- `/dashboard/**`
|
||||
- protected API namespaces such as:
|
||||
- `/api/training-records/**`
|
||||
- `/api/notifications/**`
|
||||
- `/api/announcements/**`
|
||||
- `/api/audit-logs/**`
|
||||
- other protected modules
|
||||
|
||||
This ensures unauthenticated users cannot directly browse protected pages or call protected APIs.
|
||||
|
||||
### 2. Server-side session helpers
|
||||
|
||||
[src/lib/auth/session.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/session.ts) is the main authorization entry point.
|
||||
|
||||
Important helpers:
|
||||
|
||||
- `requireSession()`
|
||||
- `requireOrganizationAccess()`
|
||||
- `requireHRD()`
|
||||
- `requireEmployee()`
|
||||
|
||||
These helpers:
|
||||
|
||||
- validate authentication
|
||||
- validate active organization context
|
||||
- verify membership
|
||||
- derive role-based access
|
||||
- return organization-scoped access data for downstream queries
|
||||
|
||||
### 3. Page-level guards
|
||||
|
||||
[src/lib/auth/page-guards.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/page-guards.ts) handles protected dashboard navigation and redirect behavior.
|
||||
|
||||
Important guards:
|
||||
|
||||
- `requireEmployeeDashboardAccess()`
|
||||
- `requireHRDDashboardAccess()`
|
||||
|
||||
These are used by dashboard pages so direct URL access is also protected, not only sidebar visibility.
|
||||
|
||||
### 4. Route handler authorization
|
||||
|
||||
Every sensitive route handler must still verify organization and role server-side.
|
||||
|
||||
Examples:
|
||||
|
||||
- employee-owned training data is scoped by `organizationId` and `userId`
|
||||
- HRD-only actions use `requireHRD()`
|
||||
- review and audit routes validate higher privilege before returning data
|
||||
|
||||
## Navigation Filtering
|
||||
|
||||
Sidebar and kbar filtering are driven by:
|
||||
|
||||
- [src/config/nav-config.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/config/nav-config.ts)
|
||||
- [src/hooks/use-nav.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/hooks/use-nav.ts)
|
||||
|
||||
Supported visibility checks in nav items include:
|
||||
|
||||
- `requireOrg`
|
||||
- `systemRole`
|
||||
- `role`
|
||||
- `permission`
|
||||
- `businessRole`
|
||||
|
||||
This filtering is based on the current Auth.js session in the client.
|
||||
|
||||
Important: hiding a menu item does not grant or remove backend access. It only controls what the user sees in the UI.
|
||||
|
||||
## Role-Based Menu Behavior
|
||||
|
||||
Current practical behavior:
|
||||
|
||||
### Employee
|
||||
|
||||
Employee users can see employee-facing areas such as:
|
||||
|
||||
- Dashboard
|
||||
- Training Records
|
||||
- Announcements
|
||||
- Notifications
|
||||
|
||||
Employee access is still server-scoped to their own allowed data.
|
||||
|
||||
### HRD
|
||||
|
||||
HRD users can access broader organization operations such as:
|
||||
|
||||
- Pending Review
|
||||
- Employees
|
||||
- Courses
|
||||
- Training Policy
|
||||
- Import Employees
|
||||
- Reports
|
||||
- Audit Logs
|
||||
- Master Review
|
||||
|
||||
These pages also require server-side HRD validation, not only visible nav items.
|
||||
|
||||
### Super Admin
|
||||
|
||||
Super admin users can access system-level organizer management and can be elevated into organization context when required by server-side helpers.
|
||||
|
||||
## Direct URL Protection
|
||||
|
||||
The system explicitly protects direct navigation to pages even if a user manually enters a URL.
|
||||
|
||||
Examples:
|
||||
|
||||
- unauthenticated users are redirected at the proxy layer
|
||||
- authenticated but unauthorized users are redirected by page guards
|
||||
- API routes return `401` or `403` from server-side helpers when access is invalid
|
||||
|
||||
This prevents bypass through copied links or bookmarked URLs.
|
||||
|
||||
## API Protection
|
||||
|
||||
API protection follows these rules:
|
||||
|
||||
1. Authentication is required through Auth.js session
|
||||
2. Organization membership is verified server-side
|
||||
3. Role-specific actions require additional server checks
|
||||
4. Data queries are scoped by organization and, when needed, by the current user
|
||||
|
||||
This is the main security boundary for all business data.
|
||||
|
||||
## File Download Protection
|
||||
|
||||
Protected files must not be served only by direct public URLs.
|
||||
|
||||
Current protected examples:
|
||||
|
||||
- training certificates:
|
||||
- `/api/training-records/[id]/certificates/[certificateId]/download`
|
||||
- announcement attachments:
|
||||
- `/api/announcements/[id]/attachment`
|
||||
|
||||
These endpoints verify:
|
||||
|
||||
- authentication
|
||||
- organization scope
|
||||
- record-level access
|
||||
- HRD/employee visibility rules
|
||||
|
||||
This prevents unauthorized users from opening files just because they know or guess the storage path.
|
||||
|
||||
## Recommended Maintenance Rules
|
||||
|
||||
When adding or changing protected features:
|
||||
|
||||
1. Add navigation visibility only after server-side access rules are defined
|
||||
2. Use `requireOrganizationAccess()` for organization-scoped routes
|
||||
3. Use `requireHRD()` for HRD-only operations
|
||||
4. Use page guards for dashboard routes
|
||||
5. Protect file downloads through route handlers when files are sensitive
|
||||
6. Treat `use-nav.ts` as UX logic, not as a security mechanism
|
||||
|
||||
## What To Avoid
|
||||
|
||||
Do not reintroduce:
|
||||
|
||||
- Clerk-specific authorization assumptions
|
||||
- client-side-only protection for sensitive features
|
||||
- public file URLs for protected business documents
|
||||
- page access that depends only on hidden sidebar items
|
||||
|
||||
## Summary
|
||||
|
||||
The current model is:
|
||||
|
||||
- Auth.js for authentication
|
||||
- proxy-based route gating for unauthenticated requests
|
||||
- server-side authorization helpers for real access control
|
||||
- client-side nav filtering for usability
|
||||
|
||||
That combination keeps the UI predictable while ensuring that direct URLs, APIs, and file downloads are protected at the server boundary.
|
||||
Reference in New Issue
Block a user