[FEAT] Allineamento NIS2 ↔ TRPG (Fasi 1-5): SSO + Sessions + Reset + Impersonate + Branding
Implementazione completa del progetto allineamento alla suite Evix (TRPG/lg231),
basato sul doc canonico docs/GAP_TRPG_NIS2_ALIGNMENT.md (5 fasi, 18 gap).
Version 1.0.0 → 1.5.0
Fase 1 — SSO Federation (v1.1.0)
- Migration 015_sso_columns: users.sso_identity_id + password_version
- application/services/SsoHelper.php (client SSO dual-mode, cURL nativo, zero deps)
- AuthController::login() + changePassword() conditional SSO (SSO_MODE=local default)
Fase 2 — Multi-device Sessions (v1.2.0)
- Migration 016_active_sessions: tabella + refresh_tokens.session_jti
- BaseController::requireAuth() verifica jti + last_activity throttle + parseDeviceLabel
- login() genera jti, logout/changePassword revoca selettiva
- GET/DELETE /auth/sessions[/{id}]
- UI settings.html tab Sicurezza con lista device + revoca
Fase 3 — Password Reset + Tenant Switcher (v1.3.0)
- Migration 017_password_reset_tokens (TTL 30min, single-use)
- POST /auth/forgot-password (risposta opaca) + reset-password
- Pagine forgot-password.html + reset-password.html (con strength bar)
- EmailService::sendPasswordReset
- POST /auth/switchContext con rotazione JWT + organization_id claim
- Dropdown tenant in sidebar esposto a tutti gli utenti con ≥2 org
Fase 4 — Impersonate + Preferences + Versioning UI (v1.4.0)
- POST /auth/impersonate (super_admin o consulente stesso firm, TTL 1h, audit)
- Migration 018_user_preferences: users.theme/timezone/notif_email/notif_inapp
- GET/PUT /auth/preferences
- Sidebar footer mostra versione + changelog modal su click
Fase 5 — Branding white-label + Auth-gate (v1.5.0)
- Migration 019_firm_branding (logo/colori/brand_name per consulting firm)
- BrandingController GET /branding/current (auth opzionale) + PUT
- common.js auto-applica CSS variables al boot
- public/js/auth-gate.js (gate password client-side per docs riservati, da TRPG)
Skip motivati:
- G15 demo login: simulator esistenti coprono
- G18 refactor controllers: rinviato (~5gg, valore tecnico solo)
Cron sync SSO: AgileHub Ticket #220 aperto a team AGILEHUB per estendere
sso-password-sync.sh al DB nis2_agile_db. Prerequisito per switch SSO_MODE=dual.
Backup files: tutti i file modificati hanno .bak.pre-{fase}-{ts} sia in DEV
sia in /var/www/nis2-agile/.backups/ su Hetzner (rollback ready).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
c37423f900
commit
e4f9e9179e
@@ -0,0 +1,66 @@
|
||||
-- Migration 015: SSO Federation columns
|
||||
-- Progetto allineamento NIS2 ↔ TRPG — Fase 1 / G01
|
||||
-- Data: 2026-05-29
|
||||
--
|
||||
-- Aggiunge le colonne necessarie a collegare gli utenti NIS2 alle identità SSO
|
||||
-- centralizzate in `nexus_tenant_db.sso_identities` (gestito da agile-services).
|
||||
--
|
||||
-- - sso_identity_id: FK logica verso nexus_tenant_db.sso_identities.id.
|
||||
-- NULL = utente non ancora linkato (sarà popolato lazy al primo
|
||||
-- login post-SSO se l'email matcha — decisione utente 2026-05-29).
|
||||
-- - password_version: contatore monotono incrementato a ogni cambio password SSO;
|
||||
-- usato dal cron sync `sso-password-sync.sh` per decidere quando
|
||||
-- riallineare password_hash locale.
|
||||
--
|
||||
-- Comportamento atteso post-migration:
|
||||
-- * Utenti esistenti: sso_identity_id=NULL, password_version=1
|
||||
-- * Login locale continua a funzionare senza modifiche (SSO_MODE=local di default)
|
||||
-- * Nessun controller esistente si rompe (campi additivi)
|
||||
--
|
||||
-- Rollback:
|
||||
-- ALTER TABLE users
|
||||
-- DROP INDEX idx_sso_identity,
|
||||
-- DROP COLUMN sso_identity_id,
|
||||
-- DROP COLUMN password_version;
|
||||
--
|
||||
-- Note MySQL 8.x:
|
||||
-- * `ADD COLUMN IF NOT EXISTS` non standard → controllo via information_schema.
|
||||
-- * Eseguire come utente con privilegio ALTER su nis2_agile_db.
|
||||
|
||||
SET @col_sso := (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'users' AND COLUMN_NAME = 'sso_identity_id'
|
||||
);
|
||||
SET @sql_sso := IF(@col_sso = 0,
|
||||
'ALTER TABLE users ADD COLUMN sso_identity_id INT NULL COMMENT ''FK logica verso nexus_tenant_db.sso_identities.id'' AFTER email_verified_at',
|
||||
'SELECT ''sso_identity_id già presente — skip'' AS info'
|
||||
);
|
||||
PREPARE stmt FROM @sql_sso; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @col_ver := (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'users' AND COLUMN_NAME = 'password_version'
|
||||
);
|
||||
SET @sql_ver := IF(@col_ver = 0,
|
||||
'ALTER TABLE users ADD COLUMN password_version INT NOT NULL DEFAULT 1 COMMENT ''Contatore versione password SSO — bumpato a ogni change-password'' AFTER sso_identity_id',
|
||||
'SELECT ''password_version già presente — skip'' AS info'
|
||||
);
|
||||
PREPARE stmt FROM @sql_ver; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @idx_sso := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'users' AND INDEX_NAME = 'idx_sso_identity'
|
||||
);
|
||||
SET @sql_idx := IF(@idx_sso = 0,
|
||||
'CREATE INDEX idx_sso_identity ON users (sso_identity_id)',
|
||||
'SELECT ''idx_sso_identity già presente — skip'' AS info'
|
||||
);
|
||||
PREPARE stmt FROM @sql_idx; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- Verifica finale
|
||||
SELECT
|
||||
COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_COMMENT
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'users'
|
||||
AND COLUMN_NAME IN ('sso_identity_id', 'password_version');
|
||||
Reference in New Issue
Block a user