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:
@@ -16,6 +16,7 @@ use App\Patient\Repository\PatientSessionRepository;
|
||||
use App\Patient\Security\PatientRecordScopeResolver;
|
||||
use App\Shared\Constant\ErrorCodes;
|
||||
use App\Shared\Controller\BaseController;
|
||||
use App\Shared\Controller\PermissionGateTrait;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
@@ -27,6 +28,17 @@ use OpenApi\Attributes as OA;
|
||||
#[IsGranted('IS_AUTHENTICATED_FULLY')]
|
||||
class BillingController extends BaseController
|
||||
{
|
||||
use PermissionGateTrait;
|
||||
|
||||
/**
|
||||
* صورتحساب و مطالبهٔ بیمه هر دو زیرِ «مدیریت پرداختها»ی پنل نشستهاند و توگل
|
||||
* جداگانهای ندارند، پس منبعِ مجوزشان یکی است.
|
||||
*/
|
||||
private function permissionResource(): string
|
||||
{
|
||||
return 'payments';
|
||||
}
|
||||
|
||||
public function __construct(
|
||||
private readonly InvoiceService $invoiceService,
|
||||
private readonly InvoiceRepository $invoiceRepo,
|
||||
@@ -108,6 +120,8 @@ class BillingController extends BaseController
|
||||
#[Route('/api/v1/billing/invoices', methods: ['POST'])]
|
||||
public function create(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->denyUnlessGranted($user, 'create');
|
||||
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
if ($entityId === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -132,6 +146,8 @@ class BillingController extends BaseController
|
||||
#[Route('/api/v1/billing/invoices/{uuid}', methods: ['GET'])]
|
||||
public function show(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->denyUnlessGranted($user, 'view');
|
||||
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$invoice = $this->invoiceRepo->findByUuid($uuid);
|
||||
if ($invoice === null || $invoice->getEntityType() !== $entityType || $invoice->getEntityId() !== $entityId) {
|
||||
@@ -144,6 +160,8 @@ class BillingController extends BaseController
|
||||
#[Route('/api/v1/billing/invoices/{uuid}/finalize', methods: ['POST'])]
|
||||
public function finalize(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->denyUnlessGranted($user, 'update');
|
||||
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$invoice = $this->invoiceRepo->findByUuid($uuid);
|
||||
if ($invoice === null || $invoice->getEntityType() !== $entityType || $invoice->getEntityId() !== $entityId) {
|
||||
@@ -164,6 +182,8 @@ class BillingController extends BaseController
|
||||
#[Route('/api/v1/my/billing/payments', methods: ['GET'])]
|
||||
public function listPayments(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->denyUnlessGranted($user, 'view');
|
||||
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
if ($entityId === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -190,6 +210,8 @@ class BillingController extends BaseController
|
||||
#[Route('/api/v1/my/billing/payments/summary', methods: ['GET'])]
|
||||
public function paymentsSummary(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->denyUnlessGranted($user, 'view');
|
||||
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
if ($entityId === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -223,6 +245,8 @@ class BillingController extends BaseController
|
||||
#[Route('/api/v1/my/billing/patients/{patientUuid}/invoices', methods: ['GET'])]
|
||||
public function listPatientInvoices(string $patientUuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->denyUnlessGranted($user, 'view');
|
||||
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
if ($entityId === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -262,6 +286,8 @@ class BillingController extends BaseController
|
||||
#[Route('/api/v1/billing/claims', methods: ['POST'])]
|
||||
public function createClaim(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->denyUnlessGranted($user, 'create');
|
||||
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
if ($entityId === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -286,6 +312,8 @@ class BillingController extends BaseController
|
||||
#[Route('/api/v1/billing/claims', methods: ['GET'])]
|
||||
public function listClaims(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->denyUnlessGranted($user, 'view');
|
||||
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
if ($entityId === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -324,6 +352,8 @@ class BillingController extends BaseController
|
||||
#[Route('/api/v1/billing/claims/by-patient', methods: ['GET'])]
|
||||
public function claimsByPatient(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->denyUnlessGranted($user, 'view');
|
||||
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
if ($entityId === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -349,6 +379,8 @@ class BillingController extends BaseController
|
||||
#[Route('/api/v1/billing/claims/by-patient/{patientUuid}', methods: ['GET'])]
|
||||
public function claimsForPatient(string $patientUuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->denyUnlessGranted($user, 'view');
|
||||
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
if ($entityId === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -439,6 +471,8 @@ class BillingController extends BaseController
|
||||
#[Route('/api/v1/billing/claims/{uuid}/{action}', methods: ['POST'], requirements: ['action' => 'submit|approve|reject|pay'])]
|
||||
public function transitionClaim(string $uuid, string $action, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->denyUnlessGranted($user, 'update');
|
||||
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$claim = $this->claimRepo->findByUuid($uuid);
|
||||
if ($claim === null || $claim->getEntityType() !== $entityType || $claim->getEntityId() !== $entityId) {
|
||||
@@ -483,6 +517,8 @@ class BillingController extends BaseController
|
||||
#[Route('/api/v1/billing/reports/insurance-debt', methods: ['GET'])]
|
||||
public function insuranceDebt(#[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->denyUnlessGranted($user, 'view');
|
||||
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
if ($entityId === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
|
||||
Reference in New Issue
Block a user