422 lines
14 KiB
PHP
422 lines
14 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Security\Authorization;
|
|
|
|
use App\Models\Admin;
|
|
use App\Models\Bank;
|
|
use App\Models\Organization;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Livewire\Livewire;
|
|
use Namu\WireChat\Models\Conversation;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* WireChat Multi-Auth Tests
|
|
*
|
|
* Tests that WireChat components work correctly with multi-guard authentication
|
|
* for User, Organization, Bank, and Admin profiles.
|
|
*
|
|
* @group security
|
|
* @group authorization
|
|
* @group multi-guard
|
|
* @group wirechat
|
|
*/
|
|
class WireChatMultiAuthTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
/**
|
|
* Test user can access conversation they belong to
|
|
*
|
|
* @test
|
|
*/
|
|
public function user_can_access_conversation_they_belong_to()
|
|
{
|
|
$user = User::factory()->create();
|
|
$recipient = User::factory()->create();
|
|
$this->actingAs($user, 'web');
|
|
|
|
// Create conversation using sendMessageTo (same logic as Pay.php line 417)
|
|
$message = $user->sendMessageTo($recipient, 'Test message');
|
|
$conversation = $message->conversation;
|
|
|
|
$response = Livewire::test(
|
|
\App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
|
|
['conversationId' => $conversation->id]
|
|
);
|
|
|
|
$response->assertStatus(200);
|
|
}
|
|
|
|
/**
|
|
* Test user cannot access conversation they don't belong to
|
|
*
|
|
* @test
|
|
*/
|
|
public function user_cannot_access_conversation_they_dont_belong_to()
|
|
{
|
|
$user = User::factory()->create();
|
|
$otherUser = User::factory()->create();
|
|
$anotherUser = User::factory()->create();
|
|
$this->actingAs($user, 'web');
|
|
|
|
// Set active profile in session (required by getActiveProfile())
|
|
session([
|
|
'activeProfileType' => get_class($user),
|
|
'activeProfileId' => $user->id,
|
|
'active_guard' => 'web',
|
|
]);
|
|
|
|
// Create conversation between otherUser and anotherUser (not involving $user)
|
|
$message = $otherUser->sendMessageTo($anotherUser, 'Test message');
|
|
$conversation = $message->conversation;
|
|
|
|
$response = Livewire::test(
|
|
\App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
|
|
['conversationId' => $conversation->id]
|
|
);
|
|
|
|
$response->assertStatus(403);
|
|
}
|
|
|
|
/**
|
|
* Test organization can access conversation they belong to
|
|
*
|
|
* @test
|
|
*/
|
|
public function organization_can_access_conversation_they_belong_to()
|
|
{
|
|
$user = User::factory()->create();
|
|
$organization = Organization::factory()->create();
|
|
$organization->users()->attach($user->id);
|
|
$recipient = User::factory()->create();
|
|
|
|
$this->actingAs($organization, 'organization');
|
|
|
|
// Create conversation with organization as sender
|
|
$message = $organization->sendMessageTo($recipient, 'Test message');
|
|
$conversation = $message->conversation;
|
|
|
|
$response = Livewire::test(
|
|
\App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
|
|
['conversationId' => $conversation->id]
|
|
);
|
|
|
|
$response->assertStatus(200);
|
|
}
|
|
|
|
/**
|
|
* Test admin can access conversation they belong to
|
|
*
|
|
* @test
|
|
*/
|
|
public function admin_can_access_conversation_they_belong_to()
|
|
{
|
|
$user = User::factory()->create();
|
|
$admin = Admin::factory()->create();
|
|
$admin->users()->attach($user->id);
|
|
$recipient = User::factory()->create();
|
|
|
|
$this->actingAs($admin, 'admin');
|
|
|
|
// Create conversation with admin as sender
|
|
$message = $admin->sendMessageTo($recipient, 'Test message');
|
|
$conversation = $message->conversation;
|
|
|
|
$response = Livewire::test(
|
|
\App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
|
|
['conversationId' => $conversation->id]
|
|
);
|
|
|
|
$response->assertStatus(200);
|
|
}
|
|
|
|
/**
|
|
* Test bank can access conversation they belong to
|
|
*
|
|
* @test
|
|
*/
|
|
public function bank_can_access_conversation_they_belong_to()
|
|
{
|
|
$user = User::factory()->create();
|
|
$bank = Bank::factory()->create();
|
|
$bank->managers()->attach($user->id);
|
|
$recipient = User::factory()->create();
|
|
|
|
$this->actingAs($bank, 'bank');
|
|
|
|
// Create conversation with bank as sender
|
|
$message = $bank->sendMessageTo($recipient, 'Test message');
|
|
$conversation = $message->conversation;
|
|
|
|
$response = Livewire::test(
|
|
\App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
|
|
['conversationId' => $conversation->id]
|
|
);
|
|
|
|
$response->assertStatus(200);
|
|
}
|
|
|
|
/**
|
|
* Test organization cannot access conversation they don't belong to
|
|
*
|
|
* @test
|
|
*/
|
|
public function organization_cannot_access_conversation_they_dont_belong_to()
|
|
{
|
|
$user = User::factory()->create();
|
|
$org1 = Organization::factory()->create();
|
|
$org2 = Organization::factory()->create();
|
|
$recipient = User::factory()->create();
|
|
|
|
$org1->users()->attach($user->id);
|
|
$org2->users()->attach($recipient->id);
|
|
|
|
$this->actingAs($org1, 'organization');
|
|
|
|
// Set active profile in session (required by getActiveProfile())
|
|
session([
|
|
'activeProfileType' => get_class($org1),
|
|
'activeProfileId' => $org1->id,
|
|
'active_guard' => 'organization',
|
|
]);
|
|
|
|
// Create conversation with org2 (not org1)
|
|
$message = $org2->sendMessageTo($recipient, 'Test message');
|
|
$conversation = $message->conversation;
|
|
|
|
$response = Livewire::test(
|
|
\App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
|
|
['conversationId' => $conversation->id]
|
|
);
|
|
|
|
$response->assertStatus(403);
|
|
}
|
|
|
|
/**
|
|
* Test unauthenticated user cannot access conversations
|
|
*
|
|
* @test
|
|
*/
|
|
public function unauthenticated_user_cannot_access_conversations()
|
|
{
|
|
$user1 = User::factory()->create();
|
|
$user2 = User::factory()->create();
|
|
|
|
// Create conversation between two users
|
|
$message = $user1->sendMessageTo($user2, 'Test message');
|
|
$conversation = $message->conversation;
|
|
|
|
// No authentication - accessing as guest
|
|
$response = Livewire::test(
|
|
\App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
|
|
['conversationId' => $conversation->id]
|
|
);
|
|
|
|
$response->assertStatus(403);
|
|
}
|
|
|
|
/**
|
|
* Test multi-participant conversation access (User and Organization)
|
|
*
|
|
* @test
|
|
*/
|
|
public function multi_participant_conversation_allows_both_participants()
|
|
{
|
|
$user = User::factory()->create();
|
|
$organization = Organization::factory()->create();
|
|
$organization->users()->attach($user->id);
|
|
|
|
// Create conversation between user and organization
|
|
$message = $user->sendMessageTo($organization, 'Test message');
|
|
$conversation = $message->conversation;
|
|
|
|
// Test 1: User can access
|
|
$this->actingAs($user, 'web');
|
|
$response1 = Livewire::test(
|
|
\App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
|
|
['conversationId' => $conversation->id]
|
|
);
|
|
$response1->assertStatus(200);
|
|
|
|
// Test 2: Organization can access
|
|
$this->actingAs($organization, 'organization');
|
|
$response2 = Livewire::test(
|
|
\App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
|
|
['conversationId' => $conversation->id]
|
|
);
|
|
$response2->assertStatus(200);
|
|
}
|
|
|
|
/**
|
|
* Test disappearing messages can be enabled by organization
|
|
*
|
|
* @test
|
|
*/
|
|
public function organization_can_enable_disappearing_messages()
|
|
{
|
|
$user = User::factory()->create();
|
|
$organization = Organization::factory()->create();
|
|
$organization->users()->attach($user->id);
|
|
$recipient = User::factory()->create();
|
|
|
|
$this->actingAs($organization, 'organization');
|
|
|
|
// Create conversation with organization as sender
|
|
$message = $organization->sendMessageTo($recipient, 'Test message');
|
|
$conversation = $message->conversation;
|
|
|
|
$response = Livewire::test(
|
|
\App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
|
|
['conversationId' => $conversation->id]
|
|
);
|
|
|
|
// Check that component loaded successfully
|
|
$response->assertStatus(200);
|
|
$response->assertSet('conversationId', $conversation->id);
|
|
$response->assertSet('platformEnabled', true);
|
|
}
|
|
|
|
/**
|
|
* Test admin can access disappearing message settings
|
|
*
|
|
* @test
|
|
*/
|
|
public function admin_can_access_disappearing_message_settings()
|
|
{
|
|
$user = User::factory()->create();
|
|
$admin = Admin::factory()->create();
|
|
$admin->users()->attach($user->id);
|
|
$recipient = User::factory()->create();
|
|
|
|
$this->actingAs($admin, 'admin');
|
|
|
|
// Create conversation with admin as sender
|
|
$message = $admin->sendMessageTo($recipient, 'Test message');
|
|
$conversation = $message->conversation;
|
|
|
|
$response = Livewire::test(
|
|
\App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
|
|
['conversationId' => $conversation->id]
|
|
);
|
|
|
|
$response->assertStatus(200);
|
|
$response->assertViewHas('conversation');
|
|
}
|
|
|
|
/**
|
|
* Test bank can access disappearing message settings
|
|
*
|
|
* @test
|
|
*/
|
|
public function bank_can_access_disappearing_message_settings()
|
|
{
|
|
$user = User::factory()->create();
|
|
$bank = Bank::factory()->create();
|
|
$bank->managers()->attach($user->id);
|
|
$recipient = User::factory()->create();
|
|
|
|
$this->actingAs($bank, 'bank');
|
|
|
|
// Create conversation with bank as sender
|
|
$message = $bank->sendMessageTo($recipient, 'Test message');
|
|
$conversation = $message->conversation;
|
|
|
|
$response = Livewire::test(
|
|
\App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
|
|
['conversationId' => $conversation->id]
|
|
);
|
|
|
|
$response->assertStatus(200);
|
|
$response->assertSet('conversation', function ($conversation) {
|
|
return $conversation instanceof Conversation;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test conversation access via route middleware (belongsToConversation)
|
|
*
|
|
* @test
|
|
*/
|
|
public function route_middleware_blocks_unauthorized_conversation_access()
|
|
{
|
|
$user = User::factory()->create();
|
|
$otherUser = User::factory()->create();
|
|
$anotherUser = User::factory()->create();
|
|
|
|
$this->actingAs($user, 'web');
|
|
|
|
// Set active profile in session (required by getActiveProfile())
|
|
session([
|
|
'activeProfileType' => get_class($user),
|
|
'activeProfileId' => $user->id,
|
|
'active_guard' => 'web',
|
|
]);
|
|
|
|
// Create conversation between otherUser and anotherUser (not involving $user)
|
|
$message = $otherUser->sendMessageTo($anotherUser, 'Test message');
|
|
$conversation = $message->conversation;
|
|
|
|
// Try to access via route (should be blocked)
|
|
$response = $this->get(route('chat', ['conversation' => $conversation->id]));
|
|
|
|
// Middleware may return 403 or redirect (302) when unauthorized
|
|
// Both are acceptable - what matters is user cannot access the conversation
|
|
$this->assertTrue(
|
|
in_array($response->status(), [302, 403]),
|
|
"Expected 302 redirect or 403 forbidden, but got {$response->status()}"
|
|
);
|
|
|
|
// If redirected, should not be to the chat page
|
|
if ($response->status() === 302) {
|
|
$this->assertNotEquals(
|
|
route('chat', ['conversation' => $conversation->id]),
|
|
$response->headers->get('Location'),
|
|
'User should not be redirected to the unauthorized conversation'
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test route middleware allows authorized conversation access
|
|
*
|
|
* @test
|
|
*/
|
|
public function route_middleware_allows_authorized_conversation_access()
|
|
{
|
|
$user = User::factory()->create();
|
|
$recipient = User::factory()->create();
|
|
|
|
$this->actingAs($user, 'web');
|
|
|
|
// Set active profile in session (required by getActiveProfile())
|
|
session([
|
|
'activeProfileType' => get_class($user),
|
|
'activeProfileId' => $user->id,
|
|
'active_guard' => 'web',
|
|
]);
|
|
|
|
// Create conversation with current user as sender
|
|
$message = $user->sendMessageTo($recipient, 'Test message');
|
|
$conversation = $message->conversation;
|
|
|
|
// Access via route (should be allowed by middleware)
|
|
$response = $this->get(route('chat', ['conversation' => $conversation->id]));
|
|
|
|
// Should either return 200 (success) or 302 redirect to valid location
|
|
// The Livewire component tests already verify authorization at component level
|
|
// Route level we just need to ensure it's not blocked entirely
|
|
$this->assertTrue(
|
|
in_array($response->status(), [200, 302]),
|
|
"Expected 200 success or 302 redirect, but got {$response->status()}"
|
|
);
|
|
|
|
// If successful (200), verify we're not getting an error page
|
|
if ($response->status() === 200) {
|
|
$response->assertDontSee('403');
|
|
$response->assertDontSee('Forbidden');
|
|
}
|
|
}
|
|
}
|