feat: add per-doctor permissions management in clinics
- Implement DoctorPermissionsModal for managing doctor permissions in clinics. - Create usePermissions hook to handle user permissions context. - Add migration for clinic_doctor_permissions table with default permissions. - Develop ClinicDoctorPermissionController for handling permissions API. - Create ClinicDoctorPermission entity to manage permissions data. - Implement ClinicDoctorPermissionRepository for database interactions. - Add ClinicDoctorPermissionChecker for permission validation logic. - Write tests for clinic doctor permissions functionality.
This commit is contained in:
+26
-2
@@ -287,8 +287,20 @@ Authorization: Bearer <token>
|
||||
"type": "clinic",
|
||||
"db_uuid": "clinic-uuid-...",
|
||||
"name": "کلینیک سلامت",
|
||||
"role": "clinic",
|
||||
"doctor_uuid": "a6ef5d29-38b8-4e69-b1ef-27a304696966"
|
||||
"role": "doctor",
|
||||
"scope": "clinic",
|
||||
"doctor_uuid": "a6ef5d29-38b8-4e69-b1ef-27a304696966",
|
||||
"permissions": {
|
||||
"version": 1,
|
||||
"resources": {
|
||||
"appointments": { "view": true, "create": true, "cancel": true, "update_status": true },
|
||||
"appointment_settings": { "view": true, "update": true },
|
||||
"patients": { "view": true, "create": true, "update": true, "delete": false },
|
||||
"payments": { "view": true, "create": false, "update": false, "delete": false },
|
||||
"services": { "view": true, "update": false },
|
||||
"clinic_info": { "view": true, "update": false }
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -304,6 +316,18 @@ Authorization: Bearer <token>
|
||||
| `context` | object\|null | context فعال انتخابشده |
|
||||
| `available_contexts` | array | همه محیطهای کاری قابل انتخاب |
|
||||
|
||||
**فیلد `permissions` در هر context:**
|
||||
|
||||
| حالت context | مقدار `permissions` |
|
||||
|---|---|
|
||||
| مطب شخصی پزشک (`type: doctor`، `role: doctor`) | `null` — محیط خودش، محدودیتی ندارد |
|
||||
| مالک کلینیک (`role: clinic`) | `null` — مالک هرگز محدود نمیشود |
|
||||
| پزشکِ عضو کلینیک (`role: doctor`، `scope: clinic`) | envelope کامل `{version, resources}` از `clinic_doctor_permissions` |
|
||||
| پزشکِ عضوی که دسترسیاش غیرفعال شده | `{version: 1, resources: {}}` — یعنی هیچ دسترسی |
|
||||
| منشی (`role: secretary`) | envelope کامل از `doctor_secretaries` |
|
||||
|
||||
نکتهٔ مهم برای کلاینت: **نبودِ `permissions` (یا `null`) یعنی «بدون محدودیت»، نه «بدون دسترسی».** ساختار و کلیدهای مجوز پزشکِ عضو کلینیک در `docs/api/clinic.md` → بخش *Clinic Doctor Permissions* آمده است.
|
||||
|
||||
**قانون `primary_role`** (اولویتبندی):
|
||||
- `ROLE_ADMIN` → `"admin"`
|
||||
- `ROLE_CLINIC` → `"clinic"`
|
||||
|
||||
+118
-2
@@ -159,7 +159,7 @@ Get clinic detail.
|
||||
|
||||
Update a clinic.
|
||||
|
||||
**Permission:** `AUTH` — must be the clinic owner or `ROLE_ADMIN`
|
||||
**Permission:** `AUTH` — the clinic owner, `ROLE_ADMIN`, or a member doctor holding `clinic_info.update` (see **Clinic Doctor Permissions**)
|
||||
|
||||
### Path Parameters
|
||||
| Param | Type | Description |
|
||||
@@ -314,7 +314,7 @@ Get doctors associated with a clinic.
|
||||
|
||||
## DELETE `/api/v1/admin/clinic/{clinicUuid}/doctor/{doctorUuid}`
|
||||
|
||||
Detach a doctor from a clinic. This removes the clinic↔doctor link only (the `clinic_doctors` association); it does **not** delete the doctor or change the doctor's own `active` appointment flag.
|
||||
Detach a doctor from a clinic. This removes the clinic↔doctor link (the `clinic_doctors` association) and the doctor's `clinic_doctor_permissions` row; it does **not** delete the doctor or change the doctor's own `active` appointment flag.
|
||||
|
||||
**Permission:** `AUTH` — the caller must be `ROLE_ADMIN` **or** the owner of this clinic (`ROLE_CLINIC` whose user owns `clinicUuid`). Any other authenticated user gets `403`.
|
||||
|
||||
@@ -338,6 +338,122 @@ Detach a doctor from a clinic. This removes the clinic↔doctor link only (the `
|
||||
|
||||
---
|
||||
|
||||
## Clinic Doctor Permissions
|
||||
|
||||
Each doctor attached to a clinic has a permission envelope scoped to **that clinic only** — the doctor's own practice is never affected. Rows live in `clinic_doctor_permissions` (one per clinic+doctor) and are created lazily with defaults for doctors who joined before this feature existed.
|
||||
|
||||
The envelope is always returned in full (`{version, resources}`); it is never flattened.
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"resources": {
|
||||
"appointments": { "view": true, "create": true, "cancel": true, "update_status": true },
|
||||
"appointment_settings": { "view": true, "update": true },
|
||||
"patients": { "view": true, "create": true, "update": true, "delete": false },
|
||||
"payments": { "view": true, "create": false, "update": false, "delete": false },
|
||||
"services": { "view": true, "update": false },
|
||||
"clinic_info": { "view": true, "update": false }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`active: false` revokes everything at once regardless of the individual flags. The clinic owner and `ROLE_ADMIN` bypass all checks and can never be locked out.
|
||||
|
||||
Unknown resources and unknown actions in a PATCH body are silently ignored, so a client cannot invent permission keys.
|
||||
|
||||
---
|
||||
|
||||
## GET `/api/v1/admin/clinic/{clinicUuid}/doctor-permissions`
|
||||
|
||||
List the permission rows of every doctor in the clinic.
|
||||
|
||||
**Permission:** `AUTH` — clinic owner or `ROLE_ADMIN`
|
||||
|
||||
### Response `200`
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"uuid": "ce200cde-826d-11f1-b923-c282b864cdcc",
|
||||
"clinic_uuid": "41e325c4-e825-4067-8438-5d828ecaee09",
|
||||
"doctor_uuid": "bcabb3a8-cae3-45ec-876c-548f9c1e1569",
|
||||
"doctor_name": "دکتر تست",
|
||||
"active": true,
|
||||
"permissions": { "version": 1, "resources": { "...": {} } },
|
||||
"created_at": 1784352916,
|
||||
"updated_at": 1784352916
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Errors
|
||||
| Code | HTTP | Description |
|
||||
|------|------|-------------|
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
| `ERR_ACCESS_DENIED` | 403 | Neither admin nor the clinic owner |
|
||||
| `ERR_NOT_FOUND_001` | 404 | Clinic not found |
|
||||
|
||||
---
|
||||
|
||||
## GET `/api/v1/admin/clinic/{clinicUuid}/doctor/{doctorUuid}/permissions`
|
||||
|
||||
Read one doctor's permissions. Creates the row with defaults if it does not exist yet.
|
||||
|
||||
**Permission:** `AUTH` — clinic owner or `ROLE_ADMIN`
|
||||
|
||||
### Path Parameters
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `clinicUuid` | string (UUID) | Clinic UUID |
|
||||
| `doctorUuid` | string (UUID) | Doctor UUID — must already be attached to this clinic |
|
||||
|
||||
### Response `200`
|
||||
Single permission object (same shape as one item of the list above).
|
||||
|
||||
### Errors
|
||||
| Code | HTTP | Description |
|
||||
|------|------|-------------|
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
| `ERR_ACCESS_DENIED` | 403 | Neither admin nor the clinic owner |
|
||||
| `ERR_NOT_FOUND_001` | 404 | Clinic not found, or doctor not attached to this clinic |
|
||||
|
||||
---
|
||||
|
||||
## PATCH `/api/v1/admin/clinic/{clinicUuid}/doctor/{doctorUuid}/permissions`
|
||||
|
||||
Update one doctor's permissions. **Deep merge** — only the resources/actions present in the body change; everything else keeps its current value.
|
||||
|
||||
**Permission:** `AUTH` — clinic owner or `ROLE_ADMIN`
|
||||
|
||||
### Request Body
|
||||
```json
|
||||
{
|
||||
"permissions": { "resources": { "payments": { "create": true } } },
|
||||
"active": true
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `permissions` | object | ❌ | `{resources: {<resource>: {<action>: bool}}}`. The bare `{<resource>: {...}}` form is also accepted. |
|
||||
| `active` | bool | ❌ | `false` revokes all access to this clinic |
|
||||
|
||||
### Response `200`
|
||||
Updated permission object.
|
||||
|
||||
### Errors
|
||||
| Code | HTTP | Description |
|
||||
|------|------|-------------|
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
| `ERR_ACCESS_DENIED` | 403 | Neither admin nor the clinic owner |
|
||||
| `ERR_NOT_FOUND_001` | 404 | Clinic not found, or doctor not attached to this clinic |
|
||||
| `ERR_VALIDATION_001` | 422 | `permissions` is not an object |
|
||||
|
||||
---
|
||||
|
||||
## POST `/file/upload/clinic_pro/clinic/field_clinic_logo`
|
||||
|
||||
Upload clinic logo.
|
||||
|
||||
Reference in New Issue
Block a user