181 lines
5.6 KiB
PHP
181 lines
5.6 KiB
PHP
<?php
|
|
|
|
class admin {
|
|
|
|
const int ADMIN_SESSION_TIMEOUT = 86400 * 14;
|
|
const string ADMIN_COOKIE_NAME = 'admin_key';
|
|
const int ADMIN_LOGIN_MAX_LENGTH = 32;
|
|
|
|
// session data
|
|
protected static ?int $id = null;
|
|
protected static ?int $authId = null;
|
|
protected static ?string $csrfSalt = null;
|
|
protected static ?string $login = null;
|
|
|
|
public static function exists(string $login): bool {
|
|
$db = DB();
|
|
return (int)$db->result($db->query("SELECT COUNT(*) FROM admins WHERE login=? LIMIT 1", $login)) > 0;
|
|
}
|
|
|
|
public static function add(string $login, string $password): int {
|
|
$db = DB();
|
|
$db->insert('admins', [
|
|
'login' => $login,
|
|
'password' => saltPassword($password),
|
|
'activity_ts' => 0
|
|
]);
|
|
return $db->insertId();
|
|
}
|
|
|
|
public static function delete(string $login): bool {
|
|
$db = DB();
|
|
$id = self::getIdByLogin($login);
|
|
if (!$db->query("DELETE FROM admins WHERE login=?", $login)) return false;
|
|
if (!$db->query("DELETE FROM admin_auth WHERE admin_id=?", $id)) return false;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @param int[] $ids
|
|
* @return string[]
|
|
*/
|
|
public static function getLoginsById(array $ids): array {
|
|
$db = DB();
|
|
$logins = [];
|
|
$q = $db->query("SELECT id, login FROM admins WHERE id IN (".implode(',', $ids).")");
|
|
while ($row = $db->fetch($q)) {
|
|
$logins[(int)$row['id']] = $row['login'];
|
|
}
|
|
return $logins;
|
|
}
|
|
|
|
protected static function getIdByLogin(string $login): ?int {
|
|
$db = DB();
|
|
$q = $db->query("SELECT id FROM admins WHERE login=?", $login);
|
|
return $db->numRows($q) > 0 ? (int)$db->result($q) : null;
|
|
}
|
|
|
|
public static function setPassword(string $login, string $password): bool {
|
|
$db = DB();
|
|
$db->query("UPDATE admins SET password=? WHERE login=?", saltPassword($password), $login);
|
|
return $db->affectedRows() > 0;
|
|
}
|
|
|
|
public static function auth(string $login, string $password): bool {
|
|
$db = DB();
|
|
$salted_password = saltPassword($password);
|
|
$q = $db->query("SELECT id, active FROM admins WHERE login=? AND password=?", $login, $salted_password);
|
|
if (!$db->numRows($q)) {
|
|
logDebug(__METHOD__.': login or password is invalid');
|
|
return false;
|
|
}
|
|
|
|
$row = $db->fetch($q);
|
|
$id = (int)$row['id'];
|
|
$active = (bool)$row['active'];
|
|
if (!$active)
|
|
return false;
|
|
|
|
$time = time();
|
|
|
|
do {
|
|
$token = strgen(32);
|
|
} while ($db->numRows($db->query("SELECT id FROM admin_auth WHERE token=? LIMIT 1", $token)) > 0);
|
|
|
|
$db->insert('admin_auth', [
|
|
'admin_id' => $id,
|
|
'token' => $token,
|
|
'ts' => $time
|
|
]);
|
|
$auth_id = $db->insertId();
|
|
|
|
$db->insert('admin_log', [
|
|
'admin_id' => $id,
|
|
'ts' => $time,
|
|
'ip' => ip2ulong($_SERVER['REMOTE_ADDR']),
|
|
'ua' => $_SERVER['HTTP_USER_AGENT'] ?? '',
|
|
]);
|
|
|
|
$db->query("UPDATE admins SET activity_ts=? WHERE id=?", $time, $id);
|
|
|
|
self::setSessionData($id, $login, $auth_id, $salted_password);
|
|
self::setCookie($token);
|
|
|
|
return true;
|
|
}
|
|
|
|
public static function logout() {
|
|
if (!isAdmin())
|
|
return;
|
|
|
|
$db = DB();
|
|
$db->query("DELETE FROM admin_auth WHERE id=?", self::$authId);
|
|
|
|
self::unsetSessionData();
|
|
self::unsetCookie();
|
|
}
|
|
|
|
public static function log(\AdminActions\BaseAction $action) {
|
|
\AdminActions\Util\Logger::record($action);
|
|
}
|
|
|
|
public static function check(): void {
|
|
if (!isset($_COOKIE[self::ADMIN_COOKIE_NAME]))
|
|
return;
|
|
|
|
$cookie = (string)$_COOKIE[self::ADMIN_COOKIE_NAME];
|
|
$db = DB();
|
|
$time = time();
|
|
$q = $db->query("SELECT
|
|
admin_auth.id AS auth_id,
|
|
admin_auth.admin_id AS id,
|
|
admins.activity_ts AS activity_ts,
|
|
admins.password AS salted_password,
|
|
admins.login AS login
|
|
FROM admin_auth
|
|
LEFT JOIN admins ON admin_auth.admin_id=admins.id
|
|
WHERE admin_auth.token=?
|
|
LIMIT 1", $cookie);
|
|
|
|
if (!$db->numRows($q)) {
|
|
unset($_COOKIE[self::ADMIN_COOKIE_NAME]);
|
|
return;
|
|
}
|
|
|
|
$info = $db->fetch($q);
|
|
self::setSessionData((int)$info['id'], $info['login'], (int)$info['auth_id'], $info['salted_password']);
|
|
|
|
if ($time - $info['activity_ts'] > 15)
|
|
$db->query("UPDATE admins SET activity_ts=? WHERE id=?", $time, self::$id);
|
|
}
|
|
|
|
protected static function setCookie(string $token): void {
|
|
global $config;
|
|
setcookie(self::ADMIN_COOKIE_NAME, $token, time() + self::ADMIN_SESSION_TIMEOUT, '/', $config['cookie_host']);
|
|
}
|
|
|
|
protected static function unsetCookie(): void {
|
|
global $config;
|
|
setcookie(self::ADMIN_COOKIE_NAME, '', 1, '/', $config['cookie_host']);
|
|
}
|
|
|
|
protected static function setSessionData(int $id, string $login, int $authId, string $saltedPassword) {
|
|
self::$id = $id;
|
|
self::$login = $login;
|
|
self::$authId = $authId;
|
|
self::$csrfSalt = saltPassword(strrev($saltedPassword));
|
|
}
|
|
|
|
protected static function unsetSessionData(): void {
|
|
self::$id = null;
|
|
self::$authId = null;
|
|
self::$csrfSalt = null;
|
|
self::$login = null;
|
|
}
|
|
|
|
public static function getId(): ?int { return self::$id; }
|
|
public static function getCSRFSalt(): ?string { return self::$csrfSalt; }
|
|
public static function getLogin(): ?string { return self::$login; }
|
|
|
|
}
|