Files
InvoiceShelf/tests/Feature/Admin/UserTest.php
Darko Gjorgjijoski 734ac9421d fix(users): scope user view & update to the acting company
User view/update authorized only that the requester owns their active company, not that the target user belonged to it, allowing an owner of one company to read or modify users of another. Require shared company membership in UserPolicy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 23:43:18 +02:00

131 lines
3.8 KiB
PHP

<?php
use App\Http\Controllers\V1\Admin\Users\UsersController;
use App\Http\Requests\UserRequest;
use App\Models\Company;
use App\Models\User;
use Laravel\Sanctum\Sanctum;
use function Pest\Faker\fake;
use function Pest\Laravel\getJson;
use function Pest\Laravel\postJson;
use function Pest\Laravel\putJson;
beforeEach(function () {
Artisan::call('db:seed', ['--class' => 'DatabaseSeeder', '--force' => true]);
Artisan::call('db:seed', ['--class' => 'DemoSeeder', '--force' => true]);
$user = User::where('role', 'super admin')->first();
$this->withHeaders([
'company' => $user->companies()->first()->id,
]);
Sanctum::actingAs(
$user,
['*']
);
});
getJson('/api/v1/users')->assertOk();
test('store user using a form request', function () {
$this->assertActionUsesFormRequest(
UsersController::class,
'store',
UserRequest::class
);
});
// test('store user', function () {
// $data = [
// 'name' => fake()->name,
// 'email' => fake()->unique()->safeEmail,
// 'phone' => fake()->phoneNumber,
// 'password' => fake()->password
// ];
// postJson('/api/v1/users', $data)->assertOk();
// $this->assertDatabaseHas('users', [
// 'name' => $data['name'],
// 'email' => $data['email'],
// 'phone' => $data['phone'],
// ]);
// });
test('get user belonging to the current company', function () {
$companyId = User::where('role', 'super admin')->first()->companies()->first()->id;
$user = User::factory()->create();
$user->companies()->attach($companyId);
getJson("/api/v1/users/{$user->id}")->assertOk();
});
test('cannot view a user belonging to another company', function () {
$user = User::factory()->create();
$user->companies()->attach(Company::factory()->create()->id);
getJson("/api/v1/users/{$user->id}")->assertForbidden();
});
test('cannot update a user belonging to another company', function () {
$companyId = User::where('role', 'super admin')->first()->companies()->first()->id;
$user = User::factory()->create();
$user->companies()->attach(Company::factory()->create()->id);
putJson("/api/v1/users/{$user->id}", [
'name' => 'Hacked',
'email' => 'pwned@attacker.test',
'companies' => [['id' => $companyId, 'role' => 'super admin']],
])->assertForbidden();
$this->assertDatabaseMissing('users', ['email' => 'pwned@attacker.test']);
});
test('update user using a form request', function () {
$this->assertActionUsesFormRequest(
UsersController::class,
'update',
UserRequest::class
);
});
// test('update user', function () {
// $user = User::factory()->create();
// $data = [
// 'name' => fake()->name,
// 'email' => fake()->unique()->safeEmail,
// 'phone' => fake()->phoneNumber,
// 'password' => fake()->password
// ];
// putJson("/api/v1/users/{$user->id}", $data)->assertOk();
// $this->assertDatabaseHas('users', [
// 'name' => $data['name'],
// 'email' => $data['email'],
// 'phone' => $data['phone'],
// ]);
// });
test('deletes a user belonging to the current company', function () {
$companyId = User::where('role', 'super admin')->first()->companies()->first()->id;
$user = User::factory()->create();
$user->companies()->attach($companyId);
postJson('/api/v1/users/delete', ['users' => [$user->id]])->assertOk();
$this->assertDatabaseMissing('users', ['id' => $user->id]);
});
test('cannot bulk delete a user belonging to another company', function () {
$user = User::factory()->create();
$user->companies()->attach(Company::factory()->create()->id);
postJson('/api/v1/users/delete', ['users' => [$user->id]])->assertOk();
$this->assertDatabaseHas('users', ['id' => $user->id]);
});