create(); $this->actingAs($user, 'web'); $result = ProfileAuthorizationHelper::can($user); $this->assertTrue($result); } /** * Test user cannot access another user's profile * * @test */ public function user_cannot_access_another_users_profile() { $user1 = User::factory()->create(); $user2 = User::factory()->create(); $this->actingAs($user1, 'web'); $result = ProfileAuthorizationHelper::can($user2); $this->assertFalse($result); } /** * Test admin can access their own admin profile * * @test */ public function admin_can_access_own_admin_profile() { // Create admin and link to user $user = User::factory()->create(); $admin = Admin::factory()->create(); $admin->users()->attach($user->id); $this->actingAs($admin, 'admin'); $result = ProfileAuthorizationHelper::can($admin); $this->assertTrue($result, 'Admin should be able to access their own admin profile'); } /** * Test organization can access their own organization profile * * @test */ public function organization_can_access_own_organization_profile() { // Create organization and link to user $user = User::factory()->create(); $organization = Organization::factory()->create(); $organization->users()->attach($user->id); $this->actingAs($organization, 'organization'); $result = ProfileAuthorizationHelper::can($organization); $this->assertTrue($result, 'Organization should be able to access their own profile'); } /** * Test bank can access their own bank profile * * @test */ public function bank_can_access_own_bank_profile() { // Create bank and link to user $user = User::factory()->create(); $bank = Bank::factory()->create(); $bank->managers()->attach($user->id); $this->actingAs($bank, 'bank'); $result = ProfileAuthorizationHelper::can($bank); $this->assertTrue($result, 'Bank should be able to access their own bank profile'); } /** * Test user can access organization they are member of (for profile switching) * * @test */ public function user_can_access_organization_they_are_member_of() { $user = User::factory()->create(); $organization = Organization::factory()->create(); $user->organizations()->attach($organization->id); $this->actingAs($user, 'web'); // Use userOwnsProfile() for cross-guard ownership checks (profile switching scenario) $result = ProfileAuthorizationHelper::userOwnsProfile($organization); $this->assertTrue($result); } /** * Test user cannot access organization they are not member of * * @test */ public function user_cannot_access_organization_they_are_not_member_of() { $user = User::factory()->create(); $organization = Organization::factory()->create(); // User is NOT attached to organization $this->actingAs($user, 'web'); $result = ProfileAuthorizationHelper::can($organization); $this->assertFalse($result); } /** * Test user can access bank they manage (for profile switching) * * @test */ public function user_can_access_bank_they_manage() { $user = User::factory()->create(); $bank = Bank::factory()->create(); $user->banksManaged()->attach($bank->id); $this->actingAs($user, 'web'); // Use userOwnsProfile() for cross-guard ownership checks (profile switching scenario) $result = ProfileAuthorizationHelper::userOwnsProfile($bank); $this->assertTrue($result); } /** * Test user cannot access bank they don't manage * * @test */ public function user_cannot_access_bank_they_dont_manage() { $user = User::factory()->create(); $bank = Bank::factory()->create(); // User is NOT attached to bank $this->actingAs($user, 'web'); $result = ProfileAuthorizationHelper::can($bank); $this->assertFalse($result); } /** * Test user can access admin profile they are linked to (for profile switching) * * @test */ public function user_can_access_admin_profile_they_are_linked_to() { $user = User::factory()->create(); $admin = Admin::factory()->create(); $user->admins()->attach($admin->id); $this->actingAs($user, 'web'); // Use userOwnsProfile() for cross-guard ownership checks (profile switching scenario) $result = ProfileAuthorizationHelper::userOwnsProfile($admin); $this->assertTrue($result); } /** * Test user cannot access admin profile they are not linked to * * @test */ public function user_cannot_access_admin_profile_they_are_not_linked_to() { $user = User::factory()->create(); $admin = Admin::factory()->create(); // User is NOT attached to admin $this->actingAs($user, 'web'); $result = ProfileAuthorizationHelper::can($admin); $this->assertFalse($result); } /** * Test admin cannot directly switch to organization (must go through web user) * * In the application, profile switching flow is: User → Admin → back to User → Organization * Direct Admin → Organization switching is not supported. * * @test */ public function admin_can_access_organization_via_linked_user() { // Create user linked to both admin and organization $user = User::factory()->create(); $admin = Admin::factory()->create(); $organization = Organization::factory()->create(); $admin->users()->attach($user->id); $user->organizations()->attach($organization->id); $this->actingAs($admin, 'admin'); // userOwnsProfile() only checks web guard, so this should return false // Profile switching requires being on web guard first $result = ProfileAuthorizationHelper::userOwnsProfile($organization); $this->assertFalse($result, 'Admin cannot directly switch to organization without going through web user first'); } /** * Test organization cannot directly switch to bank (must go through web user) * * In the application, profile switching flow is: User → Organization → back to User → Bank * Direct Organization → Bank switching is not supported. * * @test */ public function organization_can_access_bank_via_linked_user() { // Create user linked to both organization and bank $user = User::factory()->create(); $organization = Organization::factory()->create(); $bank = Bank::factory()->create(); $organization->users()->attach($user->id); $user->banksManaged()->attach($bank->id); $this->actingAs($organization, 'organization'); // userOwnsProfile() only checks web guard, so this should return false // Profile switching requires being on web guard first $result = ProfileAuthorizationHelper::userOwnsProfile($bank); $this->assertFalse($result, 'Organization cannot directly switch to bank without going through web user first'); } /** * Test admin cannot access unrelated organization * * @test */ public function admin_cannot_access_unrelated_organization() { $user = User::factory()->create(); $admin = Admin::factory()->create(); $organization = Organization::factory()->create(); $admin->users()->attach($user->id); // User is NOT linked to organization $this->actingAs($admin, 'admin'); $result = ProfileAuthorizationHelper::can($organization); $this->assertFalse($result, 'Admin should NOT access unrelated organization'); } /** * Test authorize method throws 403 for unauthorized access * * @test */ public function authorize_method_throws_403_for_unauthorized_access() { $this->expectException(\Symfony\Component\HttpKernel\Exception\HttpException::class); $this->expectExceptionMessage('Unauthorized'); $user1 = User::factory()->create(); $user2 = User::factory()->create(); $this->actingAs($user1, 'web'); ProfileAuthorizationHelper::authorize($user2); } /** * Test unauthenticated access is denied * * @test */ public function unauthenticated_access_is_denied() { $user = User::factory()->create(); $result = ProfileAuthorizationHelper::can($user); $this->assertFalse($result); } /** * Test authorize method throws 401 for unauthenticated access * * @test */ public function authorize_method_throws_401_for_unauthenticated() { $this->expectException(\Symfony\Component\HttpKernel\Exception\HttpException::class); $this->expectExceptionMessage('Authentication required'); $user = User::factory()->create(); ProfileAuthorizationHelper::authorize($user); } /** * Test admin cannot access another admin profile * * @test */ public function admin_cannot_access_another_admin_profile() { $user1 = User::factory()->create(); $admin1 = Admin::factory()->create(); $admin2 = Admin::factory()->create(); $admin1->users()->attach($user1->id); $this->actingAs($admin1, 'admin'); $result = ProfileAuthorizationHelper::can($admin2); $this->assertFalse($result, 'Admin should NOT access another admin profile'); } }