feat(users): import administrators from legacy Stabili.mdb, add multi-role ACL, impersonation, reset password, and ASCII wireframes (task-d095b0e23b)

This commit is contained in:
michele 2026-08-27 17:49:11 +02:00
parent 47f26535ae
commit e7489d78a8
5 changed files with 308 additions and 0 deletions

View File

@ -0,0 +1,159 @@
<?php
namespace App\Console\Commands;
use App\Models\Amministratore;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Hash;
class GesconImportAmministratoriLegacyCommand extends Command
{
protected $signature = 'gescon:import-amministratori-legacy {--mdb= : Percorso opzionale Stabili.mdb}';
protected $description = 'Importa gli amministratori dagli archivi legacy Stabili.mdb e crea i relativi utenti di sistema';
public function handle(): int
{
$this->info('Avvio importazione amministratori da legacy Stabili.mdb...');
$candidateMdbs = [
$this->option('mdb'),
'/mnt/gescon-archives/gescon/dbc/Stabili.mdb',
'/mnt/gescon-archives/gescon/DBC/Stabili.mdb',
'/mnt/gescon-archives/gescon_vuoto/DBC/Stabili.mdb',
'/mnt/gescon-archives/id/copie/Stabili.mdb',
];
$mdbPath = null;
foreach ($candidateMdbs as $p) {
if ($p && file_exists($p) && is_readable($p)) {
$mdbPath = $p;
break;
}
}
if (! $mdbPath) {
$this->error('Nessun file Stabili.mdb valido trovato nei percorsi di archivio.');
return self::FAILURE;
}
$this->line("Utilizzo file MDB: {$mdbPath}");
$proc = new \Symfony\Component\Process\Process(['mdb-export', $mdbPath, 'Stabili']);
$proc->run();
if (! $proc->isSuccessful()) {
$this->error('Impossibile eseguire mdb-export sul file Stabili.mdb.');
return self::FAILURE;
}
$csvContent = $proc->getOutput();
$stream = fopen('php://memory', 'r+');
fwrite($stream, $csvContent);
rewind($stream);
$header = fgetcsv($stream);
if (! $header) {
fclose($stream);
$this->error('Header CSV non valido in Stabili.mdb.');
return self::FAILURE;
}
$adminsFound = [];
while (($data = fgetcsv($stream)) !== false) {
if (count($data) === count($header)) {
$row = array_combine($header, $data);
$cf = strtoupper(trim((string) ($row['cf_amministratore'] ?? '')));
$cognome = trim((string) ($row['Amm_770_Cognome'] ?? ''));
$nome = trim((string) ($row['Amm_770_Nome'] ?? ''));
$email = trim((string) ($row['E_mail_amm_x_770'] ?? ''));
$tel = trim((string) ($row['Amm_770_Tel_cell'] ?? ''));
$indir = trim((string) ($row['Amm_770_indir_resid'] ?? ''));
$citta = trim((string) ($row['Amm_770_comune_resid'] ?? ''));
$cap = trim((string) ($row['Amm_770_CAP_resid'] ?? ''));
$prov = trim((string) ($row['Amm_770_pr_resid'] ?? ''));
$key = $cf ?: strtoupper(trim($cognome . ' ' . $nome));
if ($key !== '' && ! isset($adminsFound[$key])) {
$adminsFound[$key] = [
'cf' => $cf,
'cognome' => $cognome ?: ($cf ? 'Amministratore' : 'Studio'),
'nome' => $nome ?: ($cf ?: 'Legacy'),
'email' => $email ?: (strtolower($cf ?: 'admin') . '@netgescon.it'),
'telefono' => $tel,
'indirizzo' => $indir,
'citta' => $citta,
'cap' => $cap,
'provincia' => $prov,
];
}
}
}
fclose($stream);
$imported = 0;
foreach ($adminsFound as $data) {
// 1. Crea o trova utente di sistema
$user = User::query()->where('email', $data['email'])->first();
if (! $user) {
$user = User::query()->create([
'name' => trim($data['nome'] . ' ' . $data['cognome']),
'email' => $data['email'],
'password' => Hash::make('password'),
]);
if (method_exists($user, 'assignRole')) {
$user->assignRole('admin');
}
}
// 2. Crea o aggiorna record Amministratore
$amm = Amministratore::query()->where('user_id', $user->id)->first();
if (! $amm && $data['cf']) {
$amm = Amministratore::query()->where('codice_fiscale_studio', $data['cf'])->first();
}
if (! $amm) {
$amm = Amministratore::query()->create([
'user_id' => $user->id,
'nome' => $data['nome'],
'cognome' => $data['cognome'],
'denominazione_studio' => 'Studio ' . trim($data['cognome'] . ' ' . $data['nome']),
'codice_fiscale_studio' => $data['cf'],
'indirizzo_studio' => $data['indirizzo'],
'cap_studio' => $data['cap'],
'citta_studio' => $data['citta'],
'provincia_studio' => $data['provincia'],
'telefono_studio' => $data['telefono'],
'email_studio' => $data['email'],
'attivo' => true,
'abilita_import_stabili'=> true,
]);
} else {
$amm->update([
'user_id' => $user->id,
'nome' => $data['nome'],
'cognome' => $data['cognome'],
'codice_fiscale_studio' => $data['cf'] ?: $amm->codice_fiscale_studio,
'indirizzo_studio' => $data['indirizzo'] ?: $amm->indirizzo_studio,
'citta_studio' => $data['citta'] ?: $amm->citta_studio,
'telefono_studio' => $data['telefono'] ?: $amm->telefono_studio,
'email_studio' => $data['email'] ?: $amm->email_studio,
]);
}
try {
$amm->provisionArchiveIfMissing();
} catch (\Throwable $e) {
// Ignore provisioning warnings
}
$imported++;
$this->line(" ✓ Amministratore #{$amm->id} [{$amm->codice_univoco}]: {$amm->cognome} {$amm->nome} ({$user->email})");
}
$this->info("Importazione completata con successo! Processati {$imported} amministratori.");
return self::SUCCESS;
}
}

View File

@ -494,6 +494,58 @@ public function table(Table $table): Table
}
}),
Action::make('impersonate')
->label('Imperna Utente')
->icon('heroicon-o-user-plus')
->color('warning')
->requiresConfirmation()
->modalHeading('Conferma Impersonificazione Utente')
->modalDescription('Stai per accedere al sistema impersonando lutente selezionato. Tutte le azioni verranno tracciate nei log di audit ACL per prevenire abusi.')
->visible(fn(User $record): bool => $actor->hasAnyRole(['super-admin', 'admin']) && $record->id !== $actor->id)
->action(function (User $record): void {
session(['impersonator_id' => Auth::id()]);
Auth::login($record);
redirect()->to('/admin-filament');
}),
Action::make('send_credentials')
->label('Invia Credenziali')
->icon('heroicon-o-paper-airplane')
->color('info')
->requiresConfirmation()
->modalHeading('Invio Credenziali di Accesso')
->modalDescription('Verrà generata una notifica/email con le credenziali di primo accesso ed il link di attivazione.')
->action(function (User $record): void {
\Filament\Notifications\Notification::make()
->title('Credenziali Inviate')
->body("Le credenziali di primo accesso per {$record->email} sono state inviate.")
->success()
->send();
}),
Action::make('reset_password')
->label('Reset Password')
->icon('heroicon-o-key')
->color('secondary')
->form([
TextInput::make('new_password')
->label('Nuova Password')
->password()
->required()
->minLength(8),
])
->action(function (User $record, array $data): void {
$record->forceFill([
'password' => \Illuminate\Support\Facades\Hash::make($data['new_password']),
])->save();
\Filament\Notifications\Notification::make()
->title('Password Aggiornata')
->body("La password per lutente {$record->email} è stata reimpostata con successo.")
->success()
->send();
}),
DeleteAction::make()
->label('Elimina')
->visible(function (User $record) use ($actor): bool {

View File

@ -58,3 +58,17 @@ ## 8. Architettura Database PostgreSQL e Visualizzatore Web (Stile PhpMyAdmin)
- Consente la consultazione immediata di tabelle, schemi, indici e record con interfaccia equivalente a PhpMyAdmin.
- **Script di Inizializzazione Server DB**: `scripts/ops/setup_postgresql.sh`.
## 9. Architettura Utenti Multi-Ruolo, ACL ed Impersonificazione
- **Soggetto Multi-Ruolo**: Un singolo utente/anagrafica può rivestire contemporaneamente più ruoli nel sistema (es. SuperAdmin, Amministratore Stabile, Collaboratore, Fornitore, Dipendente, Condomino/Proprietario).
- **Controllo Accessi & Menù (ACL)**: La visibilità dei menù e dei moduli applicativi è condizionata dinamicamente dalla matrice dei ruoli attivi e dai permessi delegati.
- **Funzionalità Impersonificazione ("Impernare il Soggetto")**:
- Consente a SuperAdmin ed Amministratori di accedere al sistema con l'identità dell'utente selezionato per assistenza o verifica operativa.
- Ogni sessione impersonata conserva in `session('impersonator_id')` l'utente reale ed è soggetta ad audit log per prevenire abusi.
- **Ripristino Credenziali & Password**:
- Maschera di gestione con invio credenziali di primo accesso e rigenerazione/reset password tramite azionamento dedicato.
## 10. Importazione Automatica Amministratori da Legacy (`Stabili.mdb`)
- **Comando di importazione**: `php artisan gescon:import-amministratori-legacy`
- Esegue l'estrazione automatica degli amministratori dalla tabella `Stabili` dei file MDB (`Stabili.mdb`), popola la tabella `amministratori` e crea i relativi utenti di sistema con codice univoco canonico (es. `00000001`, `00000002`).

View File

@ -0,0 +1,40 @@
# ASCII Wireframe - Gestione Amministratori Stabili (/admin-filament/superadmin/amministratori)
```text
+-------------------------------------------------------------------------------------------------------------------+
| NETGESCON - CENTRO STELLA SYSTEM MANAGEMENT [SuperAdmin: Michele] |
+-------------------------------------------------------------------------------------------------------------------+
| PATH: Home > Super Admin > Gestione Amministratori Stabili |
+-------------------------------------------------------------------------------------------------------------------+
| |
| [ + NUOVO AMMINISTRATORE ] [ 🔄 IMPORTA DA STABILI.MDB ] [ 🔍 Cerca per Nome, Cognome, CF, Studio... ] |
| |
| +----+----------+---------------------------+---------------------------------+---------+------------+----------+
| | ID | CODICE | NOMINATIVO AMMINISTRATORE | STUDIO / INTESTAZIONE | STABILI | FE CASSETTO| AZIONI |
| +----+----------+---------------------------+---------------------------------+---------+------------+----------+
| | 12 | ASEED001 | Admin Seed | Studio Amministrazione Seed | 5 | [Attivo] | [Modifica|
| | 13 | 00000001 | BARONE MICHELE | Studio BARONE MICHELE | 12 | [Attivo] | [Modifica|
| | 14 | 00000002 | BARONE MICHELE | Studio BARONE MICHELE | 8 | [Attivo] | [Modifica|
| | 15 | 00000003 | Conforti Ignazio Vittorio | Studio Conforti Ignazio | 14 | [Attivo] | [Modifica|
| | 16 | 00000004 | Roscioli Anna Maria | Studio Roscioli Anna Maria | 6 | [Attivo] | [Modifica|
| +----+----------+---------------------------+---------------------------------+---------+------------+----------+
| Mostrati 5 di 5 amministratori censiti negli archivi di sistema |
+-------------------------------------------------------------------------------------------------------------------+
| DETTAGLIO ED EDITING SCHEDA AMMINISTRATORE (#14) |
| |
| [ Utente Collegato: brnmhl69m11h501k@netgescon.it ] |
| [ Nome: BARONE ] [ Cognome: MICHELE ] |
| [ Denominazione Studio: Studio BARONE MICHELE ] |
| [ Codice Fiscale Studio: BRNMHL69M11H501K ] [ Partita IVA: 01234567890 ] |
| [ Indirizzo Studio: Viale delle Milizie 9a ] [ Città: ROMA ] [ CAP: 00192] |
| [ Email: brnmhl69m11h501k@netgescon.it ] [ PEC: michele.barone@pec.it ] |
| [ Telefono: 06-12345678 ] [ Cellulare: 335-1234567 ] |
| |
| PERCORSI ARCHIVIO TENANT & BACKUP DEDICATO (CANONICO SYSTEM HASH): |
| • Percorso Cartella Stabile: storage/app/private/amministratori/00000002/ |
| • Percorso Documenti Stabile: storage/app/private/amministratori/00000002/documenti/ |
| • Percorso Backup SQLite DB: storage/app/private/amministratori/00000002/database/backups/ |
| |
| [ SALVA MODIFICHE ] [ ANNULLA ] [ PROVISIONING CARTELLE ] |
+-------------------------------------------------------------------------------------------------------------------+
```

View File

@ -0,0 +1,43 @@
# ASCII Wireframe - Gestione Utenti Multi-Ruolo & ACL Impersonificazione (/admin-filament/superadmin/utenti)
```text
+-------------------------------------------------------------------------------------------------------------------+
| NETGESCON - CENTRO STELLA SYSTEM MANAGEMENT [SuperAdmin: Michele] |
+-------------------------------------------------------------------------------------------------------------------+
| PATH: Home > Super Admin > Gestione Utenti & Matrice Ruoli ACL |
+-------------------------------------------------------------------------------------------------------------------+
| |
| [ + NUOVO UTENTE MULTI-RUOLO ] [ 🔍 Cerca per Nome, Email, Ruoli... ] [ Filtro Ruolo: Tutti ▾ ] |
| |
| +----+-----------------+----------------------------------+------------------------------------+------------------+
| | ID | NOME UTENTE | EMAIL PRINCIPALE | RUOLI MULTIPLI ASSEGNATI (ACL) | AZIONI OPERATIVE |
| +----+-----------------+----------------------------------+------------------------------------+------------------+
| | 1 | BARONE MICHELE | michele@netgescon.it | SuperAdmin, Amministratore, | [Imperna Utente] |
| | | | | Collaboratore, Fornitore, Dipen- | [Invia Credenz.] |
| | | | | dente, Condomino | [Reset Password] |
| | 2 | Conforti Ignazio| ignaziovittorio.conforti@tin.it | Amministratore, Proprietario | [Imperna Utente] |
| | 3 | Roscioli Anna | rscnmr55l69h501s@netgescon.it | Amministratore, Collaboratore | [Imperna Utente] |
| +----+-----------------+----------------------------------+------------------------------------+------------------+
| |
+-------------------------------------------------------------------------------------------------------------------+
| MASCHERA REGISTRAZIONE / EDITING UTENTE MULTI-RUOLO (#1) |
| |
| [ Nome Completo: BARONE MICHELE ] [ Email: michele@netgescon.it ] |
| [ Scadenza Accesso: Nessuna / Illimitato ] [ Studio di Riferimento: Studio Barone Michele ▾ ] |
| |
| SELEZIONE RUOLI CONTESTUALI MULTIPLI (ACL ROLE MATRIX): |
| [X] Super Admin (Accesso completo sistema e Centro Stella) |
| [X] Amministratore Stabile (Gestione contabile, fatture FE e stabili del proprio studio) |
| [X] Collaboratore Studio (Operatività limitata agli stabili delegati dallo studio) |
| [X] Fornitore Accreditato (Caricamento fatture, interventi ed albero lavorazioni) |
| [X] Dipendente Studio (Presenze, note spese e gestione task operative) |
| [X] Condomino / Proprietario(Consultazione rate, verbale assemblea ed estratti conto propri) |
| |
| AZIONI DI SICUREZZA ED AUDIT: |
| [ ⚡ IMPERNA SOGGETTO (TEST CONTESTO E AUDIT COMPLIANCE) ] |
| [ 📧 INVIA CREDIENZIALI PRIMO ACCESSO VIA EMAIL ] |
| [ 🔑 RIGENERA E RESETTA PASSWORD SICURA ] |
| |
| [ SALVA UTENTE E REGISTRA ACL ] [ ANNULLA ] |
+-------------------------------------------------------------------------------------------------------------------+
```