$attributes Güncellenecek güvenli kullanıcı alanları. * @param array $roleIds Seçili rollerin benzersizleştirilecek kimlikleri. * @return User Güncel kullanıcı ve rol ilişkileri. * * @throws Throwable Yetkilendirme, doğrulama veya kalıcılık işlemi başarısız olduğunda. * * @author Gökhan Engin - İletişim : https://bit.ly/YazılımUzmanı */ public function update(User $actor, User $subject, array $attributes, array $roleIds): User { $correlationId = (string) Str::uuid(); $previousRoles = $subject->roles()->pluck('name')->sort()->values()->all(); $requestedRoles = $this->roleNamesForIds($roleIds); try { if (! $actor->can('update', $subject) || ! $actor->hasPermission('roles.assign')) { throw new AuthorizationException; } return Cache::lock('super-admin-governance', 10)->block(5, fn () => Cache::lock('user-role-update:'.$subject->id, 10)->block(5, fn () => DB::transaction(function () use ($actor, $subject, $attributes, $roleIds, $correlationId) { $locked = User::query()->lockForUpdate()->findOrFail($subject->id); $roles = Role::query()->whereIn('id', array_values(array_unique($roleIds)))->get(); if ($roles->count() !== count(array_unique($roleIds))) { throw ValidationException::withMessages(['data.roles' => __('roles.invalid')]); } if (($attributes['status'] ?? $locked->status->value) === UserStatus::Active->value && $roles->isEmpty()) { throw ValidationException::withMessages(['data.roles' => __('roles.required')]); } $before = $locked->roles()->pluck('name')->sort()->values()->all(); $after = $roles->pluck('name')->sort()->values()->all(); $touchesSuperAdmin = in_array(RoleName::SuperAdmin->value, array_merge($before, $after), true); if ($touchesSuperAdmin && ! $actor->hasRole(RoleName::SuperAdmin)) { throw new AuthorizationException; } if ($actor->is($locked) && in_array(RoleName::SuperAdmin->value, $before, true) && ! in_array(RoleName::SuperAdmin->value, $after, true)) { throw ValidationException::withMessages(['data.roles' => __('roles.self_remove')]); } $wouldDisableSuper = in_array(RoleName::SuperAdmin->value, $before, true) && (($attributes['status'] ?? $locked->status->value) !== UserStatus::Active->value || ! in_array(RoleName::SuperAdmin->value, $after, true)); if ($wouldDisableSuper && $this->activeSuperAdminCount($locked->id) === 0) { throw ValidationException::withMessages(['data.roles' => __('roles.last_super_admin')]); } if ($actor->is($locked) && ($attributes['status'] ?? $locked->status->value) !== UserStatus::Active->value) { throw ValidationException::withMessages(['data.status' => __('roles.self_suspend')]); } $locked->fill($attributes)->save(); $locked->roles()->sync($roles->modelKeys()); RoleAssignmentAudit::create([ 'actor_user_id' => $actor->id, 'subject_user_id' => $locked->id, 'correlation_id' => $correlationId, 'previous_roles' => $before, 'new_roles' => $after, 'added_roles' => array_values(array_diff($after, $before)), 'removed_roles' => array_values(array_diff($before, $after)), 'result' => 'success', ]); return $locked->refresh(); }, 3))); } catch (Throwable $exception) { RoleAssignmentAudit::create([ 'actor_user_id' => $actor->id, 'subject_user_id' => $subject->id, 'correlation_id' => $correlationId, 'previous_roles' => $previousRoles, 'new_roles' => $requestedRoles, 'added_roles' => array_values(array_diff($requestedRoles, $previousRoles)), 'removed_roles' => array_values(array_diff($previousRoles, $requestedRoles)), 'result' => 'failure', ]); throw $exception; } } /** * İstemciden gelen rol kimliklerini mevcut rol kayıtlarındaki sıralı adlara dönüştürür. * * @param array $roleIds Çözümlenecek rol kimlikleri. * @return array Veritabanında bulunan benzersiz rol adları. * * @author Gökhan Engin - İletişim : https://bit.ly/YazılımUzmanı */ private function roleNamesForIds(array $roleIds): array { return Role::query()->whereIn('id', array_values(array_unique($roleIds))) ->pluck('name')->sort()->values()->all(); } public function replaceRole(User $actor, User $subject, RoleName|string $role): void { $name = $role instanceof RoleName ? $role->value : $role; $this->update($actor, $subject, [], [Role::where('name', $name)->sole()->id]); } public function delete(User $actor, User $subject): void { if ($actor->is($subject)) { throw ValidationException::withMessages(['user' => __('roles.self_delete')]); } if (! $actor->can('delete', $subject)) { throw new AuthorizationException; } Cache::lock('super-admin-governance', 10)->block(5, fn () => DB::transaction(function () use ($actor, $subject) { $locked = User::query()->lockForUpdate()->findOrFail($subject->id); if (! $actor->can('delete', $locked)) { throw new AuthorizationException; } if ($locked->hasRole(RoleName::SuperAdmin) && $this->activeSuperAdminCount($locked->id) === 0) { throw ValidationException::withMessages(['user' => __('roles.last_super_admin_delete')]); } DB::table('sessions')->where('user_id', $locked->id)->delete(); $locked->tokens()->delete(); $locked->forceFill(['remember_token' => Str::random(60)])->saveQuietly(); $locked->delete(); }, 3)); } private function activeSuperAdminCount(int $exceptId): int { return User::query()->where('id', '!=', $exceptId)->where('status', UserStatus::Active->value) ->whereHas('roles', fn ($q) => $q->where('name', RoleName::SuperAdmin->value))->lockForUpdate()->count(); } }