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
@@ -3,11 +3,9 @@
namespace App\Resource\Controller;
use App\Auth\Entity\User;
use App\Clinic\Security\ClinicDoctorAccessChecker;
use App\Secretary\Security\SecretaryAccessChecker;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\PermissionGateTrait;
use App\Shared\Exception\AppException;
use Symfony\Contracts\Service\Attribute\Required;
/**
* گِیتِ مشترک کنترلرهای این دامنه.
@@ -18,20 +16,13 @@ use Symfony\Contracts\Service\Attribute\Required;
*
* تعطیلات از این قاعده مستثناست و `appointment_settings` می‌ماند — صفحه‌اش
* زیرمجموعهٔ تنظیمات نوبت‌دهی است، نه فهرست دستگاه‌ها.
*
* تزریق checkerها و خودِ `denyUnlessGranted` در `PermissionGateTrait` مشترک است؛
* اینجا فقط منبعِ پیش‌فرض و استثنای نوبت‌دهی می‌ماند.
*/
trait ResourcePermissionTrait
{
private SecretaryAccessChecker $secretaryAccess;
private ClinicDoctorAccessChecker $clinicDoctorAccess;
#[Required]
public function setResourceAccessCheckers(
SecretaryAccessChecker $secretaryAccess,
ClinicDoctorAccessChecker $clinicDoctorAccess,
): void {
$this->secretaryAccess = $secretaryAccess;
$this->clinicDoctorAccess = $clinicDoctorAccess;
}
use PermissionGateTrait;
/** کنترلری که منبعِ دیگری را گِیت می‌کند این را بازنویسی می‌کند. */
private function permissionResource(): string
@@ -39,15 +30,6 @@ trait ResourcePermissionTrait
return 'resources';
}
/** @param 'view'|'create'|'update'|'delete' $action */
private function denyUnlessGranted(User $user, string $action): void
{
$resource = $this->permissionResource();
$this->secretaryAccess->denyUnlessGranted($user, $resource, $action);
$this->clinicDoctorAccess->denyUnlessGranted($user, $resource, $action);
}
/**
* خواندنِ منبع **برای نوبت‌دهی** — نه برای پیکربندی‌اش.
*