fix(security): enforce doctor scope on POST my/appointment (H1)

createAppointment only checked the caller held an allowed role, then booked
onto whatever doctor_uuid the request named — a doctor could book onto any
other doctor's calendar, a clinic onto doctors outside it, a secretary outside
their scope. Add canBookForDoctor(): doctor→own only, clinic→member doctors,
secretary→active scope + appointments.create permission, admin→any.

Regression: tests/Appointment/BookingScopeTest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
hamed
2026-06-28 18:52:23 +03:30
co-authored by Claude Opus 4.8
parent ae06498a96
commit c084571bf0
4 changed files with 125 additions and 2 deletions
+56
View File
@@ -0,0 +1,56 @@
<?php
namespace App\Tests\Appointment;
use App\Doctor\Entity\Doctor;
use App\Tests\ApiTestCase;
/**
* POST /api/v1/my/appointment must enforce that the acting user is scoped to
* the target doctor. A doctor having the ROLE_DOCTOR role is not enough — they
* must not be able to book onto another doctor's calendar via doctor_uuid.
*/
class BookingScopeTest extends ApiTestCase
{
private function makeDoctor(): Doctor
{
$user = $this->createUser(['ROLE_USER', 'ROLE_DOCTOR']);
$doctor = new Doctor($user, 'دکتر تست');
$this->em->persist($doctor);
$this->em->flush();
return $doctor;
}
private function bookingBody(string $doctorUuid): array
{
$start = time() + 86_400;
return [
'doctor_uuid' => $doctorUuid,
'slot_start' => $start,
'slot_end' => $start + 1_800,
'patient_mobile' => '09120000000',
'patient_name' => 'بیمار تست',
];
}
public function testDoctorCannotBookOntoAnotherDoctor(): void
{
$doctorA = $this->makeDoctor();
$doctorB = $this->makeDoctor();
$this->authJson('POST', '/api/v1/my/appointment', $doctorA->getUser(), $this->bookingBody($doctorB->getUuid()));
$this->assertSame(403, $this->responseCode());
}
public function testDoctorCanBookOntoOwnCalendar(): void
{
$doctorA = $this->makeDoctor();
$this->authJson('POST', '/api/v1/my/appointment', $doctorA->getUser(), $this->bookingBody($doctorA->getUuid()));
$this->assertSame(201, $this->responseCode());
}
}