Files

266 lines
8.6 KiB
PHP

<?php
namespace Tests\Feature\Security\IDOR;
use App\Models\User;
use App\Models\Organization;
use App\Models\Bank;
use App\Models\Admin;
use App\Models\Post;
use App\Models\Transaction;
use App\Models\Account;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Tests\TestCase;
/**
* Profile Access IDOR (Insecure Direct Object Reference) Tests
*
* Tests that users cannot access resources belonging to other users
* by manipulating IDs in requests.
*
* @group security
* @group idor
* @group critical
*/
class ProfileAccessIDORTest extends TestCase
{
use RefreshDatabase;
/**
* Test user cannot view another user's private profile data via ID manipulation
*
* @test
*/
public function user_cannot_view_another_users_private_profile_via_id()
{
// Arrange: Create two users
$user1 = User::factory()->create();
$user2 = User::factory()->create();
// Act: Login as user1, try to access user2's profile edit page
$response = $this->actingAs($user1)
->get(route('profile.edit'));
// Try to load user2's data by manipulating session
session(['activeProfileId' => $user2->id]);
$response2 = $this->actingAs($user1)
->get(route('profile.edit'));
// Assert: Should not be able to see user2's private data
// This is a basic check - deeper validation would require inspecting response content
$this->assertTrue(true); // Placeholder for more specific assertions
}
/**
* Test user cannot access another user's account balance via ID manipulation
*
* @test
*/
public function user_cannot_access_another_users_account_balance()
{
// Arrange: Create two users with accounts
$user1 = User::factory()->create();
$user2 = User::factory()->create();
$account1 = Account::factory()->create([
'accountable_type' => User::class,
'accountable_id' => $user1->id,
]);
$account2 = Account::factory()->create([
'accountable_type' => User::class,
'accountable_id' => $user2->id,
]);
$account3 = Account::factory()->create([
'accountable_type' => User::class,
'accountable_id' => User::factory()->create()->id,
]);
// A transaction of user2 that does not involve user1
Transaction::factory()->create([
'from_account_id' => $account2->id,
'to_account_id' => $account3->id,
'amount' => 100,
'description' => 'Private to user2',
]);
// Act: Login as user1 and point the account list at user2's account
$this->actingAs($user1, 'web');
session(['activeProfileType' => User::class, 'activeProfileId' => $user1->id]);
// Assert: the account must belong to the active profile, so nothing of user2 is listed
Livewire::test(\App\Http\Livewire\TransactionsTable::class)
->set('fromAccountId', $account2->id)
->assertDontSee('Private to user2');
}
/**
* Test user cannot view another user's transaction history via ID manipulation
*
* @test
*/
public function user_cannot_view_another_users_transaction_history()
{
// Arrange: a transaction between user2 and user3, not involving user1
$user1 = User::factory()->create();
$user2 = User::factory()->create();
$user3 = User::factory()->create();
Account::factory()->create([
'accountable_type' => User::class,
'accountable_id' => $user1->id,
]);
$account2 = Account::factory()->create([
'accountable_type' => User::class,
'accountable_id' => $user2->id,
]);
$account3 = Account::factory()->create([
'accountable_type' => User::class,
'accountable_id' => $user3->id,
]);
$privateTransaction = Transaction::factory()->create([
'from_account_id' => $account2->id,
'to_account_id' => $account3->id,
'amount' => 500,
'description' => 'Private transaction between user2 and user3',
]);
$this->actingAs($user1, 'web');
session(['activeProfileType' => User::class, 'activeProfileId' => $user1->id]);
// The own history is available
$this->get(route('transactions'))->assertSuccessful();
// Opening the other transaction's statement by id is refused
$response = $this->get(route('transaction.show', ['transactionId' => $privateTransaction->id]));
$response->assertForbidden();
$response->assertDontSee('Private transaction between user2 and user3');
}
/**
* Test user cannot edit another user's post via ID manipulation
*
* @test
*/
public function user_cannot_edit_another_users_post()
{
// Posts are edited in the post manager, which is for admins and the central bank only;
// a regular user cannot open it, so no post of another profile can be edited
$user1 = User::factory()->create();
$this->actingAs($user1, 'web');
session(['activeProfileType' => User::class, 'activeProfileId' => $user1->id]);
$this->get(route('posts.manage'))->assertForbidden();
}
/**
* Test user cannot access organization dashboard they're not linked to
*
* @test
*/
public function user_cannot_access_organization_dashboard_without_link()
{
// Arrange: Create user and two organizations
$user = User::factory()->create();
$org1 = Organization::factory()->create();
$org2 = Organization::factory()->create();
// Link user to org1 only
$user->organizations()->attach($org1->id);
// Act: Login as user
$this->actingAs($user, 'web');
// Point the session at org2 (not linked)
session(['activeProfileType' => 'App\\Models\\Organization']);
session(['activeProfileId' => $org2->id]); // IDOR attempt
session(['active_guard' => 'organization']);
// Assert: the page is not served and the organization guard stays logged out
$this->get(route('main'))->assertRedirect();
$this->assertGuest('organization');
}
/**
* Test user cannot access bank dashboard they're not linked to
*
* @test
*/
public function user_cannot_access_bank_dashboard_without_link()
{
// Arrange: Create user and two banks
$user = User::factory()->create();
$bank1 = Bank::factory()->create();
$bank2 = Bank::factory()->create();
// Link user to bank1 only
$user->banksManaged()->attach($bank1->id);
// Act: Login as user
$this->actingAs($user, 'web');
// Point the session at bank2 (not linked)
session(['activeProfileType' => 'App\\Models\\Bank']);
session(['activeProfileId' => $bank2->id]); // IDOR attempt
session(['active_guard' => 'bank']);
// Assert: the page is not served and the bank guard stays logged out
$this->get(route('main'))->assertRedirect();
$this->assertGuest('bank');
}
/**
* Test user cannot access admin panel by ID manipulation
*
* @test
*/
public function regular_user_cannot_access_admin_panel()
{
// Arrange: Create regular user and admin
$user = User::factory()->create();
$admin = Admin::factory()->create();
// Act: Login as regular user
$this->actingAs($user);
// Try to manipulate session to become admin
session(['activeProfileType' => 'App\\Models\\Admin']);
session(['activeProfileId' => $admin->id]); // IDOR attempt
session(['active_guard' => 'admin']);
// Try to access admin dashboard
$response = $this->get('/admin/dashboard');
// Assert: Should be denied
$this->assertTrue(
$response->status() === 403 || $response->status() === 302 || $response->status() === 404,
"Regular user should not access admin panel"
);
}
/**
* Test API endpoints validate ownership (if applicable)
*
* @test
*/
public function api_endpoints_validate_resource_ownership()
{
// Arrange: Create two users
$user1 = User::factory()->create();
$user2 = User::factory()->create();
// Act: Login as user1
$this->actingAs($user1);
// Try to access user2's data via API (if API exists)
// This is a placeholder test - actual implementation depends on API structure
// Assert: Proper 403 or ownership validation
$this->assertTrue(true); // Placeholder
}
}