* slightly modernize static class
This commit is contained in:
Sarjuuk
2025-07-26 23:18:59 +02:00
parent 0562989196
commit 0928b1b430
2 changed files with 141 additions and 113 deletions

View File

@@ -47,11 +47,11 @@ class User
return false; return false;
// check IP bans // check IP bans
if ($ipBan = DB::Aowow()->selectRow('SELECT `count`, `unbanDate` FROM ?_account_bannedips WHERE `ip` = ? AND `type` = 0', self::$ip)) if ($ipBan = DB::Aowow()->selectRow('SELECT `count`, IF(`unbanDate` > UNIX_TIMESTAMP(), 1, 0) AS "active" FROM ?_account_bannedips WHERE `ip` = ? AND `type` = 0', self::$ip))
{ {
if ($ipBan['count'] > Cfg::get('ACC_FAILED_AUTH_COUNT') && $ipBan['unbanDate'] > time()) if ($ipBan['count'] > Cfg::get('ACC_FAILED_AUTH_COUNT') && $ipBan['active'])
return false; return false;
else if ($ipBan['unbanDate'] <= time()) else if (!$ipBan['active'])
DB::Aowow()->query('DELETE FROM ?_account_bannedips WHERE `ip` = ?', self::$ip); DB::Aowow()->query('DELETE FROM ?_account_bannedips WHERE `ip` = ?', self::$ip);
} }
@@ -97,26 +97,23 @@ class User
self::$dailyVotes = $uData['dailyVotes']; self::$dailyVotes = $uData['dailyVotes'];
self::$excludeGroups = $uData['excludeGroups']; self::$excludeGroups = $uData['excludeGroups'];
$conditions = array( $conditions = [['OR', ['user', self::$id], ['ap.accountId', self::$id]]];
[['cuFlags', PROFILER_CU_DELETED, '&'], 0], if (!self::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
['OR', ['user', self::$id], ['ap.accountId', self::$id]] $conditions[] = [['cuFlags', PROFILER_CU_DELETED, '&'], 0];
);
if (self::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
array_shift($conditions);
self::$profiles = (new LocalProfileList($conditions)); self::$profiles = (new LocalProfileList($conditions));
if ($uData['avatar']) if ($uData['avatar'])
self::$avatar = $uData['avatar']; self::$avatar = $uData['avatar'];
// stuff, that updates on a daily basis goes here (if you keep you session alive indefinitly, the signin-handler doesn't do very much) // stuff, that updates on a daily basis goes here (if you keep you session alive indefinitly, the signin-handler doesn't do very much)
// - conscutive visits // - conscutive visits
// - votes per day // - votes per day
// - reputation for daily visit // - reputation for daily visit
if (self::isLoggedIn()) if (self::isLoggedIn())
{ {
$lastLogin = DB::Aowow()->selectCell('SELECT curLogin FROM ?_account WHERE id = ?d', self::$id); $lastLogin = DB::Aowow()->selectCell('SELECT `curLogin` FROM ?_account WHERE `id` = ?d', self::$id);
// either the day changed or the last visit was >24h ago // either the day changed or the last visit was >24h ago
if (date('j', $lastLogin) != date('j') || (time() - $lastLogin) > 1 * DAY) if (date('j', $lastLogin) != date('j') || (time() - $lastLogin) > 1 * DAY)
{ {
@@ -204,116 +201,124 @@ class User
/* auth mechanisms */ /* auth mechanisms */
/*******************/ /*******************/
public static function Auth($name, $pass) public static function authenticate(string $name, string $password) : int
{ {
$user = 0; $userId = 0;
$hash = ''; $hash = '';
switch (Cfg::get('ACC_AUTH_MODE')) $result = match (Cfg::get('ACC_AUTH_MODE'))
{ {
case AUTH_MODE_SELF: AUTH_MODE_SELF => self::authSelf($name, $password, $userId, $hash),
{ AUTH_MODE_REALM => self::authRealm($name, $password, $userId, $hash),
if (!self::$ip) AUTH_MODE_EXTERNAL => self::authExtern($name, $password, $userId, $hash),
return AUTH_INTERNAL_ERR; default => AUTH_INTERNAL_ERR
};
// handle login try limitation if ($result == AUTH_OK)
$ip = DB::Aowow()->selectRow('SELECT `ip`, `count`, `unbanDate` FROM ?_account_bannedips WHERE `type` = 0 AND `ip` = ?', self::$ip); {
if (!$ip || $ip['unbanDate'] < time()) // no entry exists or time expired; set count to 1 session_unset();
DB::Aowow()->query('REPLACE INTO ?_account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (?, 0, 1, UNIX_TIMESTAMP() + ?d)', self::$ip, Cfg::get('ACC_FAILED_AUTH_BLOCK')); $_SESSION['user'] = $userId;
else // entry already exists; increment count $_SESSION['hash'] = self::hashCrypt($hash);
DB::Aowow()->query('UPDATE ?_account_bannedips SET `count` = `count` + 1, `unbanDate` = UNIX_TIMESTAMP() + ?d WHERE `ip` = ?', Cfg::get('ACC_FAILED_AUTH_BLOCK'), self::$ip); }
if ($ip && $ip['count'] >= Cfg::get('ACC_FAILED_AUTH_COUNT') && $ip['unbanDate'] >= time()) return $result;
return AUTH_IPBANNED; }
$query = DB::Aowow()->SelectRow( private static function authSelf(string $name, string $password, int &$userId, string &$hash) : int
'SELECT a.`id`, a.`passHash`, BIT_OR(ab.`typeMask`) AS "bans", a.`status` {
FROM ?_account a if (!self::$ip)
LEFT JOIN ?_account_banned ab ON a.`id` = ab.`userId` AND ab.`end` > UNIX_TIMESTAMP() return AUTH_INTERNAL_ERR;
WHERE a.`user` = ?
GROUP BY a.`id`',
$name
);
if (!$query)
return AUTH_WRONGUSER;
self::$passHash = $query['passHash']; // handle login try limitation
if (!self::verifyCrypt($pass)) $ipBan = DB::Aowow()->selectRow('SELECT `ip`, `count`, IF(`unbanDate` > UNIX_TIMESTAMP(), 1, 0) AS "active" FROM ?_account_bannedips WHERE `type` = 0 AND `ip` = ?', self::$ip);
return AUTH_WRONGPASS; if (!$ipBan || !$ipBan['active']) // no entry exists or time expired; set count to 1
DB::Aowow()->query('REPLACE INTO ?_account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (?, 0, 1, UNIX_TIMESTAMP() + ?d)', self::$ip, Cfg::get('ACC_FAILED_AUTH_BLOCK'));
else // entry already exists; increment count
DB::Aowow()->query('UPDATE ?_account_bannedips SET `count` = `count` + 1, `unbanDate` = UNIX_TIMESTAMP() + ?d WHERE `ip` = ?', Cfg::get('ACC_FAILED_AUTH_BLOCK'), self::$ip);
// successfull auth; clear bans for this IP if ($ipBan && $ipBan['count'] >= Cfg::get('ACC_FAILED_AUTH_COUNT') && $ipBan['active'])
DB::Aowow()->query('DELETE FROM ?_account_bannedips WHERE `type` = 0 AND `ip` = ?', self::$ip); return AUTH_IPBANNED;
if ($query['bans'] & (ACC_BAN_PERM | ACC_BAN_TEMP)) $query = DB::Aowow()->SelectRow(
return AUTH_BANNED; 'SELECT a.`id`, a.`passHash`, BIT_OR(ab.`typeMask`) AS "bans", a.`status`
FROM ?_account a
LEFT JOIN ?_account_banned ab ON a.`id` = ab.`userId` AND ab.`end` > UNIX_TIMESTAMP()
WHERE a.`user` = ?
GROUP BY a.`id`',
$name
);
$user = $query['id']; if (!$query)
$hash = $query['passHash']; return AUTH_WRONGUSER;
break;
}
case AUTH_MODE_REALM:
{
if (!DB::isConnectable(DB_AUTH))
return AUTH_INTERNAL_ERR;
$wow = DB::Auth()->selectRow('SELECT a.id, a.salt, a.verifier, ab.active AS hasBan FROM account a LEFT JOIN account_banned ab ON ab.id = a.id AND active <> 0 WHERE username = ? LIMIT 1', $name); self::$passHash = $query['passHash'];
if (!$wow) if (!self::verifyCrypt($password))
return AUTH_WRONGUSER; return AUTH_WRONGPASS;
if (!self::verifySRP6($name, $pass, $wow['salt'], $wow['verifier'])) // successfull auth; clear bans for this IP
return AUTH_WRONGPASS; DB::Aowow()->query('DELETE FROM ?_account_bannedips WHERE `type` = 0 AND `ip` = ?', self::$ip);
if ($wow['hasBan']) if ($query['bans'] & (ACC_BAN_PERM | ACC_BAN_TEMP))
return AUTH_BANNED; return AUTH_BANNED;
if ($_ = self::checkOrCreateInDB($wow['id'], $name)) $userId = $query['id'];
$user = $_; $hash = $query['passHash'];
else
return AUTH_INTERNAL_ERR;
break; return AUTH_OK;
} }
case AUTH_MODE_EXTERNAL:
{
if (!file_exists('config/extAuth.php'))
{
trigger_error('config/extAuth.php not found');
return AUTH_INTERNAL_ERR;
}
require 'config/extAuth.php'; private static function authRealm(string $name, string $password, int &$userId, string &$hash) : int
{
if (!DB::isConnectable(DB_AUTH))
return AUTH_INTERNAL_ERR;
if (!function_exists('\extAuth')) $wow = DB::Auth()->selectRow('SELECT a.id, a.salt, a.verifier, ab.active AS hasBan FROM account a LEFT JOIN account_banned ab ON ab.id = a.id AND active <> 0 WHERE username = ? LIMIT 1', $name);
{ if (!$wow)
trigger_error('external auth function extAuth() not defined in config/extAuth.php'); return AUTH_WRONGUSER;
return AUTH_INTERNAL_ERR;
}
$extGroup = -1; if (!self::verifySRP6($name, $password, $wow['salt'], $wow['verifier']))
$result = \extAuth($name, $pass, $extId, $extGroup); return AUTH_WRONGPASS;
if ($result == AUTH_OK && $extId) if ($wow['hasBan'])
{ return AUTH_BANNED;
if ($_ = self::checkOrCreateInDB($extId, $name, $extGroup))
$user = $_;
else
return AUTH_INTERNAL_ERR;
break; if ($_ = self::checkOrCreateInDB($wow['id'], $name))
} $userId = $_;
else
return AUTH_INTERNAL_ERR;
return $result; return AUTH_OK;
} }
default:
private static function authExtern(string $name, string $password, int &$userId, string &$hash) : int
{
if (!file_exists('config/extAuth.php'))
{
trigger_error('User::authExtern - AUTH_MODE_EXTERNAL is selected but config/extAuth.php does not exist!', E_USER_ERROR);
return AUTH_INTERNAL_ERR;
}
require 'config/extAuth.php';
if (!function_exists('\extAuth'))
{
trigger_error('User::authExtern - AUTH_MODE_EXTERNAL is selected but function extAuth() is not defined!', E_USER_ERROR);
return AUTH_INTERNAL_ERR;
}
$extGroup = -1;
$extId = 0;
$result = \extAuth($name, $password, $extId, $extGroup);
if ($result == AUTH_OK && $extId)
{
if ($_ = self::checkOrCreateInDB($extId, $name, $extGroup))
$userId = $_;
else
return AUTH_INTERNAL_ERR; return AUTH_INTERNAL_ERR;
} }
// kickstart session return $result;
session_unset();
$_SESSION['user'] = $user;
$_SESSION['hash'] = $hash;
return AUTH_OK;
} }
// create a linked account for our settings if necessary // create a linked account for our settings if necessary
@@ -339,10 +344,10 @@ class User
if ($newId) if ($newId)
Util::gainSiteReputation($newId, SITEREP_ACTION_REGISTER); Util::gainSiteReputation($newId, SITEREP_ACTION_REGISTER);
return $newId; return $newId ?: 0;
} }
private static function createSalt() private static function createSalt() : string
{ {
$algo = '$2a'; $algo = '$2a';
$strength = '$09'; $strength = '$09';
@@ -352,18 +357,18 @@ class User
} }
// crypt used by aowow // crypt used by aowow
public static function hashCrypt($pass) public static function hashCrypt(string $pass) : string
{ {
return crypt($pass, self::createSalt()); return crypt($pass, self::createSalt());
} }
public static function verifyCrypt($pass, $hash = '') public static function verifyCrypt(string $pass, string $hash = '') : string
{ {
$_ = $hash ?: self::$passHash; $_ = $hash ?: self::$passHash;
return $_ === crypt($pass, $_); return $_ === crypt($pass, $_);
} }
private static function verifySRP6($user, $pass, $salt, $verifier) private static function verifySRP6(string $user, string $pass, string $salt, string $verifier) : bool
{ {
$g = gmp_init(7); $g = gmp_init(7);
$N = gmp_init('894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7', 16); $N = gmp_init('894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7', 16);
@@ -376,7 +381,7 @@ class User
return ($verifier === str_pad(gmp_export($v, 1, GMP_LSW_FIRST), 32, chr(0), STR_PAD_RIGHT)); return ($verifier === str_pad(gmp_export($v, 1, GMP_LSW_FIRST), 32, chr(0), STR_PAD_RIGHT));
} }
public static function isValidName($name, &$errCode = 0) public static function isValidName(string $name, int &$errCode = 0) : bool
{ {
$errCode = 0; $errCode = 0;
@@ -402,7 +407,7 @@ class User
return $errCode == 0; return $errCode == 0;
} }
public static function isValidPass($pass, &$errCode = 0) public static function isValidPass(string $pass, int &$errCode = 0) : bool
{ {
$errCode = 0; $errCode = 0;
@@ -420,9 +425,9 @@ class User
/* access management */ /* access management */
/*********************/ /*********************/
public static function isInGroup($group) : bool public static function isInGroup(int $group) : bool
{ {
return (self::$groups & $group) != 0; return $group == U_GROUP_NONE || (self::$groups & $group) != U_GROUP_NONE;
} }
public static function canComment() : bool public static function canComment() : bool
@@ -511,25 +516,38 @@ class User
public static function decrementDailyVotes() : void public static function decrementDailyVotes() : void
{ {
if (!self::isLoggedIn() || self::isBanned(ACC_BAN_RATE))
return;
self::$dailyVotes--; self::$dailyVotes--;
DB::Aowow()->query('UPDATE ?_account SET `dailyVotes` = ?d WHERE `id` = ?d', self::$dailyVotes, self::$id); DB::Aowow()->query('UPDATE ?_account SET `dailyVotes` = ?d WHERE `id` = ?d', self::$dailyVotes, self::$id);
} }
public static function getCurrentDailyVotes() : int public static function getCurrentDailyVotes() : int
{ {
if (!self::isLoggedIn() || self::isBanned(ACC_BAN_RATE) || self::$dailyVotes < 0)
return 0;
return self::$dailyVotes; return self::$dailyVotes;
} }
public static function getMaxDailyVotes() : int public static function getMaxDailyVotes() : int
{ {
if (!self::isLoggedIn() || self::isBanned()) if (!self::isLoggedIn() || self::isBanned(ACC_BAN_RATE))
return 0; return 0;
return Cfg::get('USER_MAX_VOTES') + (self::$reputation >= Cfg::get('REP_REQ_VOTEMORE_BASE') ? 1 + intVal((self::$reputation - Cfg::get('REP_REQ_VOTEMORE_BASE')) / Cfg::get('REP_REQ_VOTEMORE_ADD')) : 0); $threshold = Cfg::get('REP_REQ_VOTEMORE_BASE');
$extra = Cfg::get('REP_REQ_VOTEMORE_ADD');
$base = Cfg::get('USER_MAX_VOTES');
return $base + max(0, intVal((self::$reputation - $threshold + $extra) / $extra));
} }
public static function getReputation() : int public static function getReputation() : int
{ {
if (!self::isLoggedIn() || self::$reputation < 0)
return 0;
return self::$reputation; return self::$reputation;
} }
@@ -555,10 +573,10 @@ class User
$gUser['upvoteRep'] = Cfg::get('REP_REQ_UPVOTE'); $gUser['upvoteRep'] = Cfg::get('REP_REQ_UPVOTE');
$gUser['characters'] = self::getCharacters(); $gUser['characters'] = self::getCharacters();
$gUser['excludegroups'] = self::$excludeGroups; $gUser['excludegroups'] = self::$excludeGroups;
$gUser['settings'] = (new \StdClass); // profiler requires this to be set; has property premiumborder (NYI) $gUser['settings'] = (new \StdClass); // existence is checked in Profiler.js before g_user.excludegroups is applied; has property premiumborder (NYI)
if (Cfg::get('DEBUG') && User::isInGroup(U_GROUP_DEV | U_GROUP_ADMIN | U_GROUP_TESTER)) if (Cfg::get('DEBUG') && User::isInGroup(U_GROUP_DEV | U_GROUP_ADMIN | U_GROUP_TESTER))
$gUser['debug'] = true; // csv id-list output option on listviews $gUser['debug'] = true; // csv id-list output option on listviews; todo - set on per user basis
if ($_ = self::getProfilerExclusions()) if ($_ = self::getProfilerExclusions())
$gUser = array_merge($gUser, $_); $gUser = array_merge($gUser, $_);
@@ -582,6 +600,9 @@ class User
{ {
$result = []; $result = [];
if (!self::isLoggedIn() || self::isBanned())
return $result;
$res = DB::Aowow()->selectCol('SELECT `id` AS ARRAY_KEY, `name` FROM ?_account_weightscales WHERE `userId` = ?d', self::$id); $res = DB::Aowow()->selectCol('SELECT `id` AS ARRAY_KEY, `name` FROM ?_account_weightscales WHERE `userId` = ?d', self::$id);
if (!$res) if (!$res)
return $result; return $result;
@@ -596,6 +617,10 @@ class User
public static function getProfilerExclusions() : array public static function getProfilerExclusions() : array
{ {
$result = []; $result = [];
if (!self::isLoggedIn() || self::isBanned())
return $result;
$modes = [1 => 'excludes', 2 => 'includes']; $modes = [1 => 'excludes', 2 => 'includes'];
foreach ($modes as $mode => $field) foreach ($modes as $mode => $field)
if ($ex = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, `typeId` AS ARRAY_KEY2, `typeId` FROM ?_account_excludes WHERE `mode` = ?d AND `userId` = ?d', $mode, self::$id)) if ($ex = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, `typeId` AS ARRAY_KEY2, `typeId` FROM ?_account_excludes WHERE `mode` = ?d AND `userId` = ?d', $mode, self::$id))
@@ -644,10 +669,13 @@ class User
{ {
$result = []; $result = [];
if (!self::isLoggedIn() || self::isBanned(ACC_BAN_GUIDE))
return $result;
if ($guides = DB::Aowow()->select('SELECT `id`, `title`, `url` FROM ?_guides WHERE `userId` = ?d AND `status` <> ?d', self::$id, GUIDE_STATUS_ARCHIVED)) if ($guides = DB::Aowow()->select('SELECT `id`, `title`, `url` FROM ?_guides WHERE `userId` = ?d AND `status` <> ?d', self::$id, GUIDE_STATUS_ARCHIVED))
{ {
// fix url // fix url
array_walk($guides, fn(&$x) => $x['url'] = '?guide='.($x['url'] ?? $x['id'])); array_walk($guides, fn(&$x) => $x['url'] = '?guide='.($x['url'] ?: $x['id']));
$result = $guides; $result = $guides;
} }
@@ -664,7 +692,7 @@ class User
public static function getFavorites() : array public static function getFavorites() : array
{ {
if (!self::isLoggedIn()) if (!self::isLoggedIn() || self::isBanned())
return []; return [];
$res = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, `typeId` AS ARRAY_KEY2, `typeId` FROM ?_account_favorites WHERE `userId` = ?d', self::$id); $res = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, `typeId` AS ARRAY_KEY2, `typeId` FROM ?_account_favorites WHERE `userId` = ?d', self::$id);

View File

@@ -358,7 +358,7 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
if (!User::isValidPass($this->_post['password'])) if (!User::isValidPass($this->_post['password']))
return Lang::account('wrongPass'); return Lang::account('wrongPass');
switch (User::Auth($this->_post['username'], $this->_post['password'])) switch (User::authenticate($this->_post['username'], $this->_post['password']))
{ {
case AUTH_OK: case AUTH_OK:
if (!User::$ip) if (!User::$ip)