createUser(['ROLE_CLINIC']); $clinic = new Clinic($owner); $this->em->persist($clinic); $doctorUser = $this->createUser(['ROLE_DOCTOR']); $doctor = new Doctor($doctorUser, 'دکتر عضو'); $this->em->persist($doctor); $clinic->getDoctors()->add($doctor); $perm = new ClinicDoctorPermission($clinic, $doctor); $this->em->persist($perm); // محیطِ فعالِ پزشک = کلینیک، تا memberClinicId او را به کلینیک ببرد. $this->em->persist(new UserActiveContext($doctorUser, $clinic->getUuid(), 'clinic')); return [$doctorUser, $perm]; } public function testInventoryDeniedByDefault(): void { // DEFAULT_PERMISSIONS: inventory.* = false [$doctorUser] = $this->makeMemberDoctor(); $this->em->flush(); $this->authJson('GET', '/api/v1/inventory-items', $doctorUser); $this->assertSame(403, $this->responseCode()); } public function testInventoryAllowedWhenGranted(): void { [$doctorUser, $perm] = $this->makeMemberDoctor(); $perm->mergePermissions(['resources' => ['inventory' => ['view' => true]]]); $this->em->flush(); $this->authJson('GET', '/api/v1/inventory-items', $doctorUser); $this->assertSame(200, $this->responseCode()); } public function testStaffDeniedByDefault(): void { [$doctorUser] = $this->makeMemberDoctor(); $this->em->flush(); $this->authJson('GET', '/api/v1/staff', $doctorUser); $this->assertSame(403, $this->responseCode()); } public function testStaffCreateDeniedButViewGranted(): void { [$doctorUser, $perm] = $this->makeMemberDoctor(); $perm->mergePermissions(['resources' => ['staff' => ['view' => true, 'create' => false]]]); $this->em->flush(); $this->authJson('GET', '/api/v1/staff', $doctorUser); $this->assertSame(200, $this->responseCode()); $this->authJson('POST', '/api/v1/staff', $doctorUser, ['full_name' => 'خانم تست']); $this->assertSame(403, $this->responseCode()); } public function testIndependentDoctorIsNotRestricted(): void { // پزشکِ مستقل: نه عضوِ کلینیک، نه محیطِ کلینیک → روی دادهٔ شخصیِ خودش آزاد. $doctorUser = $this->createUser(['ROLE_DOCTOR']); $this->em->persist(new Doctor($doctorUser, 'دکتر مستقل')); $this->em->flush(); // inventory برای منابعِ ClinicDoctorPermission پیش‌فرض false است، اما این پزشک // اصلاً عضوِ کلینیک نیست، پس ClinicDoctorAccessChecker او را محدود نمی‌کند. $this->authJson('GET', '/api/v1/inventory-items', $doctorUser); $this->assertSame(200, $this->responseCode()); } // ── خواندنِ پرونده و تگ — قرینهٔ منشی ──────────────────────────────────── /** * تا پیش از این فقط منشی در PatientController::resolveScope بررسی می‌شد، پس * پزشکِ عضو با `patients.view` خاموش به‌جای ۴۰۳، ۲۰۰ با فهرست خالی می‌گرفت. */ public function testPatientListDeniedWhenPatientsViewOff(): void { [$doctorUser, $perm] = $this->makeMemberDoctor(); $perm->mergePermissions(['resources' => ['patients' => ['view' => false]]]); $this->em->flush(); $this->authJson('GET', '/api/v1/patients', $doctorUser); $this->assertSame(403, $this->responseCode()); } /** پیش‌فرضِ پزشکِ عضو `patients.view = true` است. */ public function testPatientListAllowedByDefault(): void { [$doctorUser] = $this->makeMemberDoctor(); $this->em->flush(); $this->authJson('GET', '/api/v1/patients', $doctorUser); $this->assertSame(200, $this->responseCode()); } /** تگ‌ها با tags.view یا patients.view باز می‌شوند — همان قاعدهٔ منشی. */ public function testTagListAllowedViaPatientsViewEvenWhenTagsViewOff(): void { [$doctorUser, $perm] = $this->makeMemberDoctor(); $perm->mergePermissions(['resources' => [ 'patients' => ['view' => true], 'tags' => ['view' => false], ]]); $this->em->flush(); $this->authJson('GET', '/api/v1/tenant-tags', $doctorUser); $this->assertSame(200, $this->responseCode()); } public function testTagListDeniedWhenNeitherTagsNorPatientsViewGranted(): void { [$doctorUser, $perm] = $this->makeMemberDoctor(); $perm->mergePermissions(['resources' => [ 'patients' => ['view' => false], 'tags' => ['view' => false], ]]); $this->em->flush(); $this->authJson('GET', '/api/v1/tenant-tags', $doctorUser); $this->assertSame(403, $this->responseCode()); } public function testTagListAllowedWithTagsViewAlone(): void { [$doctorUser, $perm] = $this->makeMemberDoctor(); $perm->mergePermissions(['resources' => [ 'patients' => ['view' => false], 'tags' => ['view' => true], ]]); $this->em->flush(); $this->authJson('GET', '/api/v1/tenant-tags', $doctorUser); $this->assertSame(200, $this->responseCode()); } /** * «پایان همکاری» با «مجوز خاموش» یکی نیست: ردیفِ غیرفعال نباید ۴۰۳ بدهد، * وگرنه وجودِ پرونده لو می‌رود. دامنهٔ داده خودش آن را می‌بندد. */ public function testDeactivatedMemberIsNotAnswered403OnTheList(): void { [$doctorUser, $perm] = $this->makeMemberDoctor(); $perm->setActive(false); $this->em->flush(); $this->authJson('GET', '/api/v1/patients', $doctorUser); $this->assertNotSame( 403, $this->responseCode(), 'ردیفِ غیرفعال باید از مسیرِ دامنهٔ داده بسته شود، نه با ۴۰۳ مجوز', ); } }