diff --git a/HANDOFF.md b/HANDOFF.md index 37a5ce9..b496466 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -111,6 +111,17 @@ lavoro nuovi, stesso master), `php-fpm -i` riporta `session.upload_progress.enabled => Off` e `session.use_strict_mode => On`, login 200 su entrambi. +### Password del sorvegliante dal pannello Utenti (21 ottobre 2026, solo su dev) + +Richiesta dell'utente: poter reimpostare la password del sorvegliante dal +pannello di amministrazione. Prima si poteva solo dalla pagina del gruppo; in +*Utenti* la sua riga non aveva azioni. Ora ha "Nuova password temporanea" (con +conferma) e "Apri il gruppo"; la password compare una volta sola in cima +all'elenco, con l'email, fino a "Ho annotato la password". La logica è una +sola, `GruppiServizio::nuovaPasswordTemporanea()`, usata anche da +`GruppoShow`; rifiuta gli utenti che non sono sorveglianti (per loro c'è il +modulo di modifica). Test in `GruppiServizioTest`. + ### Anteprima dal numero in "Il mio gruppo" (21 ottobre 2026, solo su dev) Richiesta dell'utente: anche il sorvegliante deve poter vedere l'anteprima del @@ -187,7 +198,12 @@ qui sotto **com'è oggi**, in fondo la storia. e dal profilo l'email non si cambia. Ruolo `sorvegliante`, con il solo permesso `gruppo.gestisci`; non compare nella pagina *Utenti*, dove le utenze legate a un proclamatore non si modificano né si eliminano. - "Nuova password temporanea" dalla pagina del gruppo. + "Nuova password temporanea" dalla pagina del gruppo o dalla riga del + sorvegliante nel pannello *Utenti* (stessa logica, + `GruppiServizio::nuovaPasswordTemporanea()`: password provvisoria, cambio + obbligato, "ricordami" azzerato, log `password_reimpostata`; le sessioni + aperte si chiudono perché cambia l'hash). In *Utenti* è l'unica azione su + quell'utenza, con "Apri il gruppo". - **Cosa vede**: `LimitaSorvegliante` (gruppo `web`) lascia a chi ha **solo** quel ruolo `mio-gruppo*`, `assegnazioni.rientra` (solo per il suo gruppo: `Rientra::autorizza`), profilo, cambio password e le azioni Livewire — diff --git a/README.md b/README.md index 7884d08..30017c8 100644 --- a/README.md +++ b/README.md @@ -759,7 +759,10 @@ docker compose --profile dev up -d --build "Password temporanea", non "Ripristino da backup"). L'email non si chiede, non riceve posta e non va confermata. Due gruppi non possono avere lo stesso nome. Se perde la password, l'amministratore ne genera un'altra - dalla pagina del gruppo + temporanea dalla pagina del gruppo o dal pannello *Utenti* ("Nuova password + temporanea" sulla riga del sorvegliante): si vede una volta sola, va + cambiata al primo accesso, e le sessioni aperte con quella vecchia si + chiudono - I territori del pacchetto si aggiungono in blocco tra quelli in reparto, **in ordine di priorità** come "Da assegnare" nella Home (prima i prioritari, poi quelli fermi da più tempo), con ricerca, filtri per zona, @@ -858,7 +861,8 @@ L'amministratore ha un bypass completo (`Gate::before` in `AppServiceProvider`). Il menu laterale mostra solo le voci per cui l'utente ha i permessi. Il ruolo **sorvegliante** non si assegna dalla pagina *Utenti*: nasce creando -un gruppo di servizio e sparisce sciogliendolo. Chi ha solo quel ruolo è +un gruppo di servizio e sparisce sciogliendolo. Della sua utenza, in *Utenti*, +si può solo generare una nuova password temporanea (e aprire il gruppo). Chi ha solo quel ruolo è confinato da `LimitaSorvegliante` alla pagina del proprio gruppo, al rientro dei territori del gruppo e al profilo; le sue azioni ricontrollano che territorio e assegnazione siano del suo gruppo. diff --git a/app/Livewire/Gruppi/GruppoShow.php b/app/Livewire/Gruppi/GruppoShow.php index 2bec168..53889a5 100644 --- a/app/Livewire/Gruppi/GruppoShow.php +++ b/app/Livewire/Gruppi/GruppoShow.php @@ -157,11 +157,7 @@ class GruppoShow extends Component return; } - $password = Str::password(12, symbols: false); - $utente->forceFill(['password' => $password, 'password_change_required' => true, 'password_temporanea' => true])->save(); - activity()->causedBy(auth()->user())->performedOn($utente)->log('password_reimpostata'); - - $this->passwordTemporanea = $password; + $this->passwordTemporanea = app(GruppiServizio::class)->nuovaPasswordTemporanea($utente); } /** diff --git a/app/Livewire/Settings/UsersIndex.php b/app/Livewire/Settings/UsersIndex.php index 7c39537..fc72032 100644 --- a/app/Livewire/Settings/UsersIndex.php +++ b/app/Livewire/Settings/UsersIndex.php @@ -3,8 +3,11 @@ namespace App\Livewire\Settings; use App\Livewire\Concerns\Avvisa; +use App\Models\GruppoServizio; use App\Models\User; +use App\Services\GruppiServizio; use Illuminate\Validation\Rule; +use InvalidArgumentException; use Livewire\Attributes\Locked; use Livewire\Component; use Spatie\Permission\Models\Role; @@ -22,6 +25,17 @@ class UsersIndex extends Component #[Locked] public array $availableRoles = []; + /** + * Nuova password temporanea di un sorvegliante di gruppo, mostrata una + * volta sola (come nella pagina del gruppo). Locked: le scrive solo + * nuovaPasswordSorvegliante(). + */ + #[Locked] + public ?int $passwordPerUtenteId = null; + + #[Locked] + public ?string $passwordTemporanea = null; + public ?int $editingUserId = null; public string $editName = ''; public string $editEmail = ''; @@ -238,10 +252,39 @@ class UsersIndex extends Component $this->avvisa('Utente cancellato. I log sono stati preservati.'); } + /** + * Il sorvegliante ha perso la password: una temporanea nuova, da cambiare + * al primo accesso. È l'unica cosa che di quell'utenza si fa da qui; il + * resto passa dal gruppo (gestitaDaiGruppi). + */ + public function nuovaPasswordSorvegliante(int $userId): void + { + $this->authorize('settings.manage'); + $user = User::query()->findOrFail($userId); + + try { + $this->passwordTemporanea = app(GruppiServizio::class)->nuovaPasswordTemporanea($user); + } catch (InvalidArgumentException $e) { + $this->avvisaErrore($e->getMessage()); + + return; + } + + $this->passwordPerUtenteId = $user->id; + } + + public function chiudiPassword(): void + { + $this->reset(['passwordPerUtenteId', 'passwordTemporanea']); + } + public function render() { return view('livewire.settings.users-index', [ 'users' => User::query()->with('roles')->orderBy('name')->get(), + // Per ogni sorvegliante il suo gruppo, per il collegamento. + 'gruppiDeiSorveglianti' => GruppoServizio::pluck('id', 'sorvegliante_id'), + 'utentePassword' => $this->passwordPerUtenteId ? User::find($this->passwordPerUtenteId) : null, ]); } } diff --git a/app/Services/GruppiServizio.php b/app/Services/GruppiServizio.php index b08060e..9e0b332 100644 --- a/app/Services/GruppiServizio.php +++ b/app/Services/GruppiServizio.php @@ -80,6 +80,32 @@ class GruppiServizio return [$gruppo, $password]; } + /** + * Una password temporanea nuova per il sorvegliante che l'ha persa: da + * cambiare al primo accesso ("Password temporanea"). Le sessioni aperte + * con quella vecchia si chiudono alla richiesta successiva + * (AuthenticateSession: è cambiato l'hash), il "ricordami" si azzera. + * Restituisce la password, da mostrare una volta sola. + */ + public function nuovaPasswordTemporanea(User $utente): string + { + if (! $utente->proclamatore_id || ! $utente->isSorvegliante()) { + throw new InvalidArgumentException("{$utente->name} non è il sorvegliante di un gruppo."); + } + + $password = Str::password(12, symbols: false); + $utente->forceFill([ + 'password' => $password, + 'password_change_required' => true, + 'password_temporanea' => true, + 'remember_token' => null, + ])->save(); + + activity()->causedBy(auth()->user())->performedOn($utente)->log('password_reimpostata'); + + return $password; + } + /** * L'email fittizia del sorvegliante, con cui entra: iniziale del nome e * cognome, minuscoli, senza accenti, spazi né apostrofi, sul dominio diff --git a/resources/views/livewire/settings/users-index.blade.php b/resources/views/livewire/settings/users-index.blade.php index 7a1fec9..4bd73e4 100644 --- a/resources/views/livewire/settings/users-index.blade.php +++ b/resources/views/livewire/settings/users-index.blade.php @@ -57,6 +57,19 @@
Nuova password temporanea per {{ $utentePassword->name }}, da cambiare al primo accesso. Si vede solo ora:
+{{ $utentePassword->email }}{{ $passwordTemporanea }}|
@if($user->proclamatore_id)
- {{-- Utenza di un sorvegliante di gruppo: si gestisce dal gruppo. --}}
-
- Sorvegliante: si gestisce da Gruppi di servizio
-
+ {{-- Utenza di un sorvegliante di gruppo: si gestisce dal gruppo;
+ da qui solo la password, se l'ha persa. --}}
+
+
@else
|