diff --git a/app-modules/contact/src/Filament/Resources/ContactResource/RelationManagers/ServiceRequestsRelationManager.php b/app-modules/contact/src/Filament/Resources/ContactResource/RelationManagers/ServiceRequestsRelationManager.php index f94f19ea8..5e933fb3a 100644 --- a/app-modules/contact/src/Filament/Resources/ContactResource/RelationManagers/ServiceRequestsRelationManager.php +++ b/app-modules/contact/src/Filament/Resources/ContactResource/RelationManagers/ServiceRequestsRelationManager.php @@ -69,6 +69,17 @@ public function table(Table $table): Table { return $table ->recordTitleAttribute('id') + ->modifyQueryUsing(function($query){ + $query->when(! auth()->user()->hasRole('authorization.super_admin'), function (Builder $q) { + return $q->whereHas('priority.type.managers', function (Builder $query): void { + $query->where('teams.id', auth()->user()->teams()->first()?->getKey()); + })->orWhereHas('priority.type.auditors', function (Builder $query): void { + $query->where('teams.id', auth()->user()->teams()->first()?->getKey()); + })->whereHas('respondent', function (Builder $query) { + $query->where('respondent_id', $this->getOwnerRecord()->getKey()); + }); + }); + }) ->columns([ IdColumn::make(), TextColumn::make('service_request_number') diff --git a/app-modules/service-management/src/Filament/Resources/ServiceRequestResource/Pages/CreateServiceRequest.php b/app-modules/service-management/src/Filament/Resources/ServiceRequestResource/Pages/CreateServiceRequest.php index fd885282a..016820437 100644 --- a/app-modules/service-management/src/Filament/Resources/ServiceRequestResource/Pages/CreateServiceRequest.php +++ b/app-modules/service-management/src/Filament/Resources/ServiceRequestResource/Pages/CreateServiceRequest.php @@ -83,7 +83,14 @@ public function form(Form $form): Form Grid::make() ->schema([ Select::make('type_id') - ->options(ServiceRequestType::pluck('name', 'id')) + ->options(ServiceRequestType::when(!auth()->user()->hasRole('authorization.super_admin'),function(Builder $query){ + $query->whereHas('managers', function (Builder $query): void { + $query->where('teams.id', auth()->user()->teams()->first()?->getKey()); + })->orWhereHas('auditors', function (Builder $query): void { + $query->where('teams.id', auth()->user()->teams()->first()?->getKey()); + }); + }) + ->pluck('name', 'id')) ->afterStateUpdated(fn (Set $set) => $set('priority_id', null)) ->label('Type') ->required() diff --git a/app-modules/service-management/src/Filament/Resources/ServiceRequestResource/Pages/EditServiceRequest.php b/app-modules/service-management/src/Filament/Resources/ServiceRequestResource/Pages/EditServiceRequest.php index ae46dcbd1..a6f983353 100644 --- a/app-modules/service-management/src/Filament/Resources/ServiceRequestResource/Pages/EditServiceRequest.php +++ b/app-modules/service-management/src/Filament/Resources/ServiceRequestResource/Pages/EditServiceRequest.php @@ -99,6 +99,13 @@ public function form(Form $form): Form fn (ServiceRequest $record) => ServiceRequestType::withTrashed() ->whereKey($record->priority?->type_id) ->orWhereNull('deleted_at') + ->when(!auth()->user()->hasRole('authorization.super_admin'),function(Builder $query){ + $query->whereHas('managers', function (Builder $query): void { + $query->where('teams.id', auth()->user()->teams()->first()?->getKey()); + })->orWhereHas('auditors', function (Builder $query): void { + $query->where('teams.id', auth()->user()->teams()->first()?->getKey()); + }); + }) ->orderBy('name') ->pluck('name', 'id') ) diff --git a/app-modules/service-management/src/Filament/Resources/ServiceRequestResource/Pages/ListServiceRequests.php b/app-modules/service-management/src/Filament/Resources/ServiceRequestResource/Pages/ListServiceRequests.php index 24af61276..5153d1d93 100644 --- a/app-modules/service-management/src/Filament/Resources/ServiceRequestResource/Pages/ListServiceRequests.php +++ b/app-modules/service-management/src/Filament/Resources/ServiceRequestResource/Pages/ListServiceRequests.php @@ -36,6 +36,7 @@ namespace AidingApp\ServiceManagement\Filament\Resources\ServiceRequestResource\Pages; +use AidingApp\Assistant\Models\PromptType; use Filament\Tables\Table; use Filament\Actions\CreateAction; use AidingApp\Contact\Models\Contact; @@ -57,6 +58,7 @@ use AidingApp\ServiceManagement\Models\ServiceRequestPriority; use AidingApp\ServiceManagement\Enums\SystemServiceRequestClassification; use AidingApp\ServiceManagement\Filament\Resources\ServiceRequestResource; +use Filament\Notifications\Notification; class ListServiceRequests extends ListRecords { @@ -75,13 +77,13 @@ public function table(Table $table): Table ], 'status', ]) - ->when(! auth()->user()->hasRole('authorization.super_admin'), function (Builder $q) { - return $q->whereHas('priority.type.managers', function (Builder $query): void { - $query->where('teams.id', auth()->user()->teams()->first()?->getKey()); - })->orWhereHas('priority.type.auditors', function (Builder $query): void { - $query->where('teams.id', auth()->user()->teams()->first()?->getKey()); - }); - })) + ->when(! auth()->user()->hasRole('authorization.super_admin'), function (Builder $q) { + return $q->whereHas('priority.type.managers', function (Builder $query): void { + $query->where('teams.id', auth()->user()->teams()->first()?->getKey()); + })->orWhereHas('priority.type.auditors', function (Builder $query): void { + $query->where('teams.id', auth()->user()->teams()->first()?->getKey()); + }); + })) ->columns([ IdColumn::make(), TextColumn::make('service_request_number') @@ -146,7 +148,26 @@ public function table(Table $table): Table ]) ->bulkActions([ BulkActionGroup::make([ - DeleteBulkAction::make(), + DeleteBulkAction::make() + ->action(function ($records) { + $deletedRecordsCount = ServiceRequest::query() + ->whereKey($records) + ->when(!auth()->user()->hasRole('authorization.super_admin'), function (Builder $q) { + return $q->whereHas('priority.type.managers', function (Builder $query): void { + $query->where('teams.id', auth()->user()->teams()->first()?->getKey()); + })->orWhereHas('priority.type.auditors', function (Builder $query): void { + $query->where('teams.id', auth()->user()->teams()->first()?->getKey()); + }); + }) + ->delete(); + + Notification::make() + ->title('Deleted ' . $deletedRecordsCount . ' prompt types') + ->body(($deletedRecordsCount < $records->count()) ? ($records->count() - $deletedRecordsCount) . ' service requests were not deleted because you\'re not an auditor or manager of it.' : null) + ->success() + ->send(); + }) + ->fetchSelectedRecords(false), ]), ]) ->defaultSort('created_at', 'desc') diff --git a/app-modules/service-management/src/Policies/ServiceRequestPolicy.php b/app-modules/service-management/src/Policies/ServiceRequestPolicy.php index 691ff524e..c9f29e934 100644 --- a/app-modules/service-management/src/Policies/ServiceRequestPolicy.php +++ b/app-modules/service-management/src/Policies/ServiceRequestPolicy.php @@ -96,6 +96,14 @@ public function view(Authenticatable $authenticatable, ServiceRequest $serviceRe public function create(Authenticatable $authenticatable): Response { + if (! auth()->user()->hasRole('authorization.super_admin')) { + $team = auth()->user()->teams()->first(); + + if (!$team?->managableServiceRequestTypes()->exists() && !$team?->auditableServiceRequestTypes()->exists()) { + return Response::deny("You don't have permission to view this service request because you're not an auditor or manager."); + } + } + return $authenticatable->canOrElse( abilities: 'service_request.create', denyResponse: 'You do not have permission to create service requests.' @@ -112,6 +120,18 @@ public function update(Authenticatable $authenticatable, ServiceRequest $service return Response::deny('Closed service request cannot be edited.'); } + if (! auth()->user()->hasRole('authorization.super_admin')) { + $team = auth()->user()->teams()->first(); + + if (! $serviceRequest?->priority?->type?->managers()->exists() && ! $serviceRequest?->priority?->type?->auditors()->exists()) { + return Response::deny("You don't have permission to update this service request because you're not an auditor or manager."); + } + + if (! $serviceRequest?->priority?->type?->managers->contains('id', $team?->getKey()) && ! $serviceRequest?->priority?->type?->auditors->contains('id', $team?->getKey())) { + return Response::deny("You don't have permission to update this service request because you're not an auditor or manager."); + } + } + return $authenticatable->canOrElse( abilities: ['service_request.*.update', "service_request.{$serviceRequest->id}.update"], denyResponse: 'You do not have permission to update this service request.' @@ -124,6 +144,18 @@ public function delete(Authenticatable $authenticatable, ServiceRequest $service return Response::deny('You do not have permission to delete this service request.'); } + if (! auth()->user()->hasRole('authorization.super_admin')) { + $team = auth()->user()->teams()->first(); + + if (! $serviceRequest?->priority?->type?->managers()->exists() && ! $serviceRequest?->priority?->type?->auditors()->exists()) { + return Response::deny("You don't have permission to delete this service request because you're not an auditor or manager."); + } + + if (! $serviceRequest?->priority?->type?->managers->contains('id', $team?->getKey()) && ! $serviceRequest?->priority?->type?->auditors->contains('id', $team?->getKey())) { + return Response::deny("You don't have permission to delete this service request because you're not an auditor or manager."); + } + } + return $authenticatable->canOrElse( abilities: ['service_request.*.delete', "service_request.{$serviceRequest->id}.delete"], denyResponse: 'You do not have permission to delete this service request.' @@ -136,6 +168,18 @@ public function restore(Authenticatable $authenticatable, ServiceRequest $servic return Response::deny('You do not have permission to restore this service request.'); } + if (! auth()->user()->hasRole('authorization.super_admin')) { + $team = auth()->user()->teams()->first(); + + if (! $serviceRequest?->priority?->type?->managers()->exists() && ! $serviceRequest?->priority?->type?->auditors()->exists()) { + return Response::deny("You don't have permission to restore this service request because you're not an auditor or manager."); + } + + if (! $serviceRequest?->priority?->type?->managers->contains('id', $team?->getKey()) && ! $serviceRequest?->priority?->type?->auditors->contains('id', $team?->getKey())) { + return Response::deny("You don't have permission to restore this service request because you're not an auditor or manager."); + } + } + return $authenticatable->canOrElse( abilities: ['service_request.*.restore', "service_request.{$serviceRequest->id}.restore"], denyResponse: 'You do not have permission to restore this service request.' @@ -148,6 +192,18 @@ public function forceDelete(Authenticatable $authenticatable, ServiceRequest $se return Response::deny('You do not have permission to permanently delete this service request.'); } + if (! auth()->user()->hasRole('authorization.super_admin')) { + $team = auth()->user()->teams()->first(); + + if (! $serviceRequest?->priority?->type?->managers()->exists() && ! $serviceRequest?->priority?->type?->auditors()->exists()) { + return Response::deny("You don't have permission to permanently delete this service request because you're not an auditor or manager."); + } + + if (! $serviceRequest?->priority?->type?->managers->contains('id', $team?->getKey()) && ! $serviceRequest?->priority?->type?->auditors->contains('id', $team?->getKey())) { + return Response::deny("You don't have permission to permanently delete service request because you're not an auditor or manager."); + } + } + return $authenticatable->canOrElse( abilities: ['service_request.*.force-delete', "service_request.{$serviceRequest->id}.force-delete"], denyResponse: 'You do not have permission to permanently delete this service request.' diff --git a/app-modules/service-management/tests/ServiceRequest/CreateServiceRequestTest.php b/app-modules/service-management/tests/ServiceRequest/CreateServiceRequestTest.php index c2cd19963..58bf4afb9 100644 --- a/app-modules/service-management/tests/ServiceRequest/CreateServiceRequestTest.php +++ b/app-modules/service-management/tests/ServiceRequest/CreateServiceRequestTest.php @@ -56,6 +56,8 @@ use AidingApp\ServiceManagement\Filament\Resources\ServiceRequestResource; use AidingApp\ServiceManagement\Tests\RequestFactories\CreateServiceRequestRequestFactory; use AidingApp\ServiceManagement\Filament\Resources\ServiceRequestResource\Pages\CreateServiceRequest; +use AidingApp\ServiceManagement\Models\ServiceRequestType; +use AidingApp\Team\Models\Team; test('A successful action on the CreateServiceRequest page', function () { asSuperAdmin() @@ -141,14 +143,25 @@ test('CreateServiceRequest is gated with proper access control', function () { $user = User::factory()->licensed(LicenseType::cases())->create(); + $team = Team::factory()->create(); + + $user->teams()->attach($team); + + $user->refresh(); + actingAs($user) ->get( ServiceRequestResource::getUrl('create') )->assertForbidden(); + livewire(CreateServiceRequest::class) ->assertForbidden(); + $serviceRequestType = ServiceRequestType::factory()->create(); + + $serviceRequestType->auditors()->attach($team); + $user->givePermissionTo('service_request.view-any'); $user->givePermissionTo('service_request.create'); @@ -201,6 +214,12 @@ $user = User::factory()->licensed(LicenseType::cases())->create(); + $team = Team::factory()->create(); + + $user->teams()->attach($team); + + $user->refresh(); + actingAs($user) ->get( ServiceRequestResource::getUrl('create') @@ -216,6 +235,10 @@ $settings->save(); + $serviceRequestType = ServiceRequestType::factory()->create(); + + $serviceRequestType->auditors()->attach($team); + actingAs($user) ->get( ServiceRequestResource::getUrl('create') @@ -239,3 +262,41 @@ expect($serviceRequest->division->id)->toEqual($request['division_id']); }); + +test('cannot create service requests if user is not an auditor or manager of the service request type',function(){ + + $settings = app(LicenseSettings::class); + + $settings->data->addons->serviceManagement = false; + + $settings->save(); + + $user = User::factory()->licensed(LicenseType::cases())->create(); + + $team = Team::factory()->create(); + + $user->teams()->attach($team); + + $user->refresh(); + + actingAs($user) + ->get( + ServiceRequestResource::getUrl('create') + )->assertForbidden(); + + $user->givePermissionTo('service_request.view-any'); + $user->givePermissionTo('service_request.create'); + + $settings->data->addons->serviceManagement = true; + + $settings->save(); + + livewire(CreateServiceRequest::class) + ->assertForbidden(); + + actingAs($user) + ->get( + ServiceRequestResource::getUrl('create') + )->assertForbidden(); + +}); diff --git a/app-modules/service-management/tests/ServiceRequest/EditServiceRequestTest.php b/app-modules/service-management/tests/ServiceRequest/EditServiceRequestTest.php index d98cd9527..bdb3e7d59 100644 --- a/app-modules/service-management/tests/ServiceRequest/EditServiceRequestTest.php +++ b/app-modules/service-management/tests/ServiceRequest/EditServiceRequestTest.php @@ -46,6 +46,7 @@ use Illuminate\Support\Facades\Notification; use AidingApp\Authorization\Enums\LicenseType; +use AidingApp\Contact\Models\Contact; use AidingApp\ServiceManagement\Models\ServiceRequest; use AidingApp\ServiceManagement\Models\ServiceRequestType; use AidingApp\ServiceManagement\Models\ServiceRequestStatus; @@ -55,6 +56,7 @@ use AidingApp\ServiceManagement\Notifications\SendClosedServiceFeedbackNotification; use AidingApp\ServiceManagement\Tests\RequestFactories\EditServiceRequestRequestFactory; use AidingApp\ServiceManagement\Filament\Resources\ServiceRequestResource\Pages\EditServiceRequest; +use AidingApp\Team\Models\Team; test('A successful action on the EditServiceRequest page', function () { $serviceRequest = ServiceRequest::factory([ @@ -191,10 +193,23 @@ test('EditServiceRequest is gated with proper access control', function () { $user = User::factory()->licensed(LicenseType::cases())->create(); - $serviceRequest = ServiceRequest::factory([ + $team = Team::factory()->create(); + + $user->teams()->attach($team); + + $user->refresh(); + + $serviceRequestType = ServiceRequestType::factory()->create(); + + $serviceRequestType->managers()->attach($team); + + $serviceRequest = ServiceRequest::factory()->state([ 'status_id' => ServiceRequestStatus::factory()->create([ - 'classification' => SystemServiceRequestClassification::Waiting, - ])->id, + 'classification' => SystemServiceRequestClassification::Open, + ])->getKey(), + 'priority_id' => ServiceRequestPriority::factory()->create([ + 'type_id' => $serviceRequestType->getKey(), + ])->getKey(), ])->create(); actingAs($user) @@ -262,12 +277,25 @@ $user = User::factory()->licensed(LicenseType::cases())->create(); + $team = Team::factory()->create(); + + $user->teams()->attach($team); + + $user->refresh(); + $user->givePermissionTo('service_request.view-any'); $user->givePermissionTo('service_request.*.update'); - $serviceRequest = ServiceRequest::factory([ + $serviceRequestType = ServiceRequestType::factory()->create(); + + $serviceRequestType->managers()->attach($team); + + $serviceRequest = ServiceRequest::factory()->state([ 'status_id' => ServiceRequestStatus::factory()->create([ - 'classification' => SystemServiceRequestClassification::Waiting, + 'classification' => SystemServiceRequestClassification::Open, + ])->getKey(), + 'priority_id' => ServiceRequestPriority::factory()->create([ + 'type_id' => $serviceRequestType->getKey(), ])->getKey(), ])->create(); @@ -319,10 +347,27 @@ $user = User::factory()->licensed(LicenseType::cases())->create(); - $serviceRequest = ServiceRequest::factory([ + $team = Team::factory()->create(); + + $user->teams()->attach($team); + + $user->refresh(); + + $serviceRequestType = ServiceRequestType::factory()->create([ + 'has_enabled_feedback_collection' => true, + 'has_enabled_csat' => true, + 'has_enabled_nps' => true, + ]); + + $serviceRequestType->managers()->attach($team); + + $serviceRequest = ServiceRequest::factory()->state([ 'status_id' => ServiceRequestStatus::factory()->create([ 'classification' => SystemServiceRequestClassification::Open, - ])->id, + ])->getKey(), + 'priority_id' => ServiceRequestPriority::factory()->create([ + 'type_id' => $serviceRequestType->getKey(), + ])->getKey(), ])->create(); $user->givePermissionTo('service_request.view-any'); @@ -333,14 +378,10 @@ $request = collect(EditServiceRequestRequestFactory::new([ 'status_id' => ServiceRequestStatus::factory()->create([ 'classification' => SystemServiceRequestClassification::Closed, - ])->id, + ])->getKey(), 'priority_id' => ServiceRequestPriority::factory()->create([ - 'type_id' => ServiceRequestType::factory()->create([ - 'has_enabled_feedback_collection' => true, - 'has_enabled_csat' => true, - 'has_enabled_nps' => true, - ])->id, - ])->id, + 'type_id' => $serviceRequestType->getKey(), + ])->getKey(), ])->create()); livewire(EditServiceRequest::class, [ @@ -373,3 +414,27 @@ SendClosedServiceFeedbackNotification::class ); }); + +test('service requests not authorized if user is not an auditor or manager of the service request type', function () { + $settings = app(LicenseSettings::class); + + $settings->data->addons->serviceManagement = true; + + $settings->save(); + + $user = User::factory()->licensed([Contact::getLicenseType()])->create(); + + $user->givePermissionTo('service_request.view-any'); + $user->givePermissionTo('service_request.*.update'); + + $user->refresh(); + + actingAs($user); + + $serviceRequest = ServiceRequest::factory() + ->create(); + + livewire(EditServiceRequest::class, [ + 'record' => $serviceRequest->getRouteKey(), + ])->assertForbidden(); +}); \ No newline at end of file diff --git a/app-modules/service-management/tests/ServiceRequest/ViewServiceRequestTest.php b/app-modules/service-management/tests/ServiceRequest/ViewServiceRequestTest.php index 96194f0b3..ea6acfb95 100644 --- a/app-modules/service-management/tests/ServiceRequest/ViewServiceRequestTest.php +++ b/app-modules/service-management/tests/ServiceRequest/ViewServiceRequestTest.php @@ -167,8 +167,6 @@ $user->givePermissionTo('service_request.view-any'); $user->givePermissionTo('service_request.*.view'); - $user->refresh(); - actingAs($user); $serviceRequest = ServiceRequest::factory()