Files
timebank-cc-public/tests/Feature/Security/IDOR/ProfileAccessIDORTest.php
Ronald Huynen 2547717edb Initial commit
2026-03-23 21:37:59 +01:00

293 lines
9.1 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 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,
]);
// Create a transaction for user2
Transaction::factory()->create([
'from_account_id' => $account2->id,
'to_account_id' => $account1->id,
'amount' => 100,
]);
// Act: Login as user1
$this->actingAs($user1);
// Try to access account page
$response = $this->get(route('accounts'));
// Assert: user1 should only see their own account balance
// Should NOT see user2's account details
$response->assertSuccessful();
// The response should not contain user2's account ID or specific balance
// (This would require more specific assertions based on actual response structure)
}
/**
* Test user cannot view another user's transaction history via ID manipulation
*
* @test
*/
public function user_cannot_view_another_users_transaction_history()
{
// Arrange: Create two users with transactions
$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,
]);
// Create transaction between two different users (not involving user1)
$user3 = User::factory()->create();
$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',
]);
// Act: Login as user1 and try to view transaction history
$response = $this->actingAs($user1)
->get(route('transactions'));
// Assert: Should only see transactions involving user1
$response->assertSuccessful();
// Try to access specific transaction by ID manipulation
$response2 = $this->actingAs($user1)
->get(route('statement', ['transactionId' => $privateTransaction->id]));
// Assert: Should not be able to access transaction details
// (Implementation should check ownership)
// For now, verify the route is accessible but doesn't leak data
}
/**
* Test user cannot edit another user's post via ID manipulation
*
* @test
*/
public function user_cannot_edit_another_users_post()
{
// Arrange: Create two users with posts
$user1 = User::factory()->create();
$user2 = User::factory()->create();
$user1Post = Post::factory()->create([
'profile_id' => $user1->id,
'profile_type' => User::class,
]);
$user2Post = Post::factory()->create([
'profile_id' => $user2->id,
'profile_type' => User::class,
]);
// Act: Login as user1 and try to edit user2's post
$this->actingAs($user1);
// Try to access edit page for user2's post
$response = $this->get("/posts/{$user2Post->id}/edit");
// Assert: Should be denied (403) or redirected
// Proper authorization should prevent accessing edit page
$this->assertTrue(
$response->status() === 403 || $response->status() === 302,
"User should not be able to edit another user's post"
);
}
/**
* 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);
// Try to switch to org2 (not linked)
session(['activeProfileType' => 'App\\Models\\Organization']);
session(['activeProfileId' => $org2->id]); // IDOR attempt
session(['active_guard' => 'organization']);
// Try to access dashboard
$response = $this->get(route('dashboard'));
// Assert: Should not have access to org2's dashboard
// The getActiveProfile() helper should validate the link
}
/**
* 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->banks()->attach($bank1->id);
// Act: Login as user
$this->actingAs($user);
// Try to switch to bank2 (not linked)
session(['activeProfileType' => 'App\\Models\\Bank']);
session(['activeProfileId' => $bank2->id]); // IDOR attempt
session(['active_guard' => 'bank']);
// Try to access protected bank functionality
$response = $this->get(route('dashboard'));
// Assert: Should not have access to bank2
// Middleware or profile validation should prevent this
}
/**
* 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
}
}