feat(clinic-doctor): full permission coverage + enforcement, parity with secretary
The clinic-member-doctor permission system (ClinicDoctorPermission) lagged the secretary system: only 6 resources, enforced in ~6 places, dead toggles (services.update never checked), and a sidebar showing just appointments+patients. Bring it to parity so a clinic owner can control exactly what each member doctor does — while an independent doctor stays completely unrestricted. Coverage: add insurances, addresses, inventory, tags, staff, discounts, sms to ClinicDoctorPermission::DEFAULT_PERMISSIONS + DoctorPermissionsModal (subscription/clinic_doctors stay owner-only by design). New App\Clinic\Security\ClinicDoctorAccessChecker (parallel to SecretaryAccessChecker): - denyUnlessGranted(user, resource, action): 403 only for a clinic-member doctor in the clinic context; owner/admin/secretary/independent-doctor pass through. - memberClinicId(user): resolves the member doctor to the CLINIC's tenant so the role-based controllers (Inventory/Tag/Staff/Discount/Sms) stop showing them their personal tenant in clinic context. Enforcement wired into 10 controllers alongside the existing secretary gates: ClinicService (services), Insurance (insurances), Patient (patients+payments), Staff, Discount, Inventory, Tag, SmsWallet, Payment, PaymentMethod. Frontend: the guest-doctor sidebar branch now exposes every permitted resource (gated by can()) plus a «تنظیمات» entry; both settings navs (PurchaseSubscription Sidebar + SETTINGS_MENU) are now permission-filtered for a scope=clinic doctor, not just secretaries; my-payments route gets the missing payments permission. CRUD-button gating already applies (usePermissions is role-agnostic). Tests: ClinicDoctorPermissionEnforcementTest (member denied/allowed + independent-doctor-unrestricted); guest-doctor sidebar gating. Backend 375 pass, frontend 503 pass. docs/api/clinic.md updated with the full resource set + enforcement notes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,13 @@ class ClinicDoctorPermission
|
||||
'payments' => ['view' => true, 'create' => false, 'update' => false, 'delete' => false],
|
||||
'services' => ['view' => true, 'update' => false],
|
||||
'clinic_info' => ['view' => true, 'update' => false],
|
||||
'insurances' => ['view' => true, 'create' => false, 'update' => false, 'delete' => false],
|
||||
'addresses' => ['view' => true, 'create' => false, 'update' => false, 'delete' => false],
|
||||
'inventory' => ['view' => false, 'create' => false, 'update' => false, 'delete' => false],
|
||||
'tags' => ['view' => false, 'create' => false, 'update' => false, 'delete' => false],
|
||||
'staff' => ['view' => false, 'create' => false, 'update' => false, 'delete' => false],
|
||||
'discounts' => ['view' => false, 'create' => false, 'update' => false, 'delete' => false],
|
||||
'sms' => ['view' => false, 'create' => false, 'update' => false, 'delete' => false],
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace App\Clinic\Security;
|
||||
|
||||
use App\Auth\Entity\User;
|
||||
use App\Auth\Repository\UserActiveContextRepository;
|
||||
use App\Clinic\Repository\ClinicRepository;
|
||||
use App\Doctor\Repository\DoctorRepository;
|
||||
use App\Shared\Constant\ErrorCodes;
|
||||
use App\Shared\Exception\AppException;
|
||||
|
||||
/**
|
||||
* نقطهٔ واحدِ اعمالِ مجوزِ «پزشکِ عضوِ کلینیک» روی endpointهای چند-نقشه — قرینهٔ
|
||||
* SecretaryAccessChecker اما برای ClinicDoctorPermission.
|
||||
*
|
||||
* فقط پزشکی که در محیطِ فعالش (UserActiveContext.db_uuid = کلینیک) عضوِ همان کلینیک
|
||||
* است محدود میشود. بقیه — مالکِ کلینیک، ادمین، منشی، پزشکِ مطب شخصی، یا هر کاربری
|
||||
* که محیطش کلینیک نیست — دستنخورده عبور میکنند (این checker آنها را محدود نمیکند).
|
||||
*/
|
||||
class ClinicDoctorAccessChecker
|
||||
{
|
||||
public function __construct(
|
||||
private readonly UserActiveContextRepository $contextRepo,
|
||||
private readonly ClinicRepository $clinicRepo,
|
||||
private readonly DoctorRepository $doctorRepo,
|
||||
private readonly ClinicDoctorPermissionChecker $permissions,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* idِ کلینیکی که این پزشک در محیطِ فعالش عضوِ آن است — برای کنترلرهایی که
|
||||
* tenant را نقشمحور حل میکنند و بدون این، پزشکِ عضو را به مطبِ شخصیاش میبردند
|
||||
* (نه دادهٔ کلینیک). null اگر محیط کلینیک نیست یا کاربر عضو نیست.
|
||||
*/
|
||||
public function memberClinicId(User $user): ?int
|
||||
{
|
||||
$dbUuid = $this->contextRepo->findByUser($user)?->getDbUuid();
|
||||
if ($dbUuid === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$clinic = $this->clinicRepo->findByUuid($dbUuid);
|
||||
if ($clinic === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$doctor = $this->doctorRepo->findByUser($user);
|
||||
if ($doctor === null || !$clinic->hasDoctor($doctor)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $clinic->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* فقط پزشکِ عضوِ کلینیک را با ClinicDoctorPermission محدود کن؛ سایر کاربران true.
|
||||
*/
|
||||
public function canOrNonMember(User $user, string $resource, string $action): bool
|
||||
{
|
||||
$dbUuid = $this->contextRepo->findByUser($user)?->getDbUuid();
|
||||
if ($dbUuid === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$clinic = $this->clinicRepo->findByUuid($dbUuid);
|
||||
if ($clinic === null) {
|
||||
// محیطِ مطب شخصی (db_uuid پزشک است، نه کلینیک) → محدود نمیکنیم.
|
||||
return true;
|
||||
}
|
||||
|
||||
// مالکِ کلینیک هرگز با مجوزهای عضویت قفل نمیشود.
|
||||
if ($clinic->getUser()->getId() === $user->getId()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$doctor = $this->doctorRepo->findByUser($user);
|
||||
if ($doctor === null || !$clinic->hasDoctor($doctor)) {
|
||||
// غیرعضو (مثلاً منشی) — این checker مالِ او نیست.
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->permissions->can($user, $clinic, $resource, $action);
|
||||
}
|
||||
|
||||
/** 403 اگر پزشکِ عضو مجاز نباشد؛ سایر کاربران بدون تغییر عبور میکنند. */
|
||||
public function denyUnlessGranted(User $user, string $resource, string $action): void
|
||||
{
|
||||
if (!$this->canOrNonMember($user, $resource, $action)) {
|
||||
throw new AppException(ErrorCodes::ERR_FORBIDDEN_001, null, 403);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ use App\ClinicService\Entity\ServiceItemAuditLog;
|
||||
use App\ClinicService\Entity\ServiceSection;
|
||||
use App\Insurance\Entity\TenantServiceCoverage;
|
||||
use App\ClinicService\Entity\Tariff;
|
||||
use App\Clinic\Security\ClinicDoctorAccessChecker;
|
||||
use App\Secretary\Security\SecretaryAccessChecker;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use App\ClinicService\Repository\ServiceItemAuditLogRepository;
|
||||
@@ -51,8 +52,16 @@ class ClinicServiceController extends BaseController
|
||||
private readonly EntityContextResolver $contextResolver,
|
||||
private readonly RequestStack $requestStack,
|
||||
private readonly SecretaryAccessChecker $secretaryAccess,
|
||||
private readonly ClinicDoctorAccessChecker $clinicDoctorAccess,
|
||||
) {}
|
||||
|
||||
/** گِیتِ ترکیبی: منشی + پزشکِ عضوِ کلینیک (هرکدام فقط نقشِ خودش را محدود میکند). */
|
||||
private function denyServices(User $user, string $action): void
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'services', $action);
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'services', $action);
|
||||
}
|
||||
|
||||
/**
|
||||
* uuid و عنوان پکیج کالای هر سرویس را به آرایهی خروجی اضافه میکند. پکیجها با یک
|
||||
* کوئری واکشی میشوند تا فهرست سرویسها به N+1 نیفتد.
|
||||
@@ -140,7 +149,7 @@ class ClinicServiceController extends BaseController
|
||||
#[Route('/api/v1/service-sections', methods: ['GET'])]
|
||||
public function listSections(#[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'services', 'view');
|
||||
$this->denyServices($user, 'view');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$this->assertServicesGate($entityType, $entityId);
|
||||
|
||||
@@ -158,7 +167,7 @@ class ClinicServiceController extends BaseController
|
||||
#[Route('/api/v1/service-section', methods: ['POST'])]
|
||||
public function createSection(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'services', 'create');
|
||||
$this->denyServices($user, 'create');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$this->assertServicesGate($entityType, $entityId);
|
||||
|
||||
@@ -178,7 +187,7 @@ class ClinicServiceController extends BaseController
|
||||
#[Route('/api/v1/service-section/{uuid}', methods: ['PATCH'])]
|
||||
public function updateSection(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'services', 'update');
|
||||
$this->denyServices($user, 'update');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$this->assertServicesGate($entityType, $entityId);
|
||||
|
||||
@@ -203,7 +212,7 @@ class ClinicServiceController extends BaseController
|
||||
#[Route('/api/v1/service-section/{uuid}', methods: ['DELETE'])]
|
||||
public function deleteSection(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'services', 'delete');
|
||||
$this->denyServices($user, 'delete');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$this->assertServicesGate($entityType, $entityId);
|
||||
|
||||
@@ -223,7 +232,7 @@ class ClinicServiceController extends BaseController
|
||||
#[Route('/api/v1/service-items', methods: ['GET'])]
|
||||
public function listAllItems(#[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'services', 'view');
|
||||
$this->denyServices($user, 'view');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
|
||||
// A user with neither a doctor profile nor a clinic (admin, secretary,
|
||||
@@ -242,7 +251,7 @@ class ClinicServiceController extends BaseController
|
||||
#[Route('/api/v1/service-items/{sectionUuid}', methods: ['GET'])]
|
||||
public function listItems(string $sectionUuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'services', 'view');
|
||||
$this->denyServices($user, 'view');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
|
||||
$section = $this->sectionRepo->findByUuid($sectionUuid);
|
||||
@@ -256,7 +265,7 @@ class ClinicServiceController extends BaseController
|
||||
#[Route('/api/v1/service-item/{uuid}', methods: ['GET'])]
|
||||
public function getItem(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'services', 'view');
|
||||
$this->denyServices($user, 'view');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
|
||||
$item = $this->itemRepo->findByUuid($uuid);
|
||||
@@ -271,7 +280,7 @@ class ClinicServiceController extends BaseController
|
||||
#[Route('/api/v1/service-item/{uuid}/audit-logs', methods: ['GET'])]
|
||||
public function listItemAuditLogs(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'services', 'view');
|
||||
$this->denyServices($user, 'view');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
|
||||
$item = $this->itemRepo->findByUuid($uuid);
|
||||
@@ -288,7 +297,7 @@ class ClinicServiceController extends BaseController
|
||||
#[Route('/api/v1/service-item', methods: ['POST'])]
|
||||
public function createItem(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'services', 'create');
|
||||
$this->denyServices($user, 'create');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$this->assertServicesGate($entityType, $entityId);
|
||||
|
||||
@@ -343,7 +352,7 @@ class ClinicServiceController extends BaseController
|
||||
#[Route('/api/v1/service-item/{uuid}', methods: ['PATCH'])]
|
||||
public function updateItem(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'services', 'update');
|
||||
$this->denyServices($user, 'update');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
|
||||
$item = $this->itemRepo->findByUuid($uuid);
|
||||
@@ -398,7 +407,7 @@ class ClinicServiceController extends BaseController
|
||||
#[Route('/api/v1/service-item/{uuid}', methods: ['DELETE'])]
|
||||
public function deleteItem(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'services', 'delete');
|
||||
$this->denyServices($user, 'delete');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
|
||||
$item = $this->itemRepo->findByUuid($uuid);
|
||||
@@ -429,7 +438,7 @@ class ClinicServiceController extends BaseController
|
||||
#[IsGranted('IS_AUTHENTICATED_FULLY')]
|
||||
public function listTariffs(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'services', 'view');
|
||||
$this->denyServices($user, 'view');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
|
||||
$item = $this->itemRepo->findByUuid($uuid);
|
||||
@@ -450,7 +459,7 @@ class ClinicServiceController extends BaseController
|
||||
#[IsGranted('IS_AUTHENTICATED_FULLY')]
|
||||
public function setTariff(string $uuid, int $year, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'services', 'update');
|
||||
$this->denyServices($user, 'update');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
|
||||
$item = $this->itemRepo->findByUuid($uuid);
|
||||
|
||||
@@ -27,12 +27,17 @@ class DiscountController extends BaseController
|
||||
private readonly ClinicRepository $clinicRepo,
|
||||
private readonly PatientSessionRepository $sessionRepo,
|
||||
private readonly SecretaryAccessChecker $secretaryAccess,
|
||||
private readonly \App\Clinic\Security\ClinicDoctorAccessChecker $clinicDoctorAccess,
|
||||
) {}
|
||||
|
||||
/** @return array{0: string, 1: ?int} */
|
||||
private function resolveOwner(User $user): array
|
||||
{
|
||||
if ($user->hasRole('ROLE_DOCTOR')) {
|
||||
$memberClinicId = $this->clinicDoctorAccess->memberClinicId($user);
|
||||
if ($memberClinicId !== null) {
|
||||
return ['clinic', $memberClinicId];
|
||||
}
|
||||
return ['doctor', $this->doctorRepo->findByUser($user)?->getId()];
|
||||
}
|
||||
if ($user->hasRole('ROLE_CLINIC')) {
|
||||
@@ -52,6 +57,7 @@ class DiscountController extends BaseController
|
||||
public function list(#[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'discounts', 'view');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'discounts', 'view');
|
||||
[$ownerType, $ownerId] = $this->resolveOwner($user);
|
||||
if ($ownerId === null) {
|
||||
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
|
||||
@@ -65,6 +71,7 @@ class DiscountController extends BaseController
|
||||
public function create(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'discounts', 'create');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'discounts', 'create');
|
||||
[$ownerType, $ownerId] = $this->resolveOwner($user);
|
||||
if ($ownerId === null) {
|
||||
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
|
||||
@@ -93,6 +100,7 @@ class DiscountController extends BaseController
|
||||
public function update(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'discounts', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'discounts', 'update');
|
||||
[$ownerType, $ownerId] = $this->resolveOwner($user);
|
||||
if ($ownerId === null) {
|
||||
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
|
||||
@@ -127,6 +135,7 @@ class DiscountController extends BaseController
|
||||
public function delete(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'discounts', 'delete');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'discounts', 'delete');
|
||||
[$ownerType, $ownerId] = $this->resolveOwner($user);
|
||||
if ($ownerId === null) {
|
||||
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
|
||||
|
||||
@@ -46,6 +46,7 @@ class InsuranceController extends BaseController
|
||||
private readonly \App\Clinic\Security\ClinicDoctorPermissionChecker $permChecker,
|
||||
private readonly \App\Patient\Security\PatientRecordScopeResolver $scopeResolver,
|
||||
private readonly SecretaryAccessChecker $secretaryAccess,
|
||||
private readonly \App\Clinic\Security\ClinicDoctorAccessChecker $clinicDoctorAccess,
|
||||
private readonly string $projectDir,
|
||||
) {}
|
||||
|
||||
@@ -255,6 +256,7 @@ class InsuranceController extends BaseController
|
||||
public function getInsurancePricing(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'insurances', 'view');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'insurances', 'view');
|
||||
[$entityType, $entityId, $err] = $this->resolveTargetEntity($user, $request->query->get('doctor_uuid'), 'view');
|
||||
if ($err !== null) {
|
||||
return $err;
|
||||
@@ -305,6 +307,7 @@ class InsuranceController extends BaseController
|
||||
public function saveInsurancePricing(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'insurances', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'insurances', 'update');
|
||||
$data = json_decode($request->getContent(), true) ?? [];
|
||||
|
||||
[$entityType, $entityId, $err] = $this->resolveTargetEntity($user, $data['doctor_uuid'] ?? null, 'update');
|
||||
@@ -375,6 +378,7 @@ class InsuranceController extends BaseController
|
||||
public function listTenantInsurances(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'insurances', 'view');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'insurances', 'view');
|
||||
[$entityType, $entityId, $err] = $this->resolveTargetEntity($user, $request->query->get('doctor_uuid'), 'view');
|
||||
if ($err !== null) {
|
||||
return $err;
|
||||
@@ -406,6 +410,7 @@ class InsuranceController extends BaseController
|
||||
public function activateTenantInsurance(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'insurances', 'create');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'insurances', 'create');
|
||||
$data = json_decode($request->getContent(), true) ?? [];
|
||||
|
||||
[$entityType, $entityId, $err] = $this->resolveTargetEntity($user, $data['doctor_uuid'] ?? null, 'update');
|
||||
@@ -442,6 +447,7 @@ class InsuranceController extends BaseController
|
||||
public function updateTenantInsurance(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'insurances', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'insurances', 'update');
|
||||
$data = json_decode($request->getContent(), true) ?? [];
|
||||
|
||||
[$entityType, $entityId, $err] = $this->resolveTargetEntity($user, $data['doctor_uuid'] ?? null, 'update');
|
||||
@@ -488,6 +494,7 @@ class InsuranceController extends BaseController
|
||||
public function deactivateTenantInsurance(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'insurances', 'delete');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'insurances', 'delete');
|
||||
[$entityType, $entityId, $err] = $this->resolveTargetEntity($user, $request->query->get('doctor_uuid'), 'update');
|
||||
if ($err !== null) {
|
||||
return $err;
|
||||
@@ -508,6 +515,7 @@ class InsuranceController extends BaseController
|
||||
public function listServiceCoverage(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'insurances', 'view');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'insurances', 'view');
|
||||
[$entityType, $entityId, $err] = $this->resolveTargetEntity($user, $request->query->get('doctor_uuid'), 'view');
|
||||
if ($err !== null) {
|
||||
return $err;
|
||||
@@ -539,6 +547,7 @@ class InsuranceController extends BaseController
|
||||
public function setServiceCoverage(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'insurances', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'insurances', 'update');
|
||||
$data = json_decode($request->getContent(), true) ?? [];
|
||||
|
||||
[$entityType, $entityId, $err] = $this->resolveTargetEntity($user, $data['doctor_uuid'] ?? null, 'update');
|
||||
@@ -585,6 +594,7 @@ class InsuranceController extends BaseController
|
||||
public function addDoctorInsurance(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'insurances', 'create');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'insurances', 'create');
|
||||
$data = json_decode($request->getContent(), true) ?? [];
|
||||
$doctorId = $data['doctor_id'] ?? null;
|
||||
$insuranceId = $data['insurance_id'] ?? null;
|
||||
@@ -626,6 +636,7 @@ class InsuranceController extends BaseController
|
||||
public function showDoctorInsurance(int $id, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'insurances', 'view');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'insurances', 'view');
|
||||
$doctorInsurance = $this->doctorInsuranceRepo->find($id);
|
||||
if ($doctorInsurance === null) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'بیمه پزشک یافت نشد', 404);
|
||||
@@ -643,6 +654,7 @@ class InsuranceController extends BaseController
|
||||
public function updateDoctorInsurance(int $id, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'insurances', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'insurances', 'update');
|
||||
$doctorInsurance = $this->doctorInsuranceRepo->find($id);
|
||||
if ($doctorInsurance === null) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'بیمه پزشک یافت نشد', 404);
|
||||
@@ -666,6 +678,7 @@ class InsuranceController extends BaseController
|
||||
public function deleteDoctorInsurance(int $id, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'insurances', 'delete');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'insurances', 'delete');
|
||||
$doctorInsurance = $this->doctorInsuranceRepo->find($id);
|
||||
if ($doctorInsurance === null) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'بیمه پزشک یافت نشد', 404);
|
||||
|
||||
@@ -39,6 +39,7 @@ class InventoryController extends BaseController
|
||||
private readonly ClinicRepository $clinicRepo,
|
||||
private readonly UserActiveContextRepository $contextRepo,
|
||||
private readonly SecretaryAccessChecker $secretaryAccess,
|
||||
private readonly \App\Clinic\Security\ClinicDoctorAccessChecker $clinicDoctorAccess,
|
||||
) {}
|
||||
|
||||
// ── Items ────────────────────────────────────────────────────────────────
|
||||
@@ -47,6 +48,7 @@ class InventoryController extends BaseController
|
||||
public function listItems(#[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'inventory', 'view');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'inventory', 'view');
|
||||
[$type, $id] = $this->resolveEntity($user);
|
||||
if ($id === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -64,6 +66,7 @@ class InventoryController extends BaseController
|
||||
public function listCategories(#[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'inventory', 'view');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'inventory', 'view');
|
||||
[$type, $id] = $this->resolveEntity($user);
|
||||
if ($id === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -85,6 +88,7 @@ class InventoryController extends BaseController
|
||||
public function createItem(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'inventory', 'create');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'inventory', 'create');
|
||||
[$type, $id] = $this->resolveEntity($user);
|
||||
if ($id === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -107,6 +111,7 @@ class InventoryController extends BaseController
|
||||
public function updateItem(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'inventory', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'inventory', 'update');
|
||||
$item = $this->ownedItem($uuid, $user);
|
||||
if ($item === null) {
|
||||
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'کالا یافت نشد', 404);
|
||||
@@ -130,6 +135,7 @@ class InventoryController extends BaseController
|
||||
public function deleteItem(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'inventory', 'delete');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'inventory', 'delete');
|
||||
$item = $this->ownedItem($uuid, $user);
|
||||
if ($item === null) {
|
||||
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'کالا یافت نشد', 404);
|
||||
@@ -146,6 +152,7 @@ class InventoryController extends BaseController
|
||||
public function listPackages(#[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'inventory', 'view');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'inventory', 'view');
|
||||
[$type, $id] = $this->resolveEntity($user);
|
||||
if ($id === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -161,6 +168,7 @@ class InventoryController extends BaseController
|
||||
public function createPackage(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'inventory', 'create');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'inventory', 'create');
|
||||
[$type, $id] = $this->resolveEntity($user);
|
||||
if ($id === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -183,6 +191,7 @@ class InventoryController extends BaseController
|
||||
public function updatePackage(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'inventory', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'inventory', 'update');
|
||||
$package = $this->ownedPackage($uuid, $user);
|
||||
if ($package === null) {
|
||||
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'پکیج یافت نشد', 404);
|
||||
@@ -210,6 +219,7 @@ class InventoryController extends BaseController
|
||||
public function deletePackage(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'inventory', 'delete');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'inventory', 'delete');
|
||||
$package = $this->ownedPackage($uuid, $user);
|
||||
if ($package === null) {
|
||||
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'پکیج یافت نشد', 404);
|
||||
@@ -280,6 +290,10 @@ class InventoryController extends BaseController
|
||||
private function resolveEntity(User $user): array
|
||||
{
|
||||
if ($user->hasRole('ROLE_DOCTOR')) {
|
||||
$memberClinicId = $this->clinicDoctorAccess->memberClinicId($user);
|
||||
if ($memberClinicId !== null) {
|
||||
return ['clinic', $memberClinicId];
|
||||
}
|
||||
$doctor = $this->doctorRepo->findByUser($user);
|
||||
return ['doctor', $doctor?->getId()];
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ class PatientController extends BaseController
|
||||
private readonly \App\Patient\Repository\SessionPaymentRepository $sessionPaymentRepo,
|
||||
private readonly \App\Patient\Repository\SessionAuditLogRepository $sessionAuditRepo,
|
||||
private readonly SecretaryAccessChecker $secretaryAccess,
|
||||
private readonly \App\Clinic\Security\ClinicDoctorAccessChecker $clinicDoctorAccess,
|
||||
private readonly LoggerInterface $logger,
|
||||
) {}
|
||||
|
||||
@@ -144,6 +145,7 @@ class PatientController extends BaseController
|
||||
public function chargeWallet(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'payments', 'create');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'payments', 'create');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$record = $this->recordRepo->findByUuid($uuid);
|
||||
if ($record === null || !$this->ownsRecord($record, $entityType, $entityId, $user)) {
|
||||
@@ -180,6 +182,7 @@ class PatientController extends BaseController
|
||||
public function withdrawWallet(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'payments', 'create');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'payments', 'create');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$record = $this->recordRepo->findByUuid($uuid);
|
||||
if ($record === null || !$this->ownsRecord($record, $entityType, $entityId, $user)) {
|
||||
@@ -240,6 +243,7 @@ class PatientController extends BaseController
|
||||
public function createCall(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'patients', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'patients', 'update');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$record = $this->recordRepo->findByUuid($uuid);
|
||||
if ($record === null || !$this->ownsRecord($record, $entityType, $entityId, $user)) {
|
||||
@@ -277,6 +281,7 @@ class PatientController extends BaseController
|
||||
public function deleteCall(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'patients', 'delete');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'patients', 'delete');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$call = $this->callRepo->findByUuid($uuid);
|
||||
if ($call === null || !$this->ownsRecord($call->getRecord(), $entityType, $entityId, $user)) {
|
||||
@@ -309,6 +314,7 @@ class PatientController extends BaseController
|
||||
public function createMessage(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'patients', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'patients', 'update');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$record = $this->recordRepo->findByUuid($uuid);
|
||||
if ($record === null || !$this->ownsRecord($record, $entityType, $entityId, $user)) {
|
||||
@@ -335,6 +341,7 @@ class PatientController extends BaseController
|
||||
public function deleteMessage(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'patients', 'delete');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'patients', 'delete');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$message = $this->messageRepo->findByUuid($uuid);
|
||||
if ($message === null || !$this->ownsRecord($message->getRecord(), $entityType, $entityId, $user)) {
|
||||
@@ -370,6 +377,7 @@ class PatientController extends BaseController
|
||||
public function createNote(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'patients', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'patients', 'update');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$record = $this->recordRepo->findByUuid($uuid);
|
||||
if ($record === null || !$this->ownsRecord($record, $entityType, $entityId, $user)) {
|
||||
@@ -393,6 +401,7 @@ class PatientController extends BaseController
|
||||
public function updateNote(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'patients', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'patients', 'update');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$note = $this->noteRepo->findByUuid($uuid);
|
||||
if ($note === null || !$this->ownsRecord($note->getRecord(), $entityType, $entityId, $user)) {
|
||||
@@ -419,6 +428,7 @@ class PatientController extends BaseController
|
||||
public function deleteNote(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'patients', 'delete');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'patients', 'delete');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$note = $this->noteRepo->findByUuid($uuid);
|
||||
if ($note === null || !$this->ownsRecord($note->getRecord(), $entityType, $entityId, $user)) {
|
||||
@@ -451,6 +461,7 @@ class PatientController extends BaseController
|
||||
public function createMedicalRecord(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'patients', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'patients', 'update');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$record = $this->recordRepo->findByUuid($uuid);
|
||||
if ($record === null || !$this->ownsRecord($record, $entityType, $entityId, $user)) {
|
||||
@@ -476,6 +487,7 @@ class PatientController extends BaseController
|
||||
public function updateMedicalRecord(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'patients', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'patients', 'update');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$medical = $this->medicalRepo->findByUuid($uuid);
|
||||
if ($medical === null || !$this->ownsRecord($medical->getRecord(), $entityType, $entityId, $user)) {
|
||||
@@ -507,6 +519,7 @@ class PatientController extends BaseController
|
||||
public function deleteMedicalRecord(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'patients', 'delete');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'patients', 'delete');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$medical = $this->medicalRepo->findByUuid($uuid);
|
||||
if ($medical === null || !$this->ownsRecord($medical->getRecord(), $entityType, $entityId, $user)) {
|
||||
@@ -539,6 +552,7 @@ class PatientController extends BaseController
|
||||
public function uploadAttachment(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'patients', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'patients', 'update');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$record = $this->recordRepo->findByUuid($uuid);
|
||||
if ($record === null || !$this->ownsRecord($record, $entityType, $entityId, $user)) {
|
||||
@@ -562,6 +576,7 @@ class PatientController extends BaseController
|
||||
public function deleteAttachment(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'patients', 'delete');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'patients', 'delete');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$attachment = $this->attachmentRepo->findByUuid($uuid);
|
||||
if ($attachment === null || !$this->ownsRecord($attachment->getRecord(), $entityType, $entityId, $user)) {
|
||||
@@ -700,6 +715,7 @@ class PatientController extends BaseController
|
||||
public function create(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'patients', 'create');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'patients', 'create');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$this->assertPatientGate($entityType, $entityId);
|
||||
|
||||
@@ -794,6 +810,7 @@ class PatientController extends BaseController
|
||||
public function update(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'patients', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'patients', 'update');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$this->assertPatientGate($entityType, $entityId);
|
||||
|
||||
@@ -1004,6 +1021,7 @@ class PatientController extends BaseController
|
||||
public function createSession(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'patients', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'patients', 'update');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$this->assertPatientGate($entityType, $entityId);
|
||||
|
||||
@@ -1048,6 +1066,7 @@ class PatientController extends BaseController
|
||||
public function updateSession(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'patients', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'patients', 'update');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$this->assertPatientGate($entityType, $entityId);
|
||||
|
||||
@@ -1124,6 +1143,7 @@ class PatientController extends BaseController
|
||||
public function addSessionPayment(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'payments', 'create');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'payments', 'create');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$this->assertPatientGate($entityType, $entityId);
|
||||
|
||||
@@ -1150,6 +1170,7 @@ class PatientController extends BaseController
|
||||
public function updateSessionPayment(string $uuid, string $paymentUuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'payments', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'payments', 'update');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$this->assertPatientGate($entityType, $entityId);
|
||||
|
||||
@@ -1172,6 +1193,7 @@ class PatientController extends BaseController
|
||||
public function deleteSessionPayment(string $uuid, string $paymentUuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'payments', 'delete');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'payments', 'delete');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$this->assertPatientGate($entityType, $entityId);
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ class PaymentController extends BaseController
|
||||
private readonly PaymentManager $paymentManager,
|
||||
private readonly SiteConfigRepository $configRepo,
|
||||
private readonly SecretaryAccessChecker $secretaryAccess,
|
||||
private readonly \App\Clinic\Security\ClinicDoctorAccessChecker $clinicDoctorAccess,
|
||||
private readonly string $appBaseUrl,
|
||||
private readonly string $allowedFrontendHosts = '',
|
||||
) {}
|
||||
@@ -509,6 +510,7 @@ class PaymentController extends BaseController
|
||||
public function myPayments(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'payments', 'view');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'payments', 'view');
|
||||
$page = max(1, (int) $request->query->get('page', 1));
|
||||
$limit = min(100, max(1, (int) $request->query->get('limit', 20)));
|
||||
$status = $request->query->get('status');
|
||||
|
||||
@@ -28,6 +28,7 @@ class PaymentMethodController extends BaseController
|
||||
public function __construct(
|
||||
private readonly PaymentMethodService $service,
|
||||
private readonly SecretaryAccessChecker $secretaryAccess,
|
||||
private readonly \App\Clinic\Security\ClinicDoctorAccessChecker $clinicDoctorAccess,
|
||||
) {}
|
||||
|
||||
/** نقش مجاز + مجوز منشی روی منبع payments (روشهای پرداخت زیرمجموعهٔ مالی است). */
|
||||
@@ -35,6 +36,7 @@ class PaymentMethodController extends BaseController
|
||||
{
|
||||
$this->assertRole($user);
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'payments', $action);
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'payments', $action);
|
||||
}
|
||||
|
||||
// ---- Bank accounts -----------------------------------------------------
|
||||
|
||||
@@ -39,6 +39,7 @@ class SmsWalletController extends BaseController
|
||||
private readonly ClinicRepository $clinicRepo,
|
||||
private readonly \App\Config\Repository\SiteConfigRepository $configRepo,
|
||||
private readonly \App\Secretary\Security\SecretaryAccessChecker $secretaryAccess,
|
||||
private readonly \App\Clinic\Security\ClinicDoctorAccessChecker $clinicDoctorAccess,
|
||||
private readonly string $appBaseUrl,
|
||||
) {}
|
||||
|
||||
@@ -52,6 +53,7 @@ class SmsWalletController extends BaseController
|
||||
public function balance(#[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'sms', 'view');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'sms', 'view');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
if ($entityId === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -72,6 +74,7 @@ class SmsWalletController extends BaseController
|
||||
public function charge(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'sms', 'create');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'sms', 'create');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
if ($entityId === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -106,6 +109,7 @@ class SmsWalletController extends BaseController
|
||||
public function logs(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'sms', 'view');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'sms', 'view');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
if ($entityId === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -130,6 +134,7 @@ class SmsWalletController extends BaseController
|
||||
public function getSettings(#[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'sms', 'view');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'sms', 'view');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
if ($entityId === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -155,6 +160,7 @@ class SmsWalletController extends BaseController
|
||||
public function updateSettings(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'sms', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'sms', 'update');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
if ($entityId === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -278,6 +284,10 @@ class SmsWalletController extends BaseController
|
||||
private function resolveEntity(User $user): array
|
||||
{
|
||||
if ($user->hasRole('ROLE_DOCTOR')) {
|
||||
$memberClinicId = $this->clinicDoctorAccess->memberClinicId($user);
|
||||
if ($memberClinicId !== null) {
|
||||
return ['clinic', $memberClinicId];
|
||||
}
|
||||
$doctor = $this->doctorRepo->findByUser($user);
|
||||
return $doctor !== null ? ['doctor', $doctor->getId()] : ['doctor', null];
|
||||
}
|
||||
|
||||
@@ -26,12 +26,14 @@ class StaffController extends BaseController
|
||||
private readonly DoctorRepository $doctorRepo,
|
||||
private readonly ClinicRepository $clinicRepo,
|
||||
private readonly SecretaryAccessChecker $secretaryAccess,
|
||||
private readonly \App\Clinic\Security\ClinicDoctorAccessChecker $clinicDoctorAccess,
|
||||
) {}
|
||||
|
||||
#[Route('/api/v1/staff', methods: ['GET'])]
|
||||
public function list(#[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'staff', 'view');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'staff', 'view');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
if ($entityId === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -49,6 +51,7 @@ class StaffController extends BaseController
|
||||
public function create(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'staff', 'create');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'staff', 'create');
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
if ($entityId === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -76,6 +79,7 @@ class StaffController extends BaseController
|
||||
public function update(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'staff', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'staff', 'update');
|
||||
$staff = $this->staffRepo->findByUuid($uuid);
|
||||
if ($staff === null) {
|
||||
return $this->error(ErrorCodes::ERR_STAFF_NOT_FOUND, ErrorCodes::message(ErrorCodes::ERR_STAFF_NOT_FOUND), 404);
|
||||
@@ -104,6 +108,7 @@ class StaffController extends BaseController
|
||||
public function toggle(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'staff', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'staff', 'update');
|
||||
$staff = $this->staffRepo->findByUuid($uuid);
|
||||
if ($staff === null) {
|
||||
return $this->error(ErrorCodes::ERR_STAFF_NOT_FOUND, ErrorCodes::message(ErrorCodes::ERR_STAFF_NOT_FOUND), 404);
|
||||
@@ -122,6 +127,10 @@ class StaffController extends BaseController
|
||||
private function resolveEntity(User $user): array
|
||||
{
|
||||
if ($user->hasRole('ROLE_DOCTOR')) {
|
||||
$memberClinicId = $this->clinicDoctorAccess->memberClinicId($user);
|
||||
if ($memberClinicId !== null) {
|
||||
return ['clinic', $memberClinicId];
|
||||
}
|
||||
$doctor = $this->doctorRepo->findByUser($user);
|
||||
return $doctor !== null ? ['doctor', $doctor->getId()] : ['doctor', null];
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ class TenantTagController extends BaseController
|
||||
private readonly ClinicRepository $clinicRepo,
|
||||
private readonly UserActiveContextRepository $contextRepo,
|
||||
private readonly SecretaryAccessChecker $secretaryAccess,
|
||||
private readonly \App\Clinic\Security\ClinicDoctorAccessChecker $clinicDoctorAccess,
|
||||
) {}
|
||||
|
||||
/** تگها ابزار پروندهٔ بیمار هم هستند؛ مشاهده با tags.view یا patients.view مجاز است. */
|
||||
@@ -65,6 +66,7 @@ class TenantTagController extends BaseController
|
||||
public function create(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'tags', 'create');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'tags', 'create');
|
||||
[$type, $id] = $this->resolveEntity($user);
|
||||
if ($id === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
|
||||
@@ -94,6 +96,7 @@ class TenantTagController extends BaseController
|
||||
public function update(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'tags', 'update');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'tags', 'update');
|
||||
$tag = $this->ownedTag($uuid, $user);
|
||||
if ($tag === null) {
|
||||
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'برچسب یافت نشد', 404);
|
||||
@@ -127,6 +130,7 @@ class TenantTagController extends BaseController
|
||||
public function delete(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->secretaryAccess->denyUnlessGranted($user, 'tags', 'delete');
|
||||
$this->clinicDoctorAccess->denyUnlessGranted($user, 'tags', 'delete');
|
||||
$tag = $this->ownedTag($uuid, $user);
|
||||
if ($tag === null) {
|
||||
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'برچسب یافت نشد', 404);
|
||||
@@ -154,6 +158,10 @@ class TenantTagController extends BaseController
|
||||
private function resolveEntity(User $user): array
|
||||
{
|
||||
if ($user->hasRole('ROLE_DOCTOR')) {
|
||||
$memberClinicId = $this->clinicDoctorAccess->memberClinicId($user);
|
||||
if ($memberClinicId !== null) {
|
||||
return ['clinic', $memberClinicId];
|
||||
}
|
||||
$doctor = $this->doctorRepo->findByUser($user);
|
||||
return ['doctor', $doctor?->getId()];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user