fix(permissions): apply the patient and tag read gates to invited clinic doctors
PatientController::resolveScope and TenantTagController::guardTagView only ever checked the secretary, while every write in both controllers already ran through both checkers. So an invited clinic doctor with patients.view off got 200 with an empty list where a secretary got 403 — one permission, two behaviours. No data was exposed either way; tenant scoping emptied the result. The fix is not canOrNonMember. That collapses two different situations: a membership row switched to active=false means the collaboration ended, and ClinicDoctorPermission::can() returns false for everything in that case too. Routing it through the permission gate turned the existing 404 on a single record into a 403, which confirms the record exists to someone who just lost access. ClinicRecordAccessTest caught it. isActiveMemberDenied() answers the narrower question — active member, permission off — and leaves a deactivated row to the data scope, which closes it with a 404 and discloses nothing. A test now pins that distinction so it cannot be collapsed again. Tags keep the tags.view OR patients.view rule, now for both roles. Verified live in three states: active with both off 403/403, deactivated not 403, active with patients.view on 200/200. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -421,6 +421,8 @@ The envelope is always returned in full (`{version, resources}`); it is never fl
|
||||
|
||||
Unknown resources and unknown actions in a PATCH body are silently ignored (اعتبارسنجی از `PermissionCatalog::filterPatch`)، so a client cannot invent permission keys. `subscription` و `clinic_doctors` حالا در رجیستری هستند ولی پیشفرضشان برای پزشکِ عضو خاموش است — عملیاتِ مالکِ کلینیکاند.
|
||||
|
||||
**خواندنِ پرونده و تگ.** `PatientController::resolveScope` و `TenantTagController::guardTagView` تا پیش از این فقط منشی را بررسی میکردند، پس پزشکِ عضو با `patients.view` خاموش بهجای `403` یک `200` با فهرست **خالی** میگرفت (نشتی نبود — tenant scoping خالیاش میکرد — ولی یک مجوز دو رفتار داشت). حالا هر دو نقش قرینهاند. تگها مثل قبل با `tags.view` **یا** `patients.view` باز میشوند، برای هر دو نقش.
|
||||
|
||||
**اعمال (enforcement):** همهٔ منابع در بکاند enforce میشوند. نقطهٔ واحد `App\Clinic\Security\ClinicDoctorAccessChecker` (`denyUnlessGranted` / `memberClinicId`) که **فقط پزشکِ عضوِ کلینیک در محیطِ فعالِ کلینیک** را محدود میکند؛ مالک/ادمین/منشی/پزشکِ مطبِ شخصی دستنخورده عبور میکنند. کنترلرهایی که tenant را نقشمحور حل میکنند (Inventory/Tag/Staff/Discount/Sms) با `memberClinicId` پزشکِ عضو را به دادهٔ کلینیک میبرند (نه مطبِ شخصی). نبودِ مجوز → `403`. در پنل، سایدبار/Route/دکمههای CRUD با `usePermissions().can` برای محیطِ `scope=clinic` گِیت میشوند.
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user