auth tests

This commit is contained in:
Giuseppe Naponiello
2026-06-22 14:31:44 +02:00
parent 34a8ebc6fb
commit cc4e174880
75 changed files with 4679 additions and 168 deletions

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Actions\Fortify;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Laravel\Fortify\Contracts\CreatesNewUsers;
class CreateNewUser implements CreatesNewUsers
{
use PasswordValidationRules;
/**
* Validate and create a newly registered user.
*
* @param array<string, string> $input
*
* @throws ValidationException
*/
public function create(array $input): User
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'email' => [
'required',
'string',
'email',
'max:255',
Rule::unique(User::class),
],
'password' => $this->passwordRules(),
])->validate();
return User::create([
'name' => $input['name'],
'email' => $input['email'],
'password' => Hash::make($input['password']),
]);
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Actions\Fortify;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Validation\Rules\Password;
trait PasswordValidationRules
{
/**
* Get the validation rules used to validate passwords.
*
* @return array<int, Rule|array<mixed>|string>
*/
protected function passwordRules(): array
{
return ['required', 'string', Password::default(), 'confirmed'];
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Actions\Fortify;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use Laravel\Fortify\Contracts\ResetsUserPasswords;
class ResetUserPassword implements ResetsUserPasswords
{
use PasswordValidationRules;
/**
* Validate and reset the user's forgotten password.
*
* @param array<string, string> $input
*
* @throws ValidationException
*/
public function reset(User $user, array $input): void
{
Validator::make($input, [
'password' => $this->passwordRules(),
])->validate();
$user->forceFill([
'password' => Hash::make($input['password']),
])->save();
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Actions\Fortify;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use Laravel\Fortify\Contracts\UpdatesUserPasswords;
class UpdateUserPassword implements UpdatesUserPasswords
{
use PasswordValidationRules;
/**
* Validate and update the user's password.
*
* @param array<string, string> $input
*
* @throws ValidationException
*/
public function update(User $user, array $input): void
{
Validator::make($input, [
'current_password' => ['required', 'string', 'current_password:web'],
'password' => $this->passwordRules(),
], [
'current_password.current_password' => __('The provided password does not match your current password.'),
])->validateWithBag('updatePassword');
$user->forceFill([
'password' => Hash::make($input['password']),
])->save();
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace App\Actions\Fortify;
use App\Models\User;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Laravel\Fortify\Contracts\UpdatesUserProfileInformation;
class UpdateUserProfileInformation implements UpdatesUserProfileInformation
{
/**
* Validate and update the given user's profile information.
*
* @param array<string, string> $input
*
* @throws ValidationException
*/
public function update(User $user, array $input): void
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'email' => [
'required',
'string',
'email',
'max:255',
Rule::unique('users')->ignore($user->id),
],
])->validateWithBag('updateProfileInformation');
if ($input['email'] !== $user->email &&
$user instanceof MustVerifyEmail) {
$this->updateVerifiedUser($user, $input);
} else {
$user->forceFill([
'name' => $input['name'],
'email' => $input['email'],
])->save();
}
}
/**
* Update the given verified user's profile information.
*
* @param array<string, string> $input
*/
protected function updateVerifiedUser(User $user, array $input): void
{
$user->forceFill([
'name' => $input['name'],
'email' => $input['email'],
'email_verified_at' => null,
])->save();
$user->sendEmailVerificationNotification();
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace App\Actions;
use App\Exceptions\CannotDeleteSystemUserException;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
/**
* Cancellazione definitiva di un utente conforme al GDPR (diritto all'oblio).
*
* Non rimuove la riga (va preservata l'integrità referenziale verso i contenuti
* e la traccia di audit): i contenuti posseduti vengono riassegnati all'utente
* di sistema e i dati personali anonimizzati in-place. Il record resta
* soft-deleted ma, marcato `anonymized_at`, esce dal cestino e dalle liste.
*
* Idempotente: un utente già anonimizzato viene ignorato.
*/
class PurgeUserAction
{
/**
* Modelli posseduti (con colonna `user_id`) da riassegnare all'utente di
* sistema prima dell'anonimizzazione. Aggiungere qui i modelli di contenuto
* man mano che vengono creati (devono usare SoftDeletes).
*
* @var list<class-string>
*/
private const OWNED_MODELS = [];
public function execute(User $user): void
{
if ($user->is_system) {
throw CannotDeleteSystemUserException::make();
}
if ($user->anonymized_at !== null) {
return; // già anonimizzato: idempotente
}
$system = User::where('is_system', true)->firstOrFail();
DB::transaction(function () use ($user, $system) {
$this->reassignContent($user, $system);
$this->anonymize($user);
});
}
/**
* Riassegna all'utente di sistema tutti i contenuti posseduti, inclusi
* quelli a loro volta nel cestino.
*/
private function reassignContent(User $user, User $system): void
{
foreach (self::OWNED_MODELS as $model) {
$model::withTrashed()
->where('user_id', $user->id)
->update(['user_id' => $system->id]);
}
}
/**
* Azzera i dati personali mantenendo la riga per l'integrità referenziale.
*/
private function anonymize(User $user): void
{
$user->forceFill([
'name' => 'Deleted user',
'email' => "deleted-{$user->id}@anonymized.invalid",
'password' => Hash::make(Str::random(60)),
'two_factor_secret' => null,
'two_factor_recovery_codes' => null,
'two_factor_confirmed_at' => null,
'two_factor_setup_completed_at' => null,
'email_verified_at' => null,
'remember_token' => null,
'must_change_password' => false,
'anonymized_at' => now(),
])->save();
}
}