superuser()->create(); $manager = User::factory()->create(); $company = Company::factory()->create(); $department = Department::factory()->create(); $location = Location::factory()->create(); [$groupA, $groupB] = Group::factory()->count(2)->create(); $user = User::factory()->create([ 'activated' => false, 'remote' => false, 'vip' => false, ]); $this->actingAsForApi($admin) ->patchJson(route('api.users.update', $user), [ 'first_name' => 'Mabel', 'last_name' => 'Mora', 'username' => 'mabel', 'password' => 'super-secret', 'email' => 'mabel@onlymurderspod.com', 'permissions' => '{"a.new.permission":"1"}', 'activated' => true, 'phone' => '619-555-5555', 'jobtitle' => 'Host', 'manager_id' => $manager->id, 'employee_num' => '1111', 'notes' => 'Pretty good artist', 'company_id' => $company->id, 'department_id' => $department->id, 'location_id' => $location->id, 'remote' => true, 'groups' => $groupA->id, 'vip' => true, 'start_date' => '2021-08-01', 'end_date' => '2025-12-31', ]) ->assertOk() ->assertStatus(200) ->assertStatusMessageIs('success') ->json(); $user->refresh(); $this->assertEquals('Mabel', $user->first_name, 'First name was not updated'); $this->assertEquals('Mora', $user->last_name, 'Last name was not updated'); $this->assertEquals('mabel', $user->username, 'Username was not updated'); $this->assertTrue(Hash::check('super-secret', $user->password), 'Password was not updated'); $this->assertEquals('mabel@onlymurderspod.com', $user->email, 'Email was not updated'); $this->assertArrayHasKey('a.new.permission', $user->decodePermissions(), 'Permissions were not updated'); $this->assertTrue((bool) $user->activated, 'User not marked as activated'); $this->assertEquals('619-555-5555', $user->phone, 'Phone was not updated'); $this->assertEquals('Host', $user->jobtitle, 'Job title was not updated'); $this->assertTrue($user->manager->is($manager), 'Manager was not updated'); $this->assertEquals('1111', $user->employee_num, 'Employee number was not updated'); $this->assertEquals('Pretty good artist', $user->notes, 'Notes was not updated'); $this->assertTrue($user->company->is($company), 'Company was not updated'); $this->assertTrue($user->department->is($department), 'Department was not updated'); $this->assertTrue($user->location->is($location), 'Location was not updated'); $this->assertEquals(1, $user->remote, 'Remote was not updated'); $this->assertTrue($user->groups->contains($groupA), 'Groups were not updated'); $this->assertEquals(1, $user->vip, 'VIP was not updated'); $this->assertEquals('2021-08-01', $user->start_date, 'Start date was not updated'); $this->assertEquals('2025-12-31', $user->end_date, 'End date was not updated'); // `groups` can be an id or array or ids $this->patch(route('api.users.update', $user), ['groups' => [$groupA->id, $groupB->id]]); $user->refresh(); $this->assertTrue($user->groups->contains($groupA), 'Not part of expected group'); $this->assertTrue($user->groups->contains($groupB), 'Not part of expected group'); } public function testCanUpdateUserViaPut() { $admin = User::factory()->superuser()->create(); $manager = User::factory()->create(); $company = Company::factory()->create(); $department = Department::factory()->create(); $location = Location::factory()->create(); [$groupA, $groupB] = Group::factory()->count(2)->create(); $user = User::factory()->create([ 'activated' => false, 'remote' => false, 'vip' => false, ]); $response = $this->actingAsForApi($admin) ->putJson(route('api.users.update', $user), [ 'first_name' => 'Mabel', 'last_name' => 'Mora', 'username' => 'mabel', 'password' => 'super-secret', 'password_confirmation' => 'super-secret', 'email' => 'mabel@onlymurderspod.com', 'permissions' => '{"a.new.permission":"1"}', 'activated' => true, 'phone' => '619-555-5555', 'jobtitle' => 'Host', 'manager_id' => $manager->id, 'employee_num' => '1111', 'notes' => 'Pretty good artist', 'company_id' => $company->id, 'department_id' => $department->id, 'location_id' => $location->id, 'remote' => true, 'groups' => $groupA->id, 'vip' => true, 'start_date' => '2021-08-01', 'end_date' => '2025-12-31', ]) ->assertOk() ->assertStatus(200) ->assertStatusMessageIs('success') ->json(); $user->refresh(); $this->assertEquals('Mabel', $user->first_name, 'First name was not updated'); $this->assertEquals('Mora', $user->last_name, 'Last name was not updated'); $this->assertEquals('mabel', $user->username, 'Username was not updated'); $this->assertTrue(Hash::check('super-secret', $user->password), 'Password was not updated'); $this->assertEquals('mabel@onlymurderspod.com', $user->email, 'Email was not updated'); $this->assertArrayHasKey('a.new.permission', $user->decodePermissions(), 'Permissions were not updated'); $this->assertTrue((bool) $user->activated, 'User not marked as activated'); $this->assertEquals('619-555-5555', $user->phone, 'Phone was not updated'); $this->assertEquals('Host', $user->jobtitle, 'Job title was not updated'); $this->assertTrue($user->manager->is($manager), 'Manager was not updated'); $this->assertEquals('1111', $user->employee_num, 'Employee number was not updated'); $this->assertEquals('Pretty good artist', $user->notes, 'Notes was not updated'); $this->assertTrue($user->company->is($company), 'Company was not updated'); $this->assertTrue($user->department->is($department), 'Department was not updated'); $this->assertTrue($user->location->is($location), 'Location was not updated'); $this->assertEquals(1, $user->remote, 'Remote was not updated'); $this->assertTrue($user->groups->contains($groupA), 'Groups were not updated'); $this->assertEquals(1, $user->vip, 'VIP was not updated'); $this->assertEquals('2021-08-01', $user->start_date, 'Start date was not updated'); $this->assertEquals('2025-12-31', $user->end_date, 'End date was not updated'); // `groups` can be an id or array or ids $this->patch(route('api.users.update', $user), ['groups' => [$groupA->id, $groupB->id]]); $user->refresh(); $this->assertTrue($user->groups->contains($groupA), 'Not part of expected group'); $this->assertTrue($user->groups->contains($groupB), 'Not part of expected group'); } public function testApiUsersCanBeActivatedWithNumber() { $admin = User::factory()->superuser()->create(); $user = User::factory()->create(['activated' => 0]); $this->actingAsForApi($admin) ->patch(route('api.users.update', $user), [ 'activated' => 1, ]); $this->assertEquals(1, $user->refresh()->activated); } public function testApiUsersCanBeActivatedWithBooleanTrue() { $admin = User::factory()->superuser()->create(); $user = User::factory()->create(['activated' => false]); $this->actingAsForApi($admin) ->patch(route('api.users.update', $user), [ 'activated' => true, ]); $this->assertEquals(1, $user->refresh()->activated); } public function testApiUsersCanBeDeactivatedWithNumber() { $admin = User::factory()->superuser()->create(); $user = User::factory()->create(['activated' => true]); $this->actingAsForApi($admin) ->patch(route('api.users.update', $user), [ 'activated' => 0, ]); $this->assertEquals(0, $user->refresh()->activated); } public function testApiUsersCanBeDeactivatedWithBooleanFalse() { $admin = User::factory()->superuser()->create(); $user = User::factory()->create(['activated' => true]); $this->actingAsForApi($admin) ->patch(route('api.users.update', $user), [ 'activated' => false, ]); $this->assertEquals(0, $user->refresh()->activated); } public function testUsersScopedToCompanyDuringUpdateWhenMultipleFullCompanySupportEnabled() { $this->settings->enableMultipleFullCompanySupport(); $companyA = Company::factory()->create(['name'=>'Company A']); $companyB = Company::factory()->create(['name'=>'Company B']); $adminA = User::factory(['company_id' => $companyA->id])->admin()->create(); $adminB = User::factory(['company_id' => $companyB->id])->admin()->create(); $adminNoCompany = User::factory(['company_id' => null])->admin()->create(); // Create users that belongs to company A and B and one that is unscoped $scoped_user_in_companyA = User::factory()->create(['company_id' => $companyA->id]); $scoped_user_in_companyB = User::factory()->create(['company_id' => $companyB->id]); $scoped_user_in_no_company = User::factory()->create(['company_id' => null]); // Admin for Company A should allow updating user from Company A $this->actingAsForApi($adminA) ->patchJson(route('api.users.update', $scoped_user_in_companyA)) ->assertOk() ->assertStatus(200) ->assertStatusMessageIs('success') ->json(); // Admin for Company A should get denied updating user from Company B $this->actingAsForApi($adminA) ->patchJson(route('api.users.update', $scoped_user_in_companyB)) ->assertOk() ->assertStatus(200) ->assertStatusMessageIs('error') ->json(); // Admin for Company A should get denied updating user without a company $this->actingAsForApi($adminA) ->patchJson(route('api.users.update', $scoped_user_in_no_company)) ->assertOk() ->assertStatus(200) ->assertStatusMessageIs('error') ->json(); // Admin for Company B should allow updating user from Company B $this->actingAsForApi($adminB) ->patchJson(route('api.users.update', $scoped_user_in_companyB)) ->assertOk() ->assertStatus(200) ->assertStatusMessageIs('success') ->json(); // Admin for Company B should get denied updating user from Company A $this->actingAsForApi($adminB) ->patchJson(route('api.users.update', $scoped_user_in_companyA)) ->assertOk() ->assertStatus(200) ->assertStatusMessageIs('error') ->json(); // Admin for Company B should get denied updating user without a company $this->actingAsForApi($adminB) ->patchJson(route('api.users.update', $scoped_user_in_no_company)) ->assertOk() ->assertStatus(200) ->assertStatusMessageIs('error') ->json(); // Admin without a company should allow updating user without a company $this->actingAsForApi($adminNoCompany) ->patchJson(route('api.users.update', $scoped_user_in_no_company)) ->assertOk() ->assertStatus(200) ->assertStatusMessageIs('success') ->json(); // Admin without a company should get denied updating user from Company A $this->actingAsForApi($adminNoCompany) ->patchJson(route('api.users.update', $scoped_user_in_companyA)) ->assertOk() ->assertStatus(200) ->assertStatusMessageIs('error') ->json(); // Admin without a company should get denied updating user from Company B $this->actingAsForApi($adminNoCompany) ->patchJson(route('api.users.update', $scoped_user_in_companyB)) ->assertOk() ->assertStatus(200) ->assertStatusMessageIs('error') ->json(); } public function testUserGroupsAreOnlyUpdatedIfAuthenticatedUserIsSuperUser() { $groupToJoin = Group::factory()->create(); $userWhoCanEditUsers = User::factory()->editUsers()->create(); $superUser = User::factory()->superuser()->create(); $userToUpdateByUserWhoCanEditUsers = User::factory()->create(); $userToUpdateByToUserBySuperuser = User::factory()->create(); $this->actingAsForApi($userWhoCanEditUsers) ->patchJson(route('api.users.update', $userToUpdateByUserWhoCanEditUsers), [ 'groups' => [$groupToJoin->id], ]); $this->actingAsForApi($superUser) ->patchJson(route('api.users.update', $userToUpdateByToUserBySuperuser), [ 'groups' => [$groupToJoin->id], ]); $this->assertFalse($userToUpdateByUserWhoCanEditUsers->refresh()->groups->contains($groupToJoin), 'Non-super-user was able to modify user group' ); $this->assertTrue($userToUpdateByToUserBySuperuser->refresh()->groups->contains($groupToJoin)); } public function testUserGroupsCanBeClearedBySuperUser() { $normalUser = User::factory()->editUsers()->create(); $superUser = User::factory()->superuser()->create(); $oneUserToUpdate = User::factory()->create(); $anotherUserToUpdate = User::factory()->create(); $joinedGroup = Group::factory()->create(); $oneUserToUpdate->groups()->sync([$joinedGroup->id]); $anotherUserToUpdate->groups()->sync([$joinedGroup->id]); $this->actingAsForApi($normalUser) ->patchJson(route('api.users.update', $oneUserToUpdate), [ 'groups' => null, ]); $this->actingAsForApi($superUser) ->patchJson(route('api.users.update', $anotherUserToUpdate), [ 'groups' => null, ]); $this->assertTrue($oneUserToUpdate->refresh()->groups->contains($joinedGroup)); $this->assertFalse($anotherUserToUpdate->refresh()->groups->contains($joinedGroup)); } public function testNonSuperuserCannotUpdateOwnGroups() { $groupToJoin = Group::factory()->create(); $user = User::factory()->editUsers()->create(); $this->actingAsForApi($user) ->patchJson(route('api.users.update', $user), [ 'groups' => [$groupToJoin->id], ]); $this->assertFalse($user->refresh()->groups->contains($groupToJoin), 'Non-super-user was able to modify user group' ); } public function testNonSuperuserCannotUpdateGroups() { $user = User::factory()->editUsers()->create(); $group = Group::factory()->create(); $user->groups()->sync([$group->id]); $newGroupToJoin = Group::factory()->create(); $this->actingAsForApi($user) ->patchJson(route('api.users.update', $user), [ 'groups' => [$newGroupToJoin->id], ]); $this->assertFalse($user->refresh()->groups->contains($newGroupToJoin), 'Non-super-user was able to modify user group membership' ); $this->assertTrue($user->refresh()->groups->contains($group)); } public function testUsersGroupsAreNotClearedIfNoGroupPassedBySuperUser() { $user = User::factory()->create(); $superUser = User::factory()->superuser()->create(); $group = Group::factory()->create(); $user->groups()->sync([$group->id]); $this->actingAsForApi($superUser) ->patchJson(route('api.users.update', $user), []); $this->assertTrue($user->refresh()->groups->contains($group)); } public function testMultipleGroupsUpdateBySuperUser() { $user = User::factory()->create(); $superUser = User::factory()->superuser()->create(); $groupA = Group::factory()->create(['name'=>'Group A']); $groupB = Group::factory()->create(['name'=>'Group B']); $this->actingAsForApi($superUser) ->patchJson(route('api.users.update', $user), [ 'groups' => [$groupA->id, $groupB->id], ])->json(); $this->assertTrue($user->refresh()->groups->contains($groupA)); $this->assertTrue($user->refresh()->groups->contains($groupB)); } public function testMultiCompanyUserCannotBeMovedIfHasAsset() { $this->settings->enableMultipleFullCompanySupport(); $companyA = Company::factory()->create(); $companyB = Company::factory()->create(); $user = User::factory()->create([ 'company_id' => $companyA->id, ]); $superUser = User::factory()->superuser()->create(); $asset = Asset::factory()->create(); // no assets assigned, therefore success $this->actingAsForApi($superUser)->patchJson(route('api.users.update', $user), [ 'username' => 'test', 'company_id' => $companyB->id, ])->assertStatusMessageIs('success'); // same test but PUT $this->actingAsForApi($superUser)->putJson(route('api.users.update', $user), [ 'username' => 'test', 'first_name' => 'Test', 'company_id' => $companyB->id, ])->assertStatusMessageIs('success'); $asset->checkOut($user, $superUser); // asset assigned, therefore error $this->actingAsForApi($superUser)->patchJson(route('api.users.update', $user), [ 'username' => 'test', 'company_id' => $companyB->id, ])->assertStatusMessageIs('error'); // same test but PUT $this->actingAsForApi($superUser)->putJson(route('api.users.update', $user), [ 'username' => 'test', 'first_name' => 'Test', 'company_id' => $companyB->id, ])->assertStatusMessageIs('error'); } }