136 lines
4.7 KiB
PHP
136 lines
4.7 KiB
PHP
<?php
|
|
|
|
namespace App\Domain\Identity\Services;
|
|
|
|
use App\Domain\Audit\Models\AuditLog;
|
|
use App\Domain\Identity\Enums\RoleName;
|
|
use App\Domain\Identity\Models\Role;
|
|
use App\Models\User;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
final class UserAdministrationService
|
|
{
|
|
/**
|
|
* @param array{name:string,email:string,department:?string,snipe_user_id:?int,password:string,active:bool,roles:list<string>} $data
|
|
*/
|
|
public function create(User $actor, array $data): User
|
|
{
|
|
return DB::transaction(function () use ($actor, $data): User {
|
|
$user = User::create([
|
|
'name' => $data['name'],
|
|
'email' => $data['email'],
|
|
'email_verified_at' => now(),
|
|
'department' => $data['department'],
|
|
'snipe_user_id' => $data['snipe_user_id'],
|
|
'password' => $data['password'],
|
|
'active' => $data['active'],
|
|
]);
|
|
$user->roles()->sync($this->roleIds($data['roles']));
|
|
|
|
AuditLog::create([
|
|
'actor_id' => $actor->id,
|
|
'event' => 'user.created',
|
|
'auditable_type' => $user->getMorphClass(),
|
|
'auditable_id' => $user->id,
|
|
'after' => $this->snapshot($user),
|
|
]);
|
|
|
|
return $user->load('roles');
|
|
}, 3);
|
|
}
|
|
|
|
/**
|
|
* @param array{name:string,email:string,department:?string,snipe_user_id:?int,password:?string,active:bool,roles:list<string>} $data
|
|
*/
|
|
public function update(User $user, User $actor, array $data): User
|
|
{
|
|
return DB::transaction(function () use ($user, $actor, $data): User {
|
|
$locked = User::query()->whereKey($user->id)->lockForUpdate()->firstOrFail();
|
|
$before = $this->snapshot($locked);
|
|
$removesAdministrator = ! in_array(RoleName::Administrator->value, $data['roles'], true);
|
|
|
|
if ($locked->id === $actor->id && (! $data['active'] || $removesAdministrator)) {
|
|
throw ValidationException::withMessages([
|
|
'roles' => 'Não pode desativar a própria conta nem remover o seu perfil de administrador.',
|
|
]);
|
|
}
|
|
|
|
if ((! $data['active'] || $removesAdministrator)
|
|
&& $locked->hasRole(RoleName::Administrator)
|
|
&& $this->activeAdministratorCount() <= 1) {
|
|
throw ValidationException::withMessages([
|
|
'roles' => 'O portal tem de manter pelo menos um administrador ativo.',
|
|
]);
|
|
}
|
|
|
|
$attributes = [
|
|
'name' => $data['name'],
|
|
'email' => $data['email'],
|
|
'department' => $data['department'],
|
|
'snipe_user_id' => $data['snipe_user_id'],
|
|
'active' => $data['active'],
|
|
];
|
|
if (filled($data['password'])) {
|
|
$attributes['password'] = $data['password'];
|
|
}
|
|
|
|
$locked->update($attributes);
|
|
$locked->roles()->sync($this->roleIds($data['roles']));
|
|
$locked->load('roles');
|
|
|
|
AuditLog::create([
|
|
'actor_id' => $actor->id,
|
|
'event' => 'user.updated',
|
|
'auditable_type' => $locked->getMorphClass(),
|
|
'auditable_id' => $locked->id,
|
|
'before' => $before,
|
|
'after' => $this->snapshot($locked),
|
|
'metadata' => ['password_changed' => filled($data['password'])],
|
|
]);
|
|
|
|
return $locked;
|
|
}, 3);
|
|
}
|
|
|
|
/**
|
|
* @param list<string> $roles
|
|
* @return list<int>
|
|
*/
|
|
private function roleIds(array $roles): array
|
|
{
|
|
$ids = array_values(Role::query()
|
|
->whereIn('name', $roles)
|
|
->pluck('id')
|
|
->map(fn (int $id): int => $id)
|
|
->all());
|
|
|
|
if (count($ids) !== count(array_unique($roles))) {
|
|
throw ValidationException::withMessages(['roles' => 'Um ou mais perfis selecionados não existem.']);
|
|
}
|
|
|
|
return $ids;
|
|
}
|
|
|
|
private function activeAdministratorCount(): int
|
|
{
|
|
return User::query()
|
|
->where('active', true)
|
|
->whereHas('roles', fn ($query) => $query->where('name', RoleName::Administrator->value))
|
|
->count();
|
|
}
|
|
|
|
/** @return array<string, mixed> */
|
|
private function snapshot(User $user): array
|
|
{
|
|
return [
|
|
'name' => $user->name,
|
|
'email' => $user->email,
|
|
'department' => $user->department,
|
|
'snipe_user_id' => $user->snipe_user_id,
|
|
'active' => $user->active,
|
|
'roles' => $user->roles()->orderBy('name')->pluck('name')->all(),
|
|
];
|
|
}
|
|
}
|