++ cambio password obbligato: al primo accesso del sorvegliante dice "Password temporanea", non "Ripristino da backup"
users.password_temporanea distingue la password provvisoria data dall'amministratore (creazione del gruppo, "Nuova password temporanea") dal ripristino da backup. Migrazione 000040; si spegne a password cambiata, il ripristino e l'import XML la spengono per tutti. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -210,6 +210,15 @@ Decisioni successive dell'utente (20 ottobre 2026):
|
||||
obbligatoria `users.email`. XML: niente più `<username>` né
|
||||
`<disattivato_at>`; da un file vecchio non si importano le utenze
|
||||
disattivate né i gruppi con `<sciolto_at>`.
|
||||
- **Primo accesso del sorvegliante**: la pagina del cambio password obbligato
|
||||
diceva sempre "Ripristino da backup eseguito" (era l'unico motivo
|
||||
previsto). Ora `users.password_temporanea` (migrazione `000040`, che la
|
||||
accende per i sorveglianti che non hanno ancora cambiato la password)
|
||||
distingue la password provvisoria data dall'amministratore — creazione del
|
||||
gruppo, "Nuova password temporanea" — e la pagina dice "Password
|
||||
temporanea". Si spegne a password cambiata (pagina obbligatoria o
|
||||
profilo); il ripristino e l'import XML la spengono per tutti, perché lì il
|
||||
motivo è il ripristino.
|
||||
- **Anche i membri si segnalano**: `Proclamatore::gruppo_etichetta`
|
||||
("Gruppo Zisa" / "Sorvegliante del gruppo Zisa") come badge nell'elenco
|
||||
(più il filtro *In un gruppo di servizio*, `filtroStato=gruppo`) e in testa
|
||||
|
||||
@@ -745,7 +745,8 @@ docker compose --profile dev up -d --build
|
||||
fittizia** creata dall'applicazione: iniziale del nome e cognome sul
|
||||
dominio `termanager.it` (Francesco Fontana → `ffontana@termanager.it`; se
|
||||
è già presa, un numero in coda), e una password temporanea. Si vedono una
|
||||
volta sola; la password va cambiata al primo accesso. L'email non si
|
||||
volta sola; la password va cambiata al primo accesso (la pagina dice
|
||||
"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
|
||||
@@ -894,7 +895,7 @@ una revoca ha effetto immediato senza bisogno di ricaricare la pagina.
|
||||
|
||||
| Tabella | Descrizione |
|
||||
|-------------------|--------------------------------------------------------------|
|
||||
| `users` | Utenti; `password_change_required` blocca l'accesso finché non si sceglie una password nuova; `proclamatore_id` lega l'utenza del sorvegliante (email fittizia) al suo proclamatore; sciolto il gruppo l'utenza si elimina |
|
||||
| `users` | Utenti; `password_change_required` blocca l'accesso finché non si sceglie una password nuova (`password_temporanea` dice se il motivo è una password provvisoria data dall'amministratore invece di un ripristino); `proclamatore_id` lega l'utenza del sorvegliante (email fittizia) al suo proclamatore; sciolto il gruppo l'utenza si elimina |
|
||||
| `settings` | Configurazione singleton |
|
||||
| `zone` | Zone territoriali |
|
||||
| `tipologie` | Tipologie di territorio |
|
||||
|
||||
@@ -9,7 +9,9 @@ use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
/**
|
||||
* Cambio password obbligatorio, imposto dopo il ripristino di un backup.
|
||||
* Cambio password obbligatorio, imposto dopo il ripristino di un backup o al
|
||||
* primo accesso con una password temporanea data dall'amministratore
|
||||
* (sorvegliante di gruppo): `password_temporanea` dice quale dei due.
|
||||
*
|
||||
* Volutamente NON e' un componente Livewire: la pagina deve restare
|
||||
* raggiungibile mentre RequirePasswordChange respinge tutto il resto, incluse
|
||||
@@ -27,6 +29,8 @@ class PasswordChangeController extends Controller
|
||||
// Chi arriva qui spontaneamente (dal profilo) puo' tornare indietro;
|
||||
// chi e' stato dirottato dal middleware no.
|
||||
'forced' => (bool) $user->password_change_required,
|
||||
// Perché: una password temporanea, oppure un ripristino da backup.
|
||||
'temporanea' => (bool) $user->password_temporanea,
|
||||
// Il ripristino gira in coda e non ha una sessione in cui lasciare
|
||||
// un flash; deposita l'esito in cache a nome di chi l'ha avviato e
|
||||
// lo si legge qui, che e' la prima pagina che quella persona vede.
|
||||
@@ -71,6 +75,7 @@ class PasswordChangeController extends Controller
|
||||
$user->update([
|
||||
'password' => $validated['password'],
|
||||
'password_change_required' => false,
|
||||
'password_temporanea' => false,
|
||||
]);
|
||||
|
||||
// La sessione corrente sopravvive al cambio, le altre no: dopo un
|
||||
|
||||
@@ -250,6 +250,9 @@ class RestoreBackupArchive implements ShouldQueue
|
||||
{
|
||||
DB::table('users')->update([
|
||||
'password_change_required' => true,
|
||||
// Il motivo ora è il ripristino, anche per chi aveva una
|
||||
// password temporanea (pagina del cambio password).
|
||||
'password_temporanea' => false,
|
||||
'remember_token' => null,
|
||||
]);
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ class GruppoShow extends Component
|
||||
}
|
||||
|
||||
$password = Str::password(12, symbols: false);
|
||||
$utente->forceFill(['password' => $password, 'password_change_required' => true])->save();
|
||||
$utente->forceFill(['password' => $password, 'password_change_required' => true, 'password_temporanea' => true])->save();
|
||||
activity()->causedBy(auth()->user())->performedOn($utente)->log('password_reimpostata');
|
||||
|
||||
$this->passwordTemporanea = $password;
|
||||
|
||||
@@ -80,6 +80,7 @@ class Profile extends Component
|
||||
auth()->user()->update([
|
||||
'password' => $this->password,
|
||||
'password_change_required' => false,
|
||||
'password_temporanea' => false,
|
||||
]);
|
||||
|
||||
$this->reset(['current_password', 'password', 'password_confirmation']);
|
||||
|
||||
@@ -582,6 +582,7 @@ class XmlExchange extends Component
|
||||
if ($passwordHash !== '' && ! $eAttore) {
|
||||
$meta['password'] = $passwordHash;
|
||||
$meta['password_change_required'] = true;
|
||||
$meta['password_temporanea'] = false;
|
||||
}
|
||||
if (! $eAttore) {
|
||||
$meta['remember_token'] = null;
|
||||
|
||||
@@ -26,6 +26,7 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
'email',
|
||||
'password',
|
||||
'password_change_required',
|
||||
'password_temporanea',
|
||||
'proclamatore_id',
|
||||
];
|
||||
|
||||
@@ -40,6 +41,9 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
'password_change_required' => 'boolean',
|
||||
// Il cambio obbligatorio viene da una password temporanea data
|
||||
// dall'amministratore, non da un ripristino (pagina del cambio).
|
||||
'password_temporanea' => 'boolean',
|
||||
'proclamatore_id' => 'integer',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ class GruppiServizio
|
||||
'email' => self::emailFittizia($sorvegliante),
|
||||
'password' => $password,
|
||||
'password_change_required' => true,
|
||||
'password_temporanea' => true,
|
||||
'proclamatore_id' => $sorvegliante->id,
|
||||
]);
|
||||
$utente->assignRole('sorvegliante');
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* Perché un utente deve cambiare la password: dopo un ripristino da backup
|
||||
* (com'era finora l'unico caso) o perché l'amministratore gli ha dato una
|
||||
* password temporanea (sorvegliante di gruppo, alla creazione del gruppo o
|
||||
* con "Nuova password temporanea"). La pagina del cambio password spiega il
|
||||
* motivo giusto.
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->boolean('password_temporanea')->default(false)->after('password_change_required');
|
||||
});
|
||||
|
||||
// I sorveglianti che non hanno ancora cambiato la password di
|
||||
// creazione del gruppo: la loro è temporanea, non da ripristino.
|
||||
DB::table('users')
|
||||
->whereNotNull('proclamatore_id')
|
||||
->where('password_change_required', true)
|
||||
->update(['password_temporanea' => true]);
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('password_temporanea');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -21,7 +21,15 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($forced)
|
||||
@if($forced && $temporanea)
|
||||
{{-- Primo accesso del sorvegliante, o password nuova data dall'amministratore --}}
|
||||
<div class="rounded-lg border border-amber-300 bg-amber-50 px-4 py-3 text-sm text-amber-800">
|
||||
<strong>Password temporanea.</strong>
|
||||
Stai usando la password provvisoria che ti ha dato l’amministratore:
|
||||
scegline una tua per continuare. Fino ad allora il resto
|
||||
dell’applicazione resta bloccato.
|
||||
</div>
|
||||
@elseif($forced)
|
||||
<div class="rounded-lg border border-amber-300 bg-amber-50 px-4 py-3 text-sm text-amber-800">
|
||||
<strong>Ripristino da backup eseguito.</strong>
|
||||
Il pacchetto di backup contiene le password di tutti gli account, quindi
|
||||
|
||||
@@ -219,6 +219,33 @@ class GruppiServizioTest extends TestCase
|
||||
$this->get(route('dashboard'))->assertOk()->assertDontSee('Il mio gruppo')->assertSee('Gruppi di servizio');
|
||||
}
|
||||
|
||||
public function test_al_primo_accesso_il_sorvegliante_cambia_la_password_temporanea(): void
|
||||
{
|
||||
$rossi = $this->proclamatore('Rossi', 'Mario');
|
||||
[$gruppo] = app(GruppiServizio::class)->crea('Zisa', $rossi);
|
||||
$s = User::where('email', 'mrossi@termanager.it')->sole();
|
||||
$this->assertTrue($s->password_temporanea);
|
||||
$this->flushSession();
|
||||
$this->actingAs($s);
|
||||
|
||||
$this->get(route('mio-gruppo'))->assertRedirect(route('password.change'));
|
||||
$this->get(route('password.change'))->assertOk()
|
||||
->assertSee('Password temporanea')
|
||||
->assertDontSee('Ripristino da backup');
|
||||
|
||||
$this->post(route('password.change.store'), ['password' => 'una-password-mia-1', 'password_confirmation' => 'una-password-mia-1'])
|
||||
->assertRedirect(route('dashboard'));
|
||||
$this->assertFalse($s->fresh()->password_temporanea);
|
||||
$this->assertFalse($s->fresh()->password_change_required);
|
||||
$this->get(route('mio-gruppo'))->assertOk();
|
||||
|
||||
// "Nuova password temporanea" dalla pagina del gruppo: di nuovo temporanea.
|
||||
$this->flushSession();
|
||||
Livewire::actingAs($this->admin)->test(GruppoShow::class, ['gruppo' => $gruppo])->call('nuovaPassword');
|
||||
$this->assertTrue($s->fresh()->password_temporanea);
|
||||
$this->assertTrue($s->fresh()->password_change_required);
|
||||
}
|
||||
|
||||
public function test_il_sorvegliante_entra_con_l_email_fittizia_senza_conferma_e_va_al_suo_gruppo(): void
|
||||
{
|
||||
$this->gruppo();
|
||||
|
||||
Reference in New Issue
Block a user