feat: Implement permission gate for appointment and billing controllers

- Added PermissionGateTrait to manage access control for AppointmentPlanController and BillingController.
- Introduced denyUnlessGrantedForPlanning method in AppointmentPlanController to handle specific permission checks for planning appointments.
- Updated existing methods in both controllers to utilize the new permission checks.
- Refactored ResourcePermissionTrait to use PermissionGateTrait for cleaner permission management.
- Added tests to ensure proper permission enforcement across different scenarios, including cross-tenant access restrictions for staff.
This commit is contained in:
hamed
2026-08-08 10:27:13 +03:30
parent c452150a83
commit 934405c42d
14 changed files with 830 additions and 67 deletions
+27 -1
View File
@@ -52,11 +52,37 @@ ddev exec php bin/console app:billing:backfill-claims # ساخت م
---
## مجوزها
از ۲۰۲۶-۰۸-۰۸ هر روتِ این کنترلر پیش از هر واکشی، مجوزِ `payments` را با
`App\Shared\Controller\PermissionGateTrait` می‌سنجد. پیش از آن هیچ روتی گِیت مجوزی
نداشت: منشیِ `payments:false` هم پرداخت‌ها را می‌دید، هم صورتحساب می‌ساخت، هم وضعیت
مطالبه را عوض می‌کرد. جزئیات در `docs/security/AUDIT-2026-08-07.md` یافتهٔ ۸.
منبعِ مجوز برای صورتحساب و مطالبه یکی است — `payments` — چون هر دو زیر همان توگلِ
«مدیریت پرداخت‌ها»ی پنل نشسته‌اند و توگل جداگانه‌ای ندارند.
- `payments.view` — خواندن: صورتحساب، فهرست پرداخت‌ها و خلاصه‌شان، فهرست مطالبات،
مطالبات به تفکیک بیمار، گزارش بدهی بیمه، صورتحساب‌های یک بیمار.
- `payments.create` — ساخت: صورتحساب تازه، مطالبهٔ تازه.
- `payments.update` — تغییر وضعیت: نهایی‌کردن صورتحساب، و `submit`/`approve`/`reject`/`pay`
روی مطالبه.
گِیت پیش از `findByUuid()` می‌نشیند. ترتیب عمدی است: اگر بعدش بود، uuidِ ناموجود ۴۰۴
می‌داد و همان تفاوت ۴۰۳/۴۰۴ به کاربرِ بی‌مجوز می‌گفت کدام uuid در این محیط وجود دارد.
نقش‌های دیگر اثری نمی‌گیرند: هر دو checker برای ادمین، مالک کلینیک و پزشک مطب شخصی
pass-through هستند و فقط منشی و پزشکِ عضوِ کلینیک را محدود می‌کنند.
خطای رد: `403` با `ERR_FORBIDDEN_001`.
---
## POST /api/v1/billing/invoices
ساخت صورتحساب از یک مراجعه. اگر صورتحساب برای آن مراجعه قبلاً ساخته شده، همان برگردانده می‌شود (idempotent).
**Permission:** `AUTH` (مالک مراجعه)
**Permission:** `payments.create` + مالکیت محیطِ مراجعه
**Body:**
```json