237 خطوط
8.0 KiB
PHP
237 خطوط
8.0 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Call;
|
|
use App\Models\FollowUp;
|
|
use App\Models\Lead;
|
|
use App\Models\Setting;
|
|
use App\Models\Team;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Spatie\Permission\Models\Permission;
|
|
use Spatie\Permission\Models\Role;
|
|
use Tests\TestCase;
|
|
|
|
class SecurityAuthorizationTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
public function test_guest_cannot_access_authenticated_api_routes(): void
|
|
{
|
|
$this->getJson('/api/leads')->assertUnauthorized();
|
|
$this->postJson('/api/auth/logout')->assertUnauthorized();
|
|
}
|
|
|
|
public function test_agent_cannot_modify_another_agents_lead(): void
|
|
{
|
|
[$agent, $otherAgent] = $this->makeAgents();
|
|
$lead = $this->makeLead(['assigned_to' => $otherAgent->id, 'is_unassigned' => false]);
|
|
|
|
$this->actingAs($agent)
|
|
->putJson("/api/leads/{$lead->id}", ['company' => 'Tampered Co'])
|
|
->assertForbidden();
|
|
|
|
$this->assertDatabaseMissing('leads', [
|
|
'id' => $lead->id,
|
|
'company' => 'Tampered Co',
|
|
]);
|
|
}
|
|
|
|
public function test_agent_cannot_delete_another_agents_lead(): void
|
|
{
|
|
[$agent, $otherAgent] = $this->makeAgents();
|
|
$lead = $this->makeLead(['assigned_to' => $otherAgent->id, 'is_unassigned' => false]);
|
|
|
|
$this->actingAs($agent)
|
|
->deleteJson("/api/leads/{$lead->id}")
|
|
->assertForbidden();
|
|
|
|
$this->assertDatabaseHas('leads', ['id' => $lead->id, 'deleted_at' => null]);
|
|
}
|
|
|
|
public function test_agent_cannot_bulk_delete_another_agents_leads(): void
|
|
{
|
|
[$agent, $otherAgent] = $this->makeAgents();
|
|
$lead = $this->makeLead(['assigned_to' => $otherAgent->id, 'is_unassigned' => false]);
|
|
|
|
$this->actingAs($agent)
|
|
->deleteJson('/api/leads/bulk-delete', ['ids' => [$lead->id]])
|
|
->assertForbidden();
|
|
|
|
$this->assertDatabaseHas('leads', ['id' => $lead->id, 'deleted_at' => null]);
|
|
}
|
|
|
|
public function test_agent_cannot_reassign_leads(): void
|
|
{
|
|
[$agent, $otherAgent] = $this->makeAgents();
|
|
$lead = $this->makeLead(['assigned_to' => $otherAgent->id, 'is_unassigned' => false]);
|
|
|
|
$this->actingAs($agent)
|
|
->postJson('/api/assignments/reassign', [
|
|
'lead_id' => $lead->id,
|
|
'agent_id' => $agent->id,
|
|
])
|
|
->assertForbidden();
|
|
|
|
$this->assertDatabaseHas('leads', [
|
|
'id' => $lead->id,
|
|
'assigned_to' => $otherAgent->id,
|
|
]);
|
|
}
|
|
|
|
public function test_agent_cannot_modify_another_users_follow_up(): void
|
|
{
|
|
[$agent, $otherAgent] = $this->makeAgents();
|
|
$lead = $this->makeLead(['assigned_to' => $otherAgent->id, 'is_unassigned' => false]);
|
|
$followUp = FollowUp::create([
|
|
'lead_id' => $lead->id,
|
|
'user_id' => $otherAgent->id,
|
|
'scheduled_at' => now()->addDay(),
|
|
'status' => 'pending',
|
|
]);
|
|
|
|
$this->actingAs($agent)
|
|
->patchJson("/api/follow-ups/{$followUp->id}/mark-done")
|
|
->assertForbidden();
|
|
|
|
$this->assertDatabaseHas('follow_ups', [
|
|
'id' => $followUp->id,
|
|
'status' => 'pending',
|
|
]);
|
|
}
|
|
|
|
public function test_agent_cannot_view_or_register_result_for_another_agents_call(): void
|
|
{
|
|
[$agent, $otherAgent] = $this->makeAgents();
|
|
$lead = $this->makeLead(['assigned_to' => $otherAgent->id, 'is_unassigned' => false]);
|
|
$call = Call::create([
|
|
'lead_id' => $lead->id,
|
|
'user_id' => $otherAgent->id,
|
|
'direction' => 'outbound',
|
|
'phone' => '09129998877',
|
|
]);
|
|
|
|
$this->actingAs($agent)
|
|
->getJson("/api/calls/{$call->id}")
|
|
->assertForbidden();
|
|
|
|
$this->actingAs($agent)
|
|
->postJson('/api/calls/register-result', [
|
|
'call_id' => $call->id,
|
|
'result' => 'پاسخ داد',
|
|
])
|
|
->assertForbidden();
|
|
|
|
$this->assertDatabaseHas('calls', [
|
|
'id' => $call->id,
|
|
'result' => null,
|
|
]);
|
|
}
|
|
|
|
public function test_agent_cannot_create_follow_up_for_unauthorized_lead(): void
|
|
{
|
|
[$agent, $otherAgent] = $this->makeAgents();
|
|
$lead = $this->makeLead(['assigned_to' => $otherAgent->id, 'is_unassigned' => false]);
|
|
|
|
$this->actingAs($agent)
|
|
->postJson('/api/follow-ups', [
|
|
'lead_id' => $lead->id,
|
|
'scheduled_at' => now()->addDay()->toISOString(),
|
|
'notes' => 'Unauthorized follow-up',
|
|
])
|
|
->assertForbidden();
|
|
|
|
$this->assertDatabaseMissing('follow_ups', [
|
|
'lead_id' => $lead->id,
|
|
'user_id' => $agent->id,
|
|
]);
|
|
}
|
|
|
|
public function test_phone_numbers_and_recordings_are_masked_without_permissions(): void
|
|
{
|
|
[$agent] = $this->makeAgents();
|
|
Setting::create(['key' => 'phone_mask_enabled', 'value' => 'true', 'group' => 'security', 'type' => 'boolean']);
|
|
$lead = $this->makeLead([
|
|
'assigned_to' => $agent->id,
|
|
'is_unassigned' => false,
|
|
'phone' => '09121234567',
|
|
'phone_secondary' => '02199887766',
|
|
]);
|
|
$call = Call::create([
|
|
'lead_id' => $lead->id,
|
|
'user_id' => $agent->id,
|
|
'direction' => 'outbound',
|
|
'phone' => '09121234567',
|
|
'recording_url' => 'https://recordings.example.test/call.mp3',
|
|
]);
|
|
|
|
$response = $this->actingAs($agent)
|
|
->getJson("/api/calls/{$call->id}")
|
|
->assertOk();
|
|
|
|
$this->assertStringNotContainsString('09121234567', $response->getContent());
|
|
$response->assertJsonPath('data.recording_url', null);
|
|
}
|
|
|
|
public function test_supervisor_cannot_access_other_team_lead_or_report(): void
|
|
{
|
|
$supervisorRole = Role::create(['name' => 'supervisor', 'guard_name' => 'web']);
|
|
$agentRole = Role::create(['name' => 'agent', 'guard_name' => 'web']);
|
|
$supervisor = User::factory()->create(['is_active' => true]);
|
|
$ownAgent = User::factory()->create(['is_active' => true]);
|
|
$otherAgent = User::factory()->create(['is_active' => true]);
|
|
$supervisor->assignRole($supervisorRole);
|
|
Permission::create(['name' => 'view_reports', 'guard_name' => 'web']);
|
|
$supervisor->givePermissionTo('view_reports');
|
|
$ownAgent->assignRole($agentRole);
|
|
$otherAgent->assignRole($agentRole);
|
|
|
|
$ownTeam = Team::create(['name' => 'Own Team', 'supervisor_id' => $supervisor->id, 'is_active' => true]);
|
|
$otherTeam = Team::create(['name' => 'Other Team', 'is_active' => true]);
|
|
$supervisor->teams()->attach($ownTeam->id);
|
|
$ownAgent->teams()->attach($ownTeam->id);
|
|
$otherAgent->teams()->attach($otherTeam->id);
|
|
|
|
$otherLead = $this->makeLead([
|
|
'assigned_to' => $otherAgent->id,
|
|
'team_id' => $otherTeam->id,
|
|
'is_unassigned' => false,
|
|
]);
|
|
|
|
$this->actingAs($supervisor)
|
|
->getJson("/api/leads/{$otherLead->id}")
|
|
->assertForbidden();
|
|
|
|
$this->actingAs($supervisor)
|
|
->getJson("/api/reports/agent-performance?agent_id={$otherAgent->id}")
|
|
->assertForbidden();
|
|
}
|
|
|
|
/**
|
|
* @return array{0: User, 1: User}
|
|
*/
|
|
private function makeAgents(): array
|
|
{
|
|
$agentRole = Role::create(['name' => 'agent', 'guard_name' => 'web']);
|
|
|
|
$agent = User::factory()->create(['is_active' => true]);
|
|
$otherAgent = User::factory()->create(['is_active' => true]);
|
|
|
|
$agent->assignRole($agentRole);
|
|
$otherAgent->assignRole($agentRole);
|
|
|
|
return [$agent, $otherAgent];
|
|
}
|
|
|
|
private function makeLead(array $attributes = []): Lead
|
|
{
|
|
return Lead::create(array_merge([
|
|
'company' => 'Acme',
|
|
'first_name' => 'Ali',
|
|
'last_name' => 'Karimi',
|
|
'phone' => fake()->unique()->numerify('021########'),
|
|
], $attributes));
|
|
}
|
|
}
|