misuzu/src/Users/session.php

212 lines
6.1 KiB
PHP
Raw Normal View History

2018-05-27 02:20:35 +02:00
<?php
define('MSZ_SESSION_KEY_SIZE', 64);
function user_session_create(
int $userId,
string $ipAddress,
string $userAgent
): string {
$sessionKey = user_session_generate_key();
2019-09-29 00:38:39 +02:00
$createSession = \Misuzu\DB::prepare('
2018-05-27 02:20:35 +02:00
INSERT INTO `msz_sessions`
(
`user_id`, `session_ip`, `session_country`,
`session_user_agent`, `session_key`, `session_created`, `session_expires`
2018-05-27 02:20:35 +02:00
)
VALUES
(
:user_id, INET6_ATON(:session_ip), :session_country,
:session_user_agent, :session_key, NOW(), NOW() + INTERVAL 1 MONTH
2018-05-27 02:20:35 +02:00
)
');
2019-09-29 00:38:39 +02:00
$createSession->bind('user_id', $userId);
$createSession->bind('session_ip', $ipAddress);
$createSession->bind('session_country', ip_country_code($ipAddress));
$createSession->bind('session_user_agent', $userAgent);
$createSession->bind('session_key', $sessionKey);
2018-05-27 02:20:35 +02:00
return $createSession->execute() ? $sessionKey : '';
}
2019-06-10 19:04:53 +02:00
function user_session_find($sessionId, bool $byKey = false): array {
if(!$byKey && $sessionId < 1) {
2018-09-28 00:03:43 +02:00
return [];
}
2019-09-29 00:38:39 +02:00
$findSession = \Misuzu\DB::prepare(sprintf('
2018-09-28 00:03:43 +02:00
SELECT
`session_id`, `user_id`,
INET6_NTOA(`session_ip`) as `session_ip`,
INET6_NTOA(`session_ip_last`) as `session_ip_last`,
`session_country`, `session_user_agent`, `session_key`, `session_created`,
`session_expires`, `session_active`, `session_expires_bump`
2018-09-28 00:03:43 +02:00
FROM `msz_sessions`
WHERE `%s` = :session_id
', $byKey ? 'session_key' : 'session_id'));
2019-09-29 00:38:39 +02:00
$findSession->bind('session_id', $sessionId);
return $findSession->fetch();
2018-09-28 00:03:43 +02:00
}
2019-06-10 19:04:53 +02:00
function user_session_delete(int $sessionId): void {
2019-09-29 00:38:39 +02:00
$deleteSession = \Misuzu\DB::prepare('
2018-05-27 02:20:35 +02:00
DELETE FROM `msz_sessions`
WHERE `session_id` = :session_id
');
2019-09-29 00:38:39 +02:00
$deleteSession->bind('session_id', $sessionId);
$deleteSession->execute();
2018-05-27 02:20:35 +02:00
}
2019-06-10 19:04:53 +02:00
function user_session_generate_key(): string {
2018-05-27 02:20:35 +02:00
return bin2hex(random_bytes(MSZ_SESSION_KEY_SIZE / 2));
}
2018-09-28 00:03:43 +02:00
2019-06-10 19:04:53 +02:00
function user_session_purge_all(int $userId): void {
2019-09-29 00:38:39 +02:00
\Misuzu\DB::prepare('
2018-09-28 00:03:43 +02:00
DELETE FROM `msz_sessions`
WHERE `user_id` = :user_id
')->execute([
'user_id' => $userId,
]);
}
2019-06-10 19:04:53 +02:00
function user_session_count($userId = 0): int {
2019-09-29 00:38:39 +02:00
$getCount = \Misuzu\DB::prepare(sprintf('
2018-10-29 20:12:06 +01:00
SELECT COUNT(`session_id`)
FROM `msz_sessions`
2019-05-07 10:08:27 +02:00
%s
', $userId < 1 ? '' : 'WHERE `user_id` = :user_id'));
2018-10-29 20:12:06 +01:00
2019-06-10 19:04:53 +02:00
if($userId >= 1) {
2019-09-29 00:38:39 +02:00
$getCount->bind('user_id', $userId);
2018-10-29 20:12:06 +01:00
}
2019-09-29 00:38:39 +02:00
return (int)$getCount->fetchColumn();
2018-10-29 20:12:06 +01:00
}
2019-06-10 19:04:53 +02:00
function user_session_list(int $offset, int $take, int $userId = 0): array {
2018-10-29 20:12:06 +01:00
$offset = max(0, $offset);
$take = max(1, $take);
2019-09-29 00:38:39 +02:00
$getSessions = \Misuzu\DB::prepare(sprintf('
2018-10-29 20:12:06 +01:00
SELECT
`session_id`, `session_country`, `session_user_agent`, `session_created`,
`session_expires`, `session_active`, `session_expires_bump`,
INET6_NTOA(`session_ip`) as `session_ip`,
INET6_NTOA(`session_ip_last`) as `session_ip_last`
2018-10-29 20:12:06 +01:00
FROM `msz_sessions`
WHERE %s
ORDER BY `session_id` DESC
LIMIT :offset, :take
', $userId < 1 ? '1' : '`user_id` = :user_id'));
2019-06-10 19:04:53 +02:00
if($userId > 0) {
2019-09-29 00:38:39 +02:00
$getSessions->bind('user_id', $userId);
2018-10-29 20:12:06 +01:00
}
2019-09-29 00:38:39 +02:00
$getSessions->bind('offset', $offset);
$getSessions->bind('take', $take);
2018-10-29 20:12:06 +01:00
2019-09-29 00:38:39 +02:00
return $getSessions->fetchAll();
2018-10-29 20:12:06 +01:00
}
2019-06-10 19:04:53 +02:00
function user_session_bump_active(int $sessionId, string $ipAddress = null): void {
if($sessionId < 1) {
2018-10-29 22:23:53 +01:00
return;
}
2019-09-29 00:38:39 +02:00
$bump = \Misuzu\DB::prepare('
2018-10-29 22:23:53 +01:00
UPDATE `msz_sessions`
SET `session_active` = NOW(),
`session_ip_last` = INET6_ATON(:last_ip),
`session_expires` = IF(`session_expires_bump`, NOW() + INTERVAL 1 MONTH, `session_expires`)
2018-10-29 22:23:53 +01:00
WHERE `session_id` = :session_id
');
2019-09-29 00:38:39 +02:00
$bump->bind('session_id', $sessionId);
$bump->bind('last_ip', $ipAddress ?? ip_remote_address());
2018-10-29 22:23:53 +01:00
$bump->execute();
}
// the functions below this line are imperative
2019-06-10 19:04:53 +02:00
function user_session_data(?array $newData = null): array {
2019-05-07 10:08:27 +02:00
static $data = [];
if(!is_null($newData)) {
$data = $newData;
}
return $data;
}
2019-06-10 19:04:53 +02:00
function user_session_start(int $userId, string $sessionKey): bool {
$session = user_session_find($sessionKey, true);
2019-06-10 19:04:53 +02:00
if(!$session || $session['user_id'] !== $userId) {
return false;
}
2019-06-10 19:04:53 +02:00
if(time() >= strtotime($session['session_expires'])) {
user_session_delete($session['session_id']);
return false;
}
2019-05-07 10:08:27 +02:00
user_session_data($session);
return true;
}
2019-06-10 19:04:53 +02:00
function user_session_stop(bool $delete = false): void {
if(empty(user_session_data())) {
return;
}
2019-06-10 19:04:53 +02:00
if($delete) {
2019-05-07 10:08:27 +02:00
user_session_delete(user_session_data()['session_id']);
}
2019-05-07 10:08:27 +02:00
user_session_data([]);
}
2019-06-10 19:04:53 +02:00
function user_session_current(?string $variable = null, $default = null) {
if(empty($variable)) {
2019-05-07 10:08:27 +02:00
return user_session_data() ?? [];
}
2019-05-07 10:08:27 +02:00
return user_session_data()[$variable] ?? $default;
}
2019-06-10 19:04:53 +02:00
function user_session_active(): bool {
2019-05-07 10:08:27 +02:00
return !empty(user_session_data())
&& time() < strtotime(user_session_data()['session_expires']);
}
2019-02-12 16:26:39 +01:00
define('MSZ_SESSION_COOKIE_VERSION', 1);
// make sure to match this to the final fixed size of the cookie string
// it'll pad older tokens out for backwards compatibility
define('MSZ_SESSION_COOKIE_SIZE', 37);
2019-06-10 19:04:53 +02:00
function user_session_cookie_pack(int $userId, string $sessionToken): ?string {
if(strlen($sessionToken) !== MSZ_SESSION_KEY_SIZE) {
2019-02-12 16:26:39 +01:00
return null;
}
return pack('CNH64', MSZ_SESSION_COOKIE_VERSION, $userId, $sessionToken);
}
2019-06-10 19:04:53 +02:00
function user_session_cookie_unpack(string $packed): array {
2019-02-12 16:26:39 +01:00
$packed = str_pad($packed, MSZ_SESSION_COOKIE_SIZE, "\x00");
$unpacked = unpack('Cversion/Nuser/H64token', $packed);
2019-06-10 19:04:53 +02:00
if($unpacked['version'] < 1 || $unpacked['version'] > MSZ_SESSION_COOKIE_VERSION) {
2019-02-12 16:26:39 +01:00
return [];
}
// Make sure this contains all fields with a default for version > 1 exclusive stuff
$data = [
'user_id' => $unpacked['user'],
'session_token' => $unpacked['token'],
];
return $data;
}