- Introduced a new `PrivilegesModule` to manage user privileges and access control. - Added `RequirePrivilege` decorator to enforce privilege checks on controller handlers. - Implemented `PrivilegesGuard` to handle authorization based on user privileges. - Created database migrations for `privileges`, `privilege_keys`, and `privilege_details` tables. - Updated user model to include `is_superadmin` field for enhanced access control. - Added unit tests for the new privileges functionality and guards to ensure correct behavior.
38 lines
1.8 KiB
Plaintext
38 lines
1.8 KiB
Plaintext
---
|
||
description: Primary modules must use @RequirePrivilege on every non-public handler; privilege_keys seeded per module
|
||
globs: "src/modules/**/*.ts,src/common/decorators/**/*.ts,src/common/guards/**/*.ts"
|
||
alwaysApply: false
|
||
---
|
||
|
||
# Privileges Authorization
|
||
|
||
## Mandatory
|
||
|
||
Every **non-public** controller handler on a primary (CRUD) resource MUST use:
|
||
|
||
```typescript
|
||
@RequirePrivilege('MODULE.RESOURCE', 'view' | 'create' | 'update' | 'delete' | 'import')
|
||
```
|
||
|
||
Map HTTP verbs to actions:
|
||
|
||
| Handler | Action |
|
||
| ------- | ------ |
|
||
| `GET` list / detail | `view` |
|
||
| `POST /` create | `create` |
|
||
| `PATCH /:id`, status, bulk-status | `update` |
|
||
| `DELETE /:id`, bulk-delete | `delete` |
|
||
| `POST /import` | `import` |
|
||
|
||
Key codes use dotted uppercase module levels (`PRIVILEGES`, `SALES.INVOICE`). New modules add a `privilege_keys` seed row via migration — do not invent a parallel permission helper.
|
||
|
||
Seed an Administrator privilege only via SQL/ops after the first user exists (`created_by` requires a user). Documented bootstrap: insert privilege + details, then `UPDATE users SET privilege_id = …`. Do not auto-grant on register.
|
||
|
||
## Guard behavior
|
||
|
||
`PrivilegesGuard` (global) allows when there is no metadata. When metadata is present, `users.is_superadmin === true` skips the matrix check. Otherwise the user’s assigned privilege must be **status `active`** and the matrix cell must be `value === true`, or the request is `403 Forbidden`. Missing privilege / draft / archived / missing cell / `false` → deny.
|
||
|
||
Do not set `is_superadmin` via register/login. Default is `false`; promote via SQL/ops (`UPDATE users SET is_superadmin = true`). The flag is loaded from the database on each JWT validation (not from JWT claims).
|
||
|
||
Primary tables still use `primaryEntityColumns(users)` (`status`, audit timestamps, `created_by` / `updated_by`).
|