togethere.cloud/private_html/includes/session_bootstrap.php

310 lines
8.3 KiB
PHP

<?php
declare(strict_types=1);
if (defined('OG_SESSION_BOOTSTRAP_LOADED')) {
return;
}
define('OG_SESSION_BOOTSTRAP_LOADED', true);
define('OG_SESSION_TIMEOUT_DEFAULT', 24 * 60 * 60);
define('OG_SESSION_TIMEOUT_REMEMBER', 7 * 24 * 60 * 60);
function og_session_is_secure_request(): bool
{
if (!empty($_SERVER['HTTPS']) && strtolower((string) $_SERVER['HTTPS']) !== 'off') {
return true;
}
if (isset($_SERVER['SERVER_PORT']) && (int) $_SERVER['SERVER_PORT'] === 443) {
return true;
}
return false;
}
function og_session_cookie_options(int $lifetime): array
{
return [
'lifetime' => $lifetime,
'path' => '/',
'secure' => og_session_is_secure_request(),
'httponly' => true,
'samesite' => 'Lax',
];
}
function og_session_configure(int $timeout): void
{
if (session_status() === PHP_SESSION_ACTIVE) {
return;
}
ini_set('session.gc_maxlifetime', (string) $timeout);
ini_set('session.cookie_httponly', '1');
ini_set('session.use_strict_mode', '1');
if (PHP_VERSION_ID >= 70300) {
session_set_cookie_params(og_session_cookie_options($timeout));
return;
}
$path = '/; samesite=Lax';
session_set_cookie_params($timeout, $path, '', og_session_is_secure_request(), true);
}
function og_session_get_pdo(): ?PDO
{
static $pdo = null;
if ($pdo instanceof PDO) {
return $pdo;
}
try {
$pdo = new PDO(
'mysql:host=localhost;dbname=togethere_cloud;charset=utf8mb4',
'root',
'HasloDoSQL',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]
);
$pdo->exec('SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci');
return $pdo;
} catch (Throwable $e) {
return null;
}
}
function og_session_ensure_remember_tokens_table(?PDO $pdo = null): bool
{
$pdo = $pdo ?: og_session_get_pdo();
if (!$pdo instanceof PDO) {
return false;
}
$pdo->exec(
'CREATE TABLE IF NOT EXISTS remember_tokens (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
token VARCHAR(255) NOT NULL,
expires_at DATETIME NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uniq_remember_token (token),
KEY idx_remember_user (user_id),
KEY idx_remember_expires (expires_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci'
);
return true;
}
function og_session_remember_cookie_value(): string
{
return isset($_COOKIE['remember_token']) ? trim((string) $_COOKIE['remember_token']) : '';
}
function og_session_uses_remember_me(): bool
{
if (!empty($_SESSION['remember_me'])) {
return true;
}
return og_session_remember_cookie_value() !== '';
}
function og_session_timeout_seconds(): int
{
return og_session_uses_remember_me() ? OG_SESSION_TIMEOUT_REMEMBER : OG_SESSION_TIMEOUT_DEFAULT;
}
function og_session_refresh_cookie(string $name, string $value, int $lifetime): void
{
if (headers_sent()) {
return;
}
$expiresAt = time() + $lifetime;
if (PHP_VERSION_ID >= 70300) {
$options = og_session_cookie_options($lifetime);
$options['expires'] = $expiresAt;
setcookie($name, $value, $options);
return;
}
setcookie($name, $value, $expiresAt, '/; samesite=Lax', '', og_session_is_secure_request(), true);
}
function og_session_clear_cookie(string $name): void
{
if (headers_sent()) {
return;
}
if (PHP_VERSION_ID >= 70300) {
$options = og_session_cookie_options(0);
$options['expires'] = time() - 3600;
setcookie($name, '', $options);
return;
}
setcookie($name, '', time() - 3600, '/; samesite=Lax', '', og_session_is_secure_request(), true);
}
function og_session_refresh_remember_token(): void
{
if (empty($_SESSION['remember_me'])) {
return;
}
$token = og_session_remember_cookie_value();
if ($token === '') {
return;
}
$pdo = og_session_get_pdo();
if (!$pdo instanceof PDO || !og_session_ensure_remember_tokens_table($pdo)) {
return;
}
$expiresAt = date('Y-m-d H:i:s', time() + OG_SESSION_TIMEOUT_REMEMBER);
$stmt = $pdo->prepare('UPDATE remember_tokens SET expires_at = :expires WHERE token = :token');
$stmt->execute([
':expires' => $expiresAt,
':token' => hash('sha256', $token),
]);
og_session_refresh_cookie('remember_token', $token, OG_SESSION_TIMEOUT_REMEMBER);
}
function og_session_clear_remember_token(): void
{
$token = og_session_remember_cookie_value();
if ($token !== '') {
$pdo = og_session_get_pdo();
if ($pdo instanceof PDO && og_session_ensure_remember_tokens_table($pdo)) {
$stmt = $pdo->prepare('DELETE FROM remember_tokens WHERE token = :token');
$stmt->execute([':token' => hash('sha256', $token)]);
}
}
og_session_clear_cookie('remember_token');
}
function og_session_destroy_auth(bool $clearRememberToken = false): void
{
$_SESSION = [];
if ($clearRememberToken) {
og_session_clear_remember_token();
}
if (session_status() === PHP_SESSION_ACTIVE) {
if (!headers_sent()) {
og_session_clear_cookie(session_name());
}
session_destroy();
}
}
function og_session_find_remember_user(PDO $pdo, string $token): ?array
{
if ($token === '') {
return null;
}
if (!og_session_ensure_remember_tokens_table($pdo)) {
return null;
}
$stmt = $pdo->prepare(
'SELECT u.id, u.username, u.email, COALESCE(u.role, "user") AS role
FROM remember_tokens rt
INNER JOIN users u ON u.id = rt.user_id
WHERE rt.token = :token
AND rt.expires_at > NOW()
LIMIT 1'
);
$stmt->execute([':token' => hash('sha256', $token)]);
$row = $stmt->fetch();
return is_array($row) ? $row : null;
}
function og_session_restore_from_remember_cookie(): void
{
if (!empty($_SESSION['logged_in']) && !empty($_SESSION['user_id'])) {
return;
}
$token = og_session_remember_cookie_value();
if ($token === '') {
return;
}
$pdo = og_session_get_pdo();
if (!$pdo instanceof PDO) {
return;
}
$user = og_session_find_remember_user($pdo, $token);
if (!$user) {
og_session_clear_remember_token();
return;
}
session_regenerate_id(true);
$_SESSION['logged_in'] = true;
$_SESSION['user_id'] = (int) $user['id'];
$_SESSION['username'] = (string) $user['username'];
$_SESSION['email'] = (string) ($user['email'] ?? '');
$_SESSION['role'] = (string) ($user['role'] ?? 'user');
$_SESSION['remember_me'] = true;
$_SESSION['last_activity'] = time();
og_session_refresh_remember_token();
}
function og_session_enforce_inactivity_timeout(): void
{
if (empty($_SESSION['logged_in']) || empty($_SESSION['user_id'])) {
return;
}
$lastActivity = isset($_SESSION['last_activity']) ? (int) $_SESSION['last_activity'] : 0;
if ($lastActivity <= 0) {
return;
}
if ((time() - $lastActivity) > og_session_timeout_seconds()) {
og_session_destroy_auth(og_session_uses_remember_me());
}
}
function og_session_touch(): void
{
if (empty($_SESSION['logged_in']) || empty($_SESSION['user_id'])) {
return;
}
$_SESSION['last_activity'] = time();
og_session_refresh_cookie(session_name(), session_id(), og_session_timeout_seconds());
og_session_refresh_remember_token();
}
$preSessionTimeout = og_session_remember_cookie_value() !== ''
? OG_SESSION_TIMEOUT_REMEMBER
: OG_SESSION_TIMEOUT_DEFAULT;
og_session_configure($preSessionTimeout);
if (session_status() !== PHP_SESSION_ACTIVE) {
session_start();
}
og_session_restore_from_remember_cookie();
og_session_enforce_inactivity_timeout();
og_session_touch();