306 lines
7.3 KiB
PHP
306 lines
7.3 KiB
PHP
<?php
|
|
/**
|
|
* Authentication Helper Functions
|
|
* Telvero Talpa Planning System
|
|
*/
|
|
|
|
if (session_status() === PHP_SESSION_NONE) {
|
|
// Secure session settings
|
|
ini_set('session.cookie_httponly', 1);
|
|
ini_set('session.use_strict_mode', 1);
|
|
ini_set('session.cookie_samesite', 'Lax');
|
|
session_start();
|
|
}
|
|
|
|
/**
|
|
* Get database connection for auth
|
|
*/
|
|
function getAuthDb(): PDO {
|
|
static $db = null;
|
|
if ($db === null) {
|
|
// Load env if not already loaded
|
|
if (!isset($_ENV['DB_HOST'])) {
|
|
require_once __DIR__ . '/../vendor/autoload.php';
|
|
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/..');
|
|
$dotenv->load();
|
|
}
|
|
$db = new PDO(
|
|
"mysql:host={$_ENV['DB_HOST']};dbname={$_ENV['DB_NAME']};charset=utf8mb4",
|
|
$_ENV['DB_USER'],
|
|
$_ENV['DB_PASS'],
|
|
[
|
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
]
|
|
);
|
|
}
|
|
return $db;
|
|
}
|
|
|
|
/**
|
|
* Check if user is logged in
|
|
*/
|
|
function isLoggedIn(): bool {
|
|
return isset($_SESSION['user_id']) && isset($_SESSION['user']) && !empty($_SESSION['user']);
|
|
}
|
|
|
|
/**
|
|
* Get current logged in user
|
|
*/
|
|
function getCurrentUser(): ?array {
|
|
return $_SESSION['user'] ?? null;
|
|
}
|
|
|
|
/**
|
|
* Check if user has specific role
|
|
*/
|
|
function hasRole(string $role): bool {
|
|
if (!isLoggedIn()) {
|
|
return false;
|
|
}
|
|
return ($_SESSION['user']['role'] ?? '') === $role;
|
|
}
|
|
|
|
/**
|
|
* Check if user is admin
|
|
*/
|
|
function isAdmin(): bool {
|
|
return hasRole('admin');
|
|
}
|
|
|
|
/**
|
|
* Check if user is guest
|
|
*/
|
|
function isGuest(): bool {
|
|
return hasRole('guest');
|
|
}
|
|
|
|
/**
|
|
* Check if user can perform write operations
|
|
*/
|
|
function canWrite(): bool {
|
|
return isLoggedIn() && isAdmin();
|
|
}
|
|
|
|
/**
|
|
* Check if user can create
|
|
*/
|
|
function canCreate(): bool {
|
|
return isAdmin();
|
|
}
|
|
|
|
/**
|
|
* Check if user can edit
|
|
*/
|
|
function canEdit(): bool {
|
|
return isAdmin();
|
|
}
|
|
|
|
/**
|
|
* Check if user can delete
|
|
*/
|
|
function canDelete(): bool {
|
|
return isAdmin();
|
|
}
|
|
|
|
/**
|
|
* Check if user can sync with Talpa
|
|
*/
|
|
function canSync(): bool {
|
|
return isAdmin();
|
|
}
|
|
|
|
/**
|
|
* Require user to be logged in, redirect to login if not
|
|
*/
|
|
function requireLogin(): void {
|
|
if (!isLoggedIn()) {
|
|
$currentUrl = $_SERVER['REQUEST_URI'] ?? '/';
|
|
$redirect = urlencode(ltrim($currentUrl, '/'));
|
|
header("Location: /auth/login.php?redirect=$redirect");
|
|
exit;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Require user to have specific role
|
|
*/
|
|
function requireRole(string $role): void {
|
|
requireLogin();
|
|
|
|
if (!hasRole($role)) {
|
|
http_response_code(403);
|
|
include __DIR__ . '/403.php';
|
|
exit;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Require admin role
|
|
*/
|
|
function requireAdmin(): void {
|
|
requireRole('admin');
|
|
}
|
|
|
|
/**
|
|
* Check brute force protection
|
|
* Returns true if user is blocked
|
|
*/
|
|
function isBlockedByBruteForce(string $username, string $ip): bool {
|
|
try {
|
|
$db = getAuthDb();
|
|
|
|
// Check by username
|
|
$stmt = $db->prepare("
|
|
SELECT COUNT(*) FROM login_attempts
|
|
WHERE username = ? AND success = 0
|
|
AND attempted_at > DATE_SUB(NOW(), INTERVAL 15 MINUTE)
|
|
");
|
|
$stmt->execute([$username]);
|
|
if ($stmt->fetchColumn() >= 5) {
|
|
return true;
|
|
}
|
|
|
|
// Check by IP
|
|
$stmt = $db->prepare("
|
|
SELECT COUNT(*) FROM login_attempts
|
|
WHERE ip_address = ? AND success = 0
|
|
AND attempted_at > DATE_SUB(NOW(), INTERVAL 15 MINUTE)
|
|
");
|
|
$stmt->execute([$ip]);
|
|
if ($stmt->fetchColumn() >= 10) {
|
|
return true;
|
|
}
|
|
} catch (Exception $e) {
|
|
// If we can't check, allow the attempt
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Log a login attempt
|
|
*/
|
|
function logLoginAttempt(string $username, string $ip, bool $success): void {
|
|
try {
|
|
$db = getAuthDb();
|
|
$stmt = $db->prepare("
|
|
INSERT INTO login_attempts (username, ip_address, success)
|
|
VALUES (?, ?, ?)
|
|
");
|
|
$stmt->execute([$username, $ip, $success ? 1 : 0]);
|
|
} catch (Exception $e) {
|
|
// Silently fail
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attempt to login a user
|
|
* Returns array with 'success', 'user', 'error' keys
|
|
*/
|
|
function attemptLogin(string $usernameOrEmail, string $password): array {
|
|
$ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
|
|
|
|
// Check brute force
|
|
if (isBlockedByBruteForce($usernameOrEmail, $ip)) {
|
|
return [
|
|
'success' => false,
|
|
'error' => 'Te veel mislukte inlogpogingen. Probeer het over 15 minuten opnieuw.'
|
|
];
|
|
}
|
|
|
|
try {
|
|
$db = getAuthDb();
|
|
|
|
// Find user by username or email
|
|
$stmt = $db->prepare("
|
|
SELECT id, username, email, password_hash, role, is_active
|
|
FROM users
|
|
WHERE (username = ? OR email = ?)
|
|
LIMIT 1
|
|
");
|
|
$stmt->execute([$usernameOrEmail, $usernameOrEmail]);
|
|
$user = $stmt->fetch();
|
|
|
|
if (!$user) {
|
|
logLoginAttempt($usernameOrEmail, $ip, false);
|
|
return ['success' => false, 'error' => 'Ongeldige gebruikersnaam of wachtwoord.'];
|
|
}
|
|
|
|
if (!$user['is_active']) {
|
|
logLoginAttempt($usernameOrEmail, $ip, false);
|
|
return ['success' => false, 'error' => 'Dit account is gedeactiveerd. Neem contact op met de beheerder.'];
|
|
}
|
|
|
|
if (!password_verify($password, $user['password_hash'])) {
|
|
logLoginAttempt($usernameOrEmail, $ip, false);
|
|
return ['success' => false, 'error' => 'Ongeldige gebruikersnaam of wachtwoord.'];
|
|
}
|
|
|
|
// Success - create session
|
|
session_regenerate_id(true);
|
|
$_SESSION['user_id'] = $user['id'];
|
|
$_SESSION['user'] = [
|
|
'id' => $user['id'],
|
|
'username' => $user['username'],
|
|
'email' => $user['email'],
|
|
'role' => $user['role'],
|
|
];
|
|
$_SESSION['login_time'] = time();
|
|
|
|
// Log success
|
|
logLoginAttempt($usernameOrEmail, $ip, true);
|
|
|
|
// Update last login
|
|
$stmt = $db->prepare("UPDATE users SET last_login = NOW() WHERE id = ?");
|
|
$stmt->execute([$user['id']]);
|
|
|
|
return ['success' => true, 'user' => $_SESSION['user']];
|
|
|
|
} catch (Exception $e) {
|
|
return ['success' => false, 'error' => 'Er is een fout opgetreden. Probeer het opnieuw.'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Logout the current user
|
|
*/
|
|
function logout(): void {
|
|
$_SESSION = [];
|
|
|
|
if (ini_get('session.use_cookies')) {
|
|
$params = session_get_cookie_params();
|
|
setcookie(
|
|
session_name(),
|
|
'',
|
|
time() - 42000,
|
|
$params['path'],
|
|
$params['domain'],
|
|
$params['secure'],
|
|
$params['httponly']
|
|
);
|
|
}
|
|
|
|
session_destroy();
|
|
}
|
|
|
|
/**
|
|
* Check session timeout (2 hours of inactivity)
|
|
*/
|
|
function checkSessionTimeout(): void {
|
|
$timeout = 7200; // 2 hours
|
|
|
|
if (isLoggedIn()) {
|
|
if (isset($_SESSION['login_time']) && (time() - $_SESSION['login_time']) > $timeout) {
|
|
logout();
|
|
header("Location: /auth/login.php?reason=timeout");
|
|
exit;
|
|
}
|
|
// Update last activity
|
|
$_SESSION['login_time'] = time();
|
|
}
|
|
}
|
|
|
|
// Check session timeout on every request
|
|
checkSessionTimeout();
|