[ 'code' => 1, 'message' => 'Login riuscito!', ], 'failed' => [ 'code' => 0, 'message' => 'Autenticazione fallita!', ], 'disabled' => [ 'code' => 2, 'message' => 'Utente non abilitato!', ], 'unauthorized' => [ 'code' => 3, 'message' => "L'utente non ha nessun permesso impostato!", ], ]; /** @var array Opzioni di sicurezza relative all'hashing delle password */ protected static $passwordOptions = [ 'algorithm' => PASSWORD_BCRYPT, 'options' => [], ]; /** @var array Informazioni riguardanti l'utente autenticato */ protected $infos; /** @var string Nome del primo modulo su cui l'utente ha permessi di navigazione */ protected $first_module; protected function __construct() { $database = Database::getConnection(); if ($database->isInstalled()) { if (API::isAPIRequest()) { $token = API::getRequest()['token']; $token = !empty($token) ? $token : get('token'); $id = $database->fetchArray('SELECT `id_utente` FROM `zz_tokens` WHERE `token` = '.prepare($token))[0]['id_utente']; } // Controllo sulla sessione attiva elseif (!empty($_SESSION['idutente'])) { $id = $_SESSION['idutente']; } if (!empty($id)) { $this->identifyUser($id); } if (!empty($_SESSION['idutente']) && $this->isAuthenticated()) { $this->saveToSession(); } } } /** * Effettua un tentativo di accesso con le credenziali fornite. * * @param string $username * @param string $password * * @return bool */ public function attempt($username, $password) { session_regenerate_id(); $database = Database::getConnection(); $log = []; $log['username'] = $username; $log['ip'] = get_client_ip(); $log['stato'] = self::$status['failed']['code']; $users = $database->fetchArray('SELECT idutente, password, enabled FROM zz_users WHERE username = '.prepare($username).' LIMIT 1'); if (!empty($users)) { $user = $users[0]; if (!empty($user['enabled'])) { $this->identifyUser($user['idutente']); $module = $this->getFirstModule(); if ( $this->isAuthenticated() && $this->password_check($password, $user['password'], $user['idutente']) && !empty($module) ) { $log['idutente'] = $this->infos['idutente']; $log['stato'] = self::$status['success']['code']; $this->saveToSession(); } else { if (empty($module)) { $log['stato'] = self::$status['unauthorized']['code']; } $this->logout(); } } else { $log['stato'] = self::$status['disabled']['code']; } } if ($log['stato'] != self::$status['success']['code']) { foreach (self::$status as $key => $value) { if ($log['stato'] == $value['code']) { $_SESSION['errors'][] = $value['message']; break; } } } $database->insert('zz_logs', $log); return $this->isAuthenticated(); } /** * Controlla la corrispondeza delle password ed eventalmente effettua un rehashing. * * @param string $password * @param string $hash * @param int $user_id */ protected function password_check($password, $hash, $user_id = null) { $result = false; // Retrocompatibilità if ($hash == md5($password)) { $rehash = true; $result = true; } // Nuova versione if (password_verify($password, $hash)) { $rehash = password_needs_rehash($hash, self::$passwordOptions['algorithm'], self::$passwordOptions['options']); $result = true; } // Controllo in automatico per futuri cambiamenti dell'algoritmo di password if ($rehash) { $database = Database::getConnection(); $database->update('zz_users', ['password' => self::hashPassword($password)], ['idutente' => $user_id]); } return $result; } /** * Memorizza le informazioni riguardanti l'utente all'interno della sessione. */ protected function saveToSession() { foreach ($this->infos as $key => $value) { $_SESSION[$key] = $value; } $identifier = md5($_SESSION['idutente'].$_SERVER['HTTP_USER_AGENT']); if ((empty($_SESSION['last_active']) || time() < $_SESSION['last_active'] + (60 * 60)) && (empty($_SESSION['identifier']) || $_SESSION['identifier'] == $identifier)) { $_SESSION['last_active'] = time(); $_SESSION['identifier'] = $identifier; } } /** * Identifica l'utente interessato dall'autenticazione. * * @param int $user_id */ protected function identifyUser($user_id) { $database = Database::getConnection(); $results = $database->fetchArray('SELECT idutente, idanagrafica, username, (SELECT nome FROM zz_groups WHERE id=idgruppo) AS gruppo FROM zz_users WHERE idutente = '.prepare($user_id).' AND enabled = 1 LIMIT 1'); if (!empty($results)) { $results[0]['is_admin'] = ($results[0]['gruppo'] == 'Amministratori'); $this->infos = $results[0]; } } /** * Controlla se l'utente è autenticato. * * @return bool */ public function isAuthenticated() { return !empty($this->infos); } /** * Controlla se l'utente appartiene al gruppo degli Amministratori. * * @return bool */ public function isAdmin() { return $this->isAuthenticated() && !empty($this->infos['is_admin']); } /** * Restituisce le informazioni riguardanti l'utente autenticato. * * @return array */ public function getUser() { return $this->infos; } /** * Distrugge le informazioni riguardanti l'utente autenticato, forzando il logout. */ public function destory() { if ($this->isAuthenticated() || !empty($_SESSION['idutente'])) { $this->infos = null; $this->first_module = null; session_unset(); session_destroy(); session_start(); session_regenerate_id(); $_SESSION['infos'] = []; $_SESSION['warnings'] = []; $_SESSION['errors'] = []; } } /** * Restituisce il nome del primo modulo navigabile dall'utente autenticato. * * @return string */ public function getFirstModule() { if (empty($this->first_module)) { $query = 'SELECT id FROM zz_modules WHERE enabled = 1'; if (!$this->isAdmin()) { $query .= ' AND id IN (SELECT idmodule FROM zz_permissions WHERE idgruppo = (SELECT id FROM zz_groups WHERE nome = '.prepare($this->getUser()['gruppo']).") AND permessi IN ('r', 'rw'))"; } $database = Database::getConnection(); $results = $database->fetchArray($query." AND options != '' AND options != 'menu' AND options IS NOT NULL ORDER BY `order` ASC"); if (!empty($results)) { $module = null; $first = Settings::get('Prima pagina'); if (!in_array($first, array_column($results, 'id'))) { $module = $results[0]['id']; } else { $module = $first; } $this->first_module = $module; } } return $this->first_module; } /** * Restituisce l'hashing della password per la relativa memorizzazione nel database. * * @param string $password * * @return string */ public static function hashPassword($password) { return password_hash($password, self::$passwordOptions['algorithm'], self::$passwordOptions['options']); } public static function check() { return self::getInstance()->isAuthenticated(); } public static function admin() { return self::getInstance()->isAdmin(); } public static function user() { return self::getInstance()->getUser(); } public static function logout() { return self::getInstance()->destory(); } public static function firstModule() { return self::getInstance()->getFirstModule(); } /** * Restituisce l'elenco degli stati del sistema di autenticazione. * * @return array */ public static function getStatus() { return self::$status; } }