Files
freescout/tests/Feature/ConversationChangeCustomerTest.php
2026-02-22 01:03:32 -08:00

127 lines
4.3 KiB
PHP

<?php
namespace Tests\Feature;
use App\Conversation;
use App\Customer;
use App\Email;
use App\Mailbox;
use App\Thread;
use App\User;
//use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ConversationChangeCustomerTest extends TestCase
{
// This cleans existing tables.
//use RefreshDatabase;
private $admin;
private $unprivUser;
private $mailbox;
private $conversation;
private $originalCustomer;
private $attackerCustomer;
protected function setUp(): void
{
parent::setUp();
// Without it csrf_token() returns empty string.
\Session::start();
// Create admin user
$this->admin = factory(User::class)->create([
'role' => User::ROLE_ADMIN,
]);
// Create mailbox
$this->mailbox = factory(Mailbox::class)->create();
$this->mailbox->users()->sync([$this->admin->id]);
// Create unprivileged user with NO mailbox access
$this->unprivUser = factory(User::class)->create([
'role' => User::ROLE_USER,
]);
// Create customers
$this->originalCustomer = factory(Customer::class)->create([
'first_name' => 'Original',
'last_name' => 'Customer',
]);
$this->originalCustomer->syncEmails(['original.customer@example.org']);
$this->attackerCustomer = factory(Customer::class)->create([
'first_name' => 'Attacker',
'last_name' => 'Evil',
]);
$this->attackerCustomer->syncEmails(['attacker@example.org']);
// Create conversation belonging to original customer.
$this->conversation = factory(Conversation::class)->create([
'mailbox_id' => $this->mailbox->id,
'customer_id' => $this->originalCustomer->id,
'customer_email' => 'original.customer@example.org',
'status' => Conversation::STATUS_ACTIVE,
'state' => Conversation::STATE_PUBLISHED,
]);
}
/**
* Test that a user without mailbox access cannot change a conversation's customer.
* This is the regression test for the authorization bypass where changeCustomer()
* executed unconditionally even when permission checks set an error message.
*/
#[RequiresPhp('>=8.3.0')]
public function testUnauthorizedUserCannotChangeCustomer()
{
$response = $this->actingAs($this->unprivUser)
->post('/conversation/ajax', [
'action' => 'conversation_change_customer',
'conversation_id' => $this->conversation->id,
'customer_email' => 'attacker@example.org',
'_token' => csrf_token(),
], [
'X-Requested-With' => 'XMLHttpRequest',
]);
$response->assertStatus(200);
$json = $response->json();
$this->assertEquals('Not enough permissions', $json['msg']);
// The customer must NOT have changed
$this->conversation->refresh();
$this->assertEquals('original.customer@example.org', $this->conversation->customer_email);
$this->assertEquals($this->originalCustomer->id, $this->conversation->customer_id);
}
/**
* Test that an authorized admin user CAN change a conversation's customer.
* This ensures the fix does not break normal functionality.
*/
#[RequiresPhp('>=8.3.0')]
public function testAuthorizedUserCanChangeCustomer()
{
$response = $this->actingAs($this->admin)
->post('/conversation/ajax', [
'action' => 'conversation_change_customer',
'conversation_id' => $this->conversation->id,
'customer_email' => 'attacker@example.org',
'_token' => csrf_token(),
], [
'X-Requested-With' => 'XMLHttpRequest',
]);
$response->assertStatus(200);
$json = $response->json();
$this->assertEquals('success', $json['status']);
$this->assertEmpty($json['msg']);
// The customer SHOULD have changed.
$this->conversation->refresh();
$this->assertEquals('attacker@example.org', $this->conversation->customer_email);
$this->assertEquals($this->attackerCustomer->id, $this->conversation->customer_id);
}
}