310 lines
8.3 KiB
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(); |