diff --git a/HANDOFF.md b/HANDOFF.md index 9620b1e..c666130 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -121,6 +121,12 @@ Richieste dell'utente, con le sue scelte: e — per l'amministratore — il server di posta. Emergenza: `php artisan utenti:conferma-email `. Il primo admin creato da riga di comando nasce confermato. + **Aggiornamento 19 ottobre 2026 — amministratori esenti** (scelta + dell'utente, preferita a un parametro segreto nell'URL): chi ha il ruolo + `amministratore` non deve confermare l'email (`User::esenteDaConfermaEmail`, + che fa rispondere `hasVerifiedEmail()` true). Non riceve l'email di conferma + né alla creazione né al cambio d'indirizzo; in Utenti compare "esente". Se + perde il ruolo torna a contare `email_verified_at`. - Nei test gli utenti nascono confermati (`tests/TestCase.php`, `$confermaUtentiCreati`). diff --git a/app/Livewire/Profile.php b/app/Livewire/Profile.php index 592cff9..3a53267 100644 --- a/app/Livewire/Profile.php +++ b/app/Livewire/Profile.php @@ -41,7 +41,8 @@ class Profile extends Component // L'indirizzo nuovo va confermato (User::booted lo segna da // confermare): finché non apre il link, l'account resta bloccato. - if ($emailCambiata) { + // Gli amministratori sono esenti (User::esenteDaConfermaEmail). + if ($emailCambiata && ! $user->esenteDaConfermaEmail()) { $user->inviaConfermaEmail() ? session()->flash('success', "Profilo aggiornato. Conferma il nuovo indirizzo: ti abbiamo mandato un'email a {$user->email}.") : session()->flash('error', 'Profilo aggiornato, ma l\'email di conferma non è partita: richiedila qui sotto.'); diff --git a/app/Livewire/Settings/UsersIndex.php b/app/Livewire/Settings/UsersIndex.php index 9e228d2..735cad5 100644 --- a/app/Livewire/Settings/UsersIndex.php +++ b/app/Livewire/Settings/UsersIndex.php @@ -86,8 +86,11 @@ class UsersIndex extends Component $this->selectedRole = $this->availableRoles[0]; } - // L'account funziona solo dopo la conferma dell'indirizzo. - if ($user->inviaConfermaEmail()) { + // L'account funziona solo dopo la conferma dell'indirizzo, tranne + // per gli amministratori (User::esenteDaConfermaEmail). + if ($user->esenteDaConfermaEmail()) { + $this->avvisa('Utente creato. Gli amministratori non devono confermare l\'email: l\'account funziona subito.'); + } elseif ($user->inviaConfermaEmail()) { $this->avvisa("Utente creato. Gli è stata inviata un'email a {$user->email}: l'account funziona dopo che conferma l'indirizzo."); } else { $this->avvisaErrore("Utente creato, ma l'email di conferma non è partita: controlla il server di posta e usa \"Reinvia conferma\"."); @@ -171,13 +174,14 @@ class UsersIndex extends Component $emailCambiata = $user->isDirty('email'); $user->save(); $user->syncRoles([$validated['editSelectedRole']]); + $user->load('roles'); $this->cancelEdit(); // Indirizzo nuovo: torna da confermare (User::booted) e gli si manda // il link, altrimenti il suo account smetterebbe di funzionare senza - // che sappia perché. - if ($emailCambiata) { + // che sappia perché. Gli amministratori sono esenti. + if ($emailCambiata && ! $user->esenteDaConfermaEmail()) { $inviata = $user->inviaConfermaEmail(); $inviata ? $this->avvisa("Utente aggiornato. Il nuovo indirizzo va confermato: email inviata a {$user->email}.") diff --git a/app/Models/User.php b/app/Models/User.php index 74df4b0..a7b3f55 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -58,13 +58,36 @@ class User extends Authenticatable implements MustVerifyEmail }); } + /** + * Gli amministratori sono esenti dalla conferma dell'email: scelta + * esplicita, perché l'amministratore deve poter entrare anche quando la + * posta non funziona (è lui che la configura). Vale finché ha il ruolo: + * se lo perde, torna a contare `email_verified_at`. + */ + public function esenteDaConfermaEmail(): bool + { + return $this->hasRole('amministratore'); + } + + /** + * Usato dal middleware `verified`: un esente è sempre "confermato". + */ + public function hasVerifiedEmail(): bool + { + return $this->esenteDaConfermaEmail() || ! is_null($this->email_verified_at); + } + /** * Manda l'email di conferma; false se il server di posta non risponde o * rifiuta. Chi chiama decide cosa dire: l'utente è comunque salvato, e - * l'email si può richiedere di nuovo. + * l'email si può richiedere di nuovo. Agli esenti non si manda nulla. */ public function inviaConfermaEmail(): bool { + if ($this->esenteDaConfermaEmail()) { + return true; + } + try { $this->sendEmailVerificationNotification(); } catch (\Throwable $e) { diff --git a/resources/views/livewire/settings/users-index.blade.php b/resources/views/livewire/settings/users-index.blade.php index 7feb2e0..be22c37 100644 --- a/resources/views/livewire/settings/users-index.blade.php +++ b/resources/views/livewire/settings/users-index.blade.php @@ -112,7 +112,10 @@ {{ $user->name }} {{ $user->email }} - @if($user->hasVerifiedEmail()) + @if($user->esenteDaConfermaEmail()) + esente + @elseif($user->hasVerifiedEmail()) confermata @else actingAs($utente)->get(route('profile'))->assertOk(); } - public function test_un_amministratore_non_confermato_puo_configurare_la_posta(): void + public function test_un_amministratore_e_esente_dalla_conferma(): void { $admin = $this->utente('amministratore'); - $this->actingAs($admin)->get(route('settings.edit'))->assertRedirect(route('verification.notice')); + $this->assertNull($admin->email_verified_at); + $this->assertTrue($admin->hasVerifiedEmail()); + $this->actingAs($admin)->get(route('settings.edit'))->assertOk(); $this->actingAs($admin)->get(route('posta.edit'))->assertOk()->assertSee('Server di posta'); } + public function test_chi_perde_il_ruolo_di_amministratore_torna_a_dover_confermare(): void + { + $utente = $this->utente('amministratore'); + $utente->syncRoles(['operatore']); + + $this->assertFalse($utente->fresh()->hasVerifiedEmail()); + $this->actingAs($utente->fresh())->get(route('dashboard'))->assertRedirect(route('verification.notice')); + } + + public function test_all_amministratore_creato_non_parte_la_conferma(): void + { + Notification::fake(); + $admin = $this->utente('amministratore'); + + Livewire::actingAs($admin)->test(UsersIndex::class) + ->set('name', 'Luca Neri') + ->set('email', 'luca@example.test') + ->set('password', 'password-di-prova') + ->set('password_confirmation', 'password-di-prova') + ->set('selectedRole', 'amministratore') + ->call('createUser') + ->assertHasNoErrors(); + + $nuovo = User::where('email', 'luca@example.test')->sole(); + $this->assertTrue($nuovo->hasVerifiedEmail()); + Notification::assertNothingSentTo($nuovo); + } + + public function test_l_amministratore_che_cambia_email_resta_dentro(): void + { + Notification::fake(); + $admin = $this->utente('amministratore'); + + Livewire::actingAs($admin)->test(Profile::class) + ->set('name', 'Mario Rossi') + ->set('email', 'nuovo-admin@example.test') + ->call('updateProfile') + ->assertRedirect(route('profile')); + + $this->assertTrue($admin->fresh()->hasVerifiedEmail()); + Notification::assertNothingSentTo($admin->fresh()); + } + public function test_il_link_conferma_anche_senza_essere_entrati(): void { $utente = $this->utente();