125 lines
4.1 KiB
PHP
125 lines
4.1 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* Configure session for iframed environments (like Flatlogic preview)
|
|
*/
|
|
function start_secure_session() {
|
|
if (session_status() === PHP_SESSION_NONE) {
|
|
$secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
|
|
|| ($_SERVER['SERVER_PORT'] == 443)
|
|
|| (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https');
|
|
|
|
// Use SameSite=None only if secure, otherwise use Lax
|
|
$samesite = $secure ? 'None' : 'Lax';
|
|
|
|
session_set_cookie_params([
|
|
'lifetime' => 0,
|
|
'path' => '/',
|
|
'domain' => '',
|
|
'secure' => $secure,
|
|
'httponly' => true,
|
|
'samesite' => $samesite
|
|
]);
|
|
|
|
if (!session_start()) {
|
|
error_log("Failed to start session");
|
|
}
|
|
}
|
|
}
|
|
|
|
start_secure_session();
|
|
|
|
/**
|
|
* Authentication Helper
|
|
*/
|
|
class Auth {
|
|
public static function isLoggedIn(): bool {
|
|
return isset($_SESSION['user_id']) && !empty($_SESSION['user_id']);
|
|
}
|
|
|
|
public static function requireLogin(): void {
|
|
if (!self::isLoggedIn()) {
|
|
header('Location: login.php');
|
|
exit;
|
|
}
|
|
}
|
|
|
|
public static function login(int $userId, int $tenantId, string $role): void {
|
|
if (session_status() === PHP_SESSION_NONE) {
|
|
start_secure_session();
|
|
}
|
|
|
|
$_SESSION['user_id'] = $userId;
|
|
$_SESSION['tenant_id'] = $tenantId;
|
|
$_SESSION['role'] = $role;
|
|
|
|
// Important: Save session before geolocation which might be slow
|
|
session_write_close();
|
|
|
|
// Tracking
|
|
$ip = self::getIpAddress();
|
|
$country = self::getCountryFromIp($ip);
|
|
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? null;
|
|
|
|
try {
|
|
// Re-open to update tracking info in DB if we want,
|
|
// but we can just use a fresh DB connection
|
|
$stmt = db()->prepare("INSERT INTO user_sessions (user_id, ip_address, country, user_agent) VALUES (?, ?, ?, ?)");
|
|
$stmt->execute([$userId, $ip, $country, $userAgent]);
|
|
|
|
$stmt = db()->prepare("UPDATE users SET last_login_at = NOW(), last_login_ip = ? WHERE id = ?");
|
|
$stmt->execute([$ip, $userId]);
|
|
} catch (\Throwable $e) {
|
|
error_log("Auth::login tracking error: " . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
public static function logout(): void {
|
|
if (session_status() === PHP_SESSION_NONE) {
|
|
start_secure_session();
|
|
}
|
|
$_SESSION = [];
|
|
$params = session_get_cookie_params();
|
|
setcookie(session_name(), '', time() - 42000,
|
|
$params["path"], $params["domain"],
|
|
$params["secure"], $params["httponly"]
|
|
);
|
|
session_destroy();
|
|
header('Location: login.php', true, 302);
|
|
exit;
|
|
}
|
|
|
|
public static function getIpAddress(): string {
|
|
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
|
|
return $_SERVER['HTTP_CLIENT_IP'];
|
|
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
|
return explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0];
|
|
} else {
|
|
return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
|
|
}
|
|
}
|
|
|
|
public static function getCountryFromIp(string $ip): ?string {
|
|
if ($ip === '127.0.0.1' || $ip === '::1') return 'Localhost';
|
|
|
|
try {
|
|
$ctx = stream_context_create(['http' => ['timeout' => 2]]);
|
|
$resp = @file_get_contents("http://ip-api.com/json/{$ip}?fields=country", false, $ctx);
|
|
if ($resp) {
|
|
$data = json_decode($resp, true);
|
|
return $data['country'] ?? 'Unknown';
|
|
}
|
|
} catch (\Throwable $e) {
|
|
}
|
|
return 'Unknown';
|
|
}
|
|
|
|
public static function recordResetAttempt(string $email, string $ip): void {
|
|
try {
|
|
$stmt = db()->prepare("INSERT INTO activity_log (tenant_id, action, details) VALUES (?, ?, ?)");
|
|
$stmt->execute([0, 'Password Reset Attempt', "Email: $email, IP: $ip"]);
|
|
} catch (\Throwable $e) {}
|
|
}
|
|
}
|