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:
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Shared\Controller;
|
||||
|
||||
use App\Auth\Entity\User;
|
||||
use App\Clinic\Security\ClinicDoctorAccessChecker;
|
||||
use App\Secretary\Security\SecretaryAccessChecker;
|
||||
use Symfony\Contracts\Service\Attribute\Required;
|
||||
|
||||
/**
|
||||
* گِیتِ مجوزِ مشترکِ کنترلرها.
|
||||
*
|
||||
* هر دو checker با هم صدا زده میشوند چون هرکدام یک نقش را میبندد و بقیه را
|
||||
* دستنخورده رد میکند: منشی با `SecretaryAccessChecker`، پزشکِ عضوِ کلینیک با
|
||||
* `ClinicDoctorAccessChecker`. ادمین، مالک کلینیک و پزشک مطب شخصی از هیچکدام
|
||||
* اثر نمیگیرند.
|
||||
*
|
||||
* تزریق با `#[Required]` است نه constructor، تا کنترلری که constructor پرِ خودش
|
||||
* را دارد برای گرفتن گِیت مجبور به بازنویسی امضایش نشود.
|
||||
*/
|
||||
trait PermissionGateTrait
|
||||
{
|
||||
private SecretaryAccessChecker $secretaryAccess;
|
||||
private ClinicDoctorAccessChecker $clinicDoctorAccess;
|
||||
|
||||
#[Required]
|
||||
public function setPermissionGateCheckers(
|
||||
SecretaryAccessChecker $secretaryAccess,
|
||||
ClinicDoctorAccessChecker $clinicDoctorAccess,
|
||||
): void {
|
||||
$this->secretaryAccess = $secretaryAccess;
|
||||
$this->clinicDoctorAccess = $clinicDoctorAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* منبعِ پیشفرضِ این کنترلر در `PermissionCatalog`. کنترلری که فقط یک منبع را
|
||||
* گِیت میکند این را بازنویسی میکند و بعد `denyUnlessGranted($user, $action)`
|
||||
* صدا میزند؛ کنترلری که چند منبع دارد `denyUnlessGrantedOn()` را مستقیم میزند.
|
||||
*/
|
||||
abstract private function permissionResource(): string;
|
||||
|
||||
/** @param 'view'|'create'|'update'|'delete'|'cancel'|'update_status' $action */
|
||||
private function denyUnlessGranted(User $user, string $action): void
|
||||
{
|
||||
$this->denyUnlessGrantedOn($user, $this->permissionResource(), $action);
|
||||
}
|
||||
|
||||
/**
|
||||
* گِیت روی یک منبعِ صریح — برای کنترلری که بیش از یک منبع را پوشش میدهد.
|
||||
*
|
||||
* @param 'view'|'create'|'update'|'delete'|'cancel'|'update_status' $action
|
||||
*/
|
||||
private function denyUnlessGrantedOn(User $user, string $resource, string $action): void
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, $resource, $action);
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, $resource, $action);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user