gestioneutente

This commit is contained in:
2025-11-23 15:31:32 +01:00
parent 9fb7432694
commit f6014a55e4
6 changed files with 412 additions and 28 deletions

402
app/UsersManager.class.php Normal file
View File

@@ -0,0 +1,402 @@
<?php
/**
* UserManager - Gestione utenti e autenticazione tramite cookie
* PHP 8+ con MariaDB
*/
class UserManager {
private PDO $db;
private string $cookieName = 'auth_token';
private int $cookieLifetime = 2592000; // 30 giorni in secondi
private int $maxLoginAttempts = 5;
private int $lockoutTime = 900; // 15 minuti in secondi
public function __construct(PDO $db) {
$this->db = $db;
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
/**
* Registra un nuovo utente
*/
public function register(string $username, string $email, string $password, string $fullName = null): bool {
try {
$passwordHash = password_hash($password, PASSWORD_ARGON2ID);
$stmt = $this->db->prepare("
INSERT INTO users (username, email, password_hash, full_name)
VALUES (:username, :email, :password_hash, :full_name)
");
$result = $stmt->execute([
'username' => $username,
'email' => $email,
'password_hash' => $passwordHash,
'full_name' => $fullName
]);
if ($result) {
$userId = $this->db->lastInsertId();
$this->logActivity($userId, $username, 'REGISTER', "Nuovo utente registrato: $username");
}
return $result;
} catch (PDOException $e) {
error_log("Errore registrazione: " . $e->getMessage());
return false;
}
}
/**
* Login utente con username/email e password
*/
public function login(string $identifier, string $password, bool $rememberMe = true): array {
// Verifica blocco per troppi tentativi
if ($this->isAccountLocked($identifier)) {
return [
'success' => false,
'message' => 'Account temporaneamente bloccato per troppi tentativi falliti'
];
}
// Cerca utente per username o email
$stmt = $this->db->prepare("
SELECT id, username, email, password_hash, is_active, full_name
FROM users
WHERE (username = :identifier OR email = :identifier) AND is_active = 1
");
$stmt->execute(['identifier' => $identifier]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user || !password_verify($password, $user['password_hash'])) {
$this->logFailedAttempt($identifier);
$this->logActivity(null, $identifier, 'LOGIN_FAILED', "Tentativo di login fallito");
return [
'success' => false,
'message' => 'Credenziali non valide'
];
}
// Login riuscito - pulisce tentativi falliti
$this->clearFailedAttempts($identifier);
// Aggiorna last_login
$stmt = $this->db->prepare("UPDATE users SET last_login = NOW() WHERE id = :id");
$stmt->execute(['id' => $user['id']]);
// Crea token di autenticazione se richiesto
if ($rememberMe) {
$this->createAuthToken($user['id']);
}
// Salva in sessione dati base
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['full_name'] = $user['full_name'];
$this->logActivity($user['id'], $user['username'], 'LOGIN', "Login effettuato");
return [
'success' => true,
'user' => [
'id' => $user['id'],
'username' => $user['username'],
'email' => $user['email'],
'full_name' => $user['full_name']
]
];
}
/**
* Re-autenticazione con solo password (quando cookie è scaduto ma username è memorizzato)
*/
public function reAuthenticate(string $username, string $password): array {
return $this->login($username, $password, true);
}
/**
* Verifica validità sessione/cookie
*/
public function checkAuth(): ?array {
// Prima controlla se esiste sessione attiva
if (isset($_SESSION['user_id'])) {
return $this->getUserById($_SESSION['user_id']);
}
// Altrimenti verifica cookie
if (isset($_COOKIE[$this->cookieName])) {
return $this->validateAuthToken($_COOKIE[$this->cookieName]);
}
return null;
}
/**
* Crea token di autenticazione e imposta cookie
*/
private function createAuthToken(int $userId): bool {
try {
// Genera selector e token casuali
$selector = bin2hex(random_bytes(16));
$token = bin2hex(random_bytes(32));
$tokenHash = hash('sha256', $token);
$expiresAt = date('Y-m-d H:i:s', time() + $this->cookieLifetime);
// Salva nel database
$stmt = $this->db->prepare("
INSERT INTO auth_tokens (user_id, token, selector, expires_at, ip_address, user_agent)
VALUES (:user_id, :token, :selector, :expires_at, :ip, :ua)
");
$stmt->execute([
'user_id' => $userId,
'token' => $tokenHash,
'selector' => $selector,
'expires_at' => $expiresAt,
'ip' => $_SERVER['REMOTE_ADDR'] ?? null,
'ua' => $_SERVER['HTTP_USER_AGENT'] ?? null
]);
// Imposta cookie (selector:token)
$cookieValue = $selector . ':' . $token;
setcookie(
$this->cookieName,
$cookieValue,
time() + $this->cookieLifetime,
'/',
'',
true, // Secure (solo HTTPS in produzione)
true // HttpOnly
);
return true;
} catch (Exception $e) {
error_log("Errore creazione token: " . $e->getMessage());
return false;
}
}
/**
* Valida token di autenticazione dal cookie
*/
private function validateAuthToken(string $cookieValue): ?array {
try {
$parts = explode(':', $cookieValue);
if (count($parts) !== 2) {
return null;
}
[$selector, $token] = $parts;
$tokenHash = hash('sha256', $token);
// Cerca token nel database
$stmt = $this->db->prepare("
SELECT at.*, u.id, u.username, u.email, u.full_name, u.is_active
FROM auth_tokens at
JOIN users u ON at.user_id = u.id
WHERE at.selector = :selector AND at.expires_at > NOW() AND u.is_active = 1
");
$stmt->execute(['selector' => $selector]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$result || !hash_equals($result['token'], $tokenHash)) {
$this->deleteAuthToken($selector);
return null;
}
// Token valido - rigenera sessione
$_SESSION['user_id'] = $result['id'];
$_SESSION['username'] = $result['username'];
$_SESSION['full_name'] = $result['full_name'];
// Aggiorna last_login
$stmt = $this->db->prepare("UPDATE users SET last_login = NOW() WHERE id = :id");
$stmt->execute(['id' => $result['id']]);
$this->logActivity($result['id'], $result['username'], 'AUTO_LOGIN', "Login automatico via cookie");
return [
'id' => $result['id'],
'username' => $result['username'],
'email' => $result['email'],
'full_name' => $result['full_name']
];
} catch (Exception $e) {
error_log("Errore validazione token: " . $e->getMessage());
return null;
}
}
/**
* Logout - elimina sessione e cookie
*/
public function logout(): void {
$userId = $_SESSION['user_id'] ?? null;
$username = $_SESSION['username'] ?? 'unknown';
// Elimina token dal database se presente
if (isset($_COOKIE[$this->cookieName])) {
$parts = explode(':', $_COOKIE[$this->cookieName]);
if (count($parts) === 2) {
$this->deleteAuthToken($parts[0]);
}
}
// Elimina cookie
setcookie($this->cookieName, '', time() - 3600, '/', '', true, true);
// Distrugge sessione
session_unset();
session_destroy();
if ($userId) {
$this->logActivity($userId, $username, 'LOGOUT', "Logout effettuato");
}
}
/**
* Elimina token dal database
*/
private function deleteAuthToken(string $selector): void {
try {
$stmt = $this->db->prepare("DELETE FROM auth_tokens WHERE selector = :selector");
$stmt->execute(['selector' => $selector]);
} catch (Exception $e) {
error_log("Errore eliminazione token: " . $e->getMessage());
}
}
/**
* Ottiene utente per ID
*/
public function getUserById(int $userId): ?array {
$stmt = $this->db->prepare("
SELECT id, username, email, full_name, is_active, created_at, last_login
FROM users WHERE id = :id AND is_active = 1
");
$stmt->execute(['id' => $userId]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
return $user ?: null;
}
/**
* Verifica se account è bloccato per troppi tentativi
*/
private function isAccountLocked(string $identifier): bool {
$stmt = $this->db->prepare("
SELECT COUNT(*) as attempts
FROM failed_login_attempts
WHERE username = :identifier
AND ip_address = :ip
AND attempted_at > DATE_SUB(NOW(), INTERVAL :lockout SECOND)
");
$stmt->execute([
'identifier' => $identifier,
'ip' => $_SERVER['REMOTE_ADDR'] ?? '',
'lockout' => $this->lockoutTime
]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result['attempts'] >= $this->maxLoginAttempts;
}
/**
* Registra tentativo di login fallito
*/
private function logFailedAttempt(string $identifier): void {
try {
$stmt = $this->db->prepare("
INSERT INTO failed_login_attempts (username, ip_address)
VALUES (:username, :ip)
");
$stmt->execute([
'username' => $identifier,
'ip' => $_SERVER['REMOTE_ADDR'] ?? ''
]);
} catch (Exception $e) {
error_log("Errore log tentativo fallito: " . $e->getMessage());
}
}
/**
* Pulisce tentativi falliti dopo login riuscito
*/
private function clearFailedAttempts(string $identifier): void {
try {
$stmt = $this->db->prepare("
DELETE FROM failed_login_attempts
WHERE username = :username AND ip_address = :ip
");
$stmt->execute([
'username' => $identifier,
'ip' => $_SERVER['REMOTE_ADDR'] ?? ''
]);
} catch (Exception $e) {
error_log("Errore pulizia tentativi: " . $e->getMessage());
}
}
/**
* Log attività utente
*/
public function logActivity(?int $userId, string $username, string $action, string $description = null): void {
try {
$stmt = $this->db->prepare("
INSERT INTO user_activity_log (user_id, username, action, description, ip_address, user_agent)
VALUES (:user_id, :username, :action, :description, :ip, :ua)
");
$stmt->execute([
'user_id' => $userId,
'username' => $username,
'action' => $action,
'description' => $description,
'ip' => $_SERVER['REMOTE_ADDR'] ?? null,
'ua' => $_SERVER['HTTP_USER_AGENT'] ?? null
]);
} catch (Exception $e) {
error_log("Errore log attività: " . $e->getMessage());
}
}
/**
* Pulizia token scaduti (da eseguire periodicamente)
*/
public function cleanExpiredTokens(): int {
try {
$stmt = $this->db->prepare("DELETE FROM auth_tokens WHERE expires_at < NOW()");
$stmt->execute();
return $stmt->rowCount();
} catch (Exception $e) {
error_log("Errore pulizia token: " . $e->getMessage());
return 0;
}
}
/**
* Ottiene username da cookie (per form di re-autenticazione)
*/
public function getRememberedUsername(): ?string {
if (!isset($_COOKIE[$this->cookieName])) {
return null;
}
$parts = explode(':', $_COOKIE[$this->cookieName]);
if (count($parts) !== 2) {
return null;
}
try {
$stmt = $this->db->prepare("
SELECT u.username
FROM auth_tokens at
JOIN users u ON at.user_id = u.id
WHERE at.selector = :selector
");
$stmt->execute(['selector' => $parts[0]]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result['username'] ?? null;
} catch (Exception $e) {
return null;
}
}
}

BIN
config/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -10,6 +10,9 @@ $DB_NAME = $_ENV['DB_DATABASE'];
$DB_USER = $_ENV['DB_USERNAME']; $DB_USER = $_ENV['DB_USERNAME'];
$DB_PASS = $_ENV['DB_PASSWORD']; $DB_PASS = $_ENV['DB_PASSWORD'];
$db = new PDO("mysql:host=$DB_HOST;dbname=$DB_NAME;charset=utf8mb4",$DB_USER,$DB_PASS,[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
// try { // try {
// $pdo = new PDO( // $pdo = new PDO(
// "mysql:host=$DB_HOST;dbname=$DB_NAME;charset=utf8mb4", // "mysql:host=$DB_HOST;dbname=$DB_NAME;charset=utf8mb4",

6
config/init.php Normal file
View File

@@ -0,0 +1,6 @@
<?php
require_once __DIR__ . '/db.php';
require_once __DIR__ . '/../app/UsesrsManager.class.php';
$userManager = new UserManager($pdo);
$currentUser = $userManager->checkAuth();

View File

@@ -1,25 +0,0 @@
<?php
require __DIR__ . '/../vendor/autoload.php';
$dotenv = Dotenv\Dotenv::createImmutable('/var/www/html/confs/dpmy_dev/');
$dotenv->load();
$DB_HOST = $_ENV['DB_HOST'];
$DB_PORT = $_ENV['DB_PORT'];
$DB_NAME = $_ENV['DB_DATABASE'];
$DB_USER = $_ENV['DB_USERNAME'];
$DB_PASS = $_ENV['DB_PASSWORD'];
try {
$pdo = new PDO(
"mysql:host=$DB_HOST;dbname=$DB_NAME;charset=utf8mb4",
$DB_USER,
$DB_PASS,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]
);
echo "Connessione al database riuscita!";
} catch (PDOException $e) {
echo "Errore connessione DB: " . $e->getMessage();
}

View File

@@ -1,10 +1,8 @@
<?php <?php
require_once __DIR__ . '/config/init.php';
include 'parts/head.php'; include 'parts/head.php';
include 'parts/sidebar.php'; include 'parts/sidebar.php';
include 'parts/content.php'; include 'parts/content.php';
include 'parts/footer.php'; include 'parts/footer.php';
?> ?>