diff --git a/includes/components/report.class.php b/includes/components/report.class.php index c3906e1d..f3b766c3 100644 --- a/includes/components/report.class.php +++ b/includes/components/report.class.php @@ -204,7 +204,7 @@ class Report 'subject' => $this->subject, 'ip' => User::$ip, 'description' => $desc, - 'userAgent' => $userAgent ?: $_SERVER['HTTP_USER_AGENT'], + 'userAgent' => $userAgent ?: User::$agent, 'appName' => $appName ?: (get_browser(null, true)['browser'] ?: '') ); diff --git a/includes/defines.php b/includes/defines.php index e6bc2b80..2ddd6cfa 100644 --- a/includes/defines.php +++ b/includes/defines.php @@ -68,6 +68,12 @@ define('ACC_STATUS_NEW', 1); // just created, awa define('ACC_STATUS_RECOVER_USER', 2); // currently recovering username define('ACC_STATUS_RECOVER_PASS', 3); // currently recovering password +// Session Status +define('SESSION_ACTIVE', 1); +define('SESSION_LOGOUT', 2); +define('SESSION_FORCED_LOGOUT', 3); +define('SESSION_EXPIRED', 4); + define('ACC_BAN_NONE', 0x00); // all clear define('ACC_BAN_TEMP', 0x01); define('ACC_BAN_PERM', 0x02); diff --git a/includes/user.class.php b/includes/user.class.php index 48a8c14b..217b5ee3 100644 --- a/includes/user.class.php +++ b/includes/user.class.php @@ -10,18 +10,19 @@ class User { public static int $id = 0; public static string $username = ''; - public static int $banStatus = 0x0; // see ACC_BAN_* defines + public static int $banStatus = 0x0; // see ACC_BAN_* defines + public static int $status = 0x0; public static int $groups = 0x0; public static int $perms = 0; public static ?string $email = null; public static int $dailyVotes = 0; + public static bool $debug = false; // show ids in lists (used to be debug, is now user setting) public static ?string $ip = null; + public static ?string $agent = null; public static Locale $preferedLoc; private static int $reputation = 0; private static string $dataKey = ''; - private static bool $expires = false; - private static string $passHash = ''; private static int $excludeGroups = 1; private static ?LocalProfileList $profiles = null; @@ -38,9 +39,10 @@ class User // session have a dataKey to access the JScripts (yes, also the anons) if (empty($_SESSION['dataKey'])) - $_SESSION['dataKey'] = Util::createHash(); // just some random numbers for identifictaion purpose + $_SESSION['dataKey'] = Util::createHash(); // just some random numbers for identification purpose self::$dataKey = $_SESSION['dataKey']; + self::$agent = $_SERVER['HTTP_USER_AGENT']; if (!self::$ip) return false; @@ -58,43 +60,65 @@ class User if (empty($_SESSION['user'])) return false; - // timed out... - if (!empty($_SESSION['timeout']) && $_SESSION['timeout'] <= time()) - return false; - - $uData = DB::Aowow()->SelectRow( - 'SELECT a.`id`, a.`passHash`, a.`username`, a.`locale`, a.`userGroups`, a.`userPerms`, a.`allowExpire`, BIT_OR(ab.`typeMask`) AS "bans", IFNULL(SUM(r.`amount`), 0) AS "reputation", a.`dailyVotes`, a.`excludeGroups` + $session = DB::Aowow()->selectRow('SELECT `userId`, `expires` FROM ?_account_sessions WHERE `status` = ?d AND `sessionId` = ?', SESSION_ACTIVE, session_id()); + $userData = DB::Aowow()->selectRow( + 'SELECT a.`id`, a.`passHash`, a.`username`, a.`locale`, a.`userGroups`, a.`userPerms`, BIT_OR(ab.`typeMask`) AS "bans", IFNULL(SUM(r.`amount`), 0) AS "reputation", a.`dailyVotes`, a.`excludeGroups`, a.`status`, a.`statusTimer`, a.`email` FROM ?_account a - LEFT JOIN ?_account_banned ab ON a.`id` = ab.`userId` AND ab.`end` > UNIX_TIMESTAMP() - LEFT JOIN ?_account_reputation r ON a.`id` = r.`userId` + LEFT JOIN ?_account_banned ab ON a.`id` = ab.`userId` AND ab.`end` > UNIX_TIMESTAMP() + LEFT JOIN ?_account_reputation r ON a.`id` = r.`userId` WHERE a.`id` = ?d GROUP BY a.`id`', $_SESSION['user'] ); - if (!$uData) - return false; - - if ($loc = Locale::tryFrom($uData['locale'])) - self::$preferedLoc = $loc; - - // password changed, terminate session - if (AUTH_MODE_SELF && $uData['passHash'] != $_SESSION['hash']) + if (!$session || !$userData) { self::destroy(); return false; } + else if ($session['expires'] && $session['expires'] < time()) + { + DB::Aowow()->query('UPDATE ?_account_sessions SET `touched` = ?d, `status` = ?d WHERE `sessionId` = ?', time(), SESSION_EXPIRED, session_id()); + self::destroy(); + return false; + } + else if ($session['userId'] != $userData['id']) // what in the name of fuck..? + { + // Don't know why, don't know how .. doesn't matter, both parties are out. + DB::Aowow()->query('UPDATE ?_account_sessions SET `touched` = ?d, `status` = ?d WHERE `userId` IN (?a) AND `status` = ?d', time(), SESSION_FORCED_LOGOUT, [$userData['id'], $session['userId']], SESSION_ACTIVE); + trigger_error('User::init - tried to resume session "'.session_id().'" of user #'.$_SESSION['user'].' linked to session data for user #'.$session['userId'].' Kicked both!', E_USER_ERROR); + self::destroy(); + return false; + } - self::$id = intVal($uData['id']); - self::$username = $uData['username']; - self::$passHash = $uData['passHash']; - self::$expires = (bool)$uData['allowExpire']; - self::$reputation = $uData['reputation']; - self::$banStatus = $uData['bans']; - self::$groups = self::isBanned() ? 0 : intval($uData['userGroups']); - self::$perms = self::isBanned() ? 0 : intval($uData['userPerms']); - self::$dailyVotes = $uData['dailyVotes']; - self::$excludeGroups = $uData['excludeGroups']; + DB::Aowow()->query('UPDATE ?_account_sessions SET `touched` = ?d, `expires` = IF(`expires`, ?d, 0) WHERE `sessionId` = ?', time(), time() + Cfg::get('SESSION_TIMEOUT_DELAY'), session_id()); + + if ($loc = Locale::tryFrom($userData['locale'])) + self::$preferedLoc = $loc; + + // reset expired account statuses + if ($userData['statusTimer'] < time() && $userData['status'] > ACC_STATUS_NEW) + { + DB::Aowow()->query('UPDATE ?_account SET `status` = ?d, `statusTimer` = 0, `token` = "", `updateValue` = "" WHERE `id` = ?d', ACC_STATUS_OK, User::$id); + $userData['status'] = ACC_STATUS_OK; + } + + + /*******************************/ + /* past here we are logged in */ + /*******************************/ + + self::$id = intVal($userData['id']); + self::$username = $userData['username']; + self::$reputation = $userData['reputation']; + self::$banStatus = $userData['bans']; + self::$groups = self::isBanned() ? 0 : intval($userData['userGroups']); + self::$perms = self::isBanned() ? 0 : intval($userData['userPerms']); + self::$dailyVotes = $userData['dailyVotes']; + self::$excludeGroups = $userData['excludeGroups']; + self::$status = $userData['status']; + // self::$debug = $userData['debug']; // TBD + self::$email = $userData['email']; $conditions = [['OR', ['user', self::$id], ['ap.accountId', self::$id]]]; if (!self::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU)) @@ -104,10 +128,10 @@ class User // 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 + // - consecutive visits // - votes per day // - reputation for daily visit - if (self::isLoggedIn()) + if (!self::isBanned()) { $lastLogin = DB::Aowow()->selectCell('SELECT `curLogin` FROM ?_account WHERE `id` = ?d', self::$id); // either the day changed or the last visit was >24h ago @@ -130,7 +154,7 @@ class User Util::gainSiteReputation(self::$id, SITEREP_ACTION_DAILYVISIT); // increment consecutive visits (next day or first of new month and not more than 48h) - // i bet my ass i forgott a corner case + // i bet my ass i forgot a corner case if ((date('j', $lastLogin) + 1 == date('j') || (date('j') == 1 && date('n', $lastLogin) != date('n'))) && (time() - $lastLogin) < 2 * DAY) DB::Aowow()->query('UPDATE ?_account SET `consecutiveVisits` = `consecutiveVisits` + 1 WHERE `id` = ?d', self::$id); else @@ -169,9 +193,7 @@ class User public static function save(bool $toDB = false) { $_SESSION['user'] = self::$id; - $_SESSION['hash'] = self::$passHash; $_SESSION['locale'] = self::$preferedLoc; - $_SESSION['timeout'] = self::$expires ? time() + Cfg::get('SESSION_TIMEOUT_DELAY') : 0; // $_SESSION['dataKey'] does not depend on user login status and is set in User::init() if (self::isLoggedIn() && $toDB) @@ -200,27 +222,27 @@ class User public static function authenticate(string $login, string $password) : int { $userId = 0; - $hash = ''; $result = match (Cfg::get('ACC_AUTH_MODE')) { - AUTH_MODE_SELF => self::authSelf($login, $password, $userId, $hash), - AUTH_MODE_REALM => self::authRealm($login, $password, $userId, $hash), - AUTH_MODE_EXTERNAL => self::authExtern($login, $password, $userId, $hash), + AUTH_MODE_SELF => self::authSelf($login, $password, $userId), + AUTH_MODE_REALM => self::authRealm($login, $password, $userId), + AUTH_MODE_EXTERNAL => self::authExtern($login, $password, $userId), default => AUTH_INTERNAL_ERR }; - if ($result == AUTH_OK) + // also banned? its a feature block, not login block.. + if ($result == AUTH_OK || $result == AUTH_BANNED) { session_unset(); $_SESSION['user'] = $userId; - $_SESSION['hash'] = self::hashCrypt($hash); + self::$id = $userId; } return $result; } - private static function authSelf(string $nameOrEmail, string $password, int &$userId, string &$hash) : int + private static function authSelf(string $nameOrEmail, string $password, int &$userId) : int { if (!self::$ip) return AUTH_INTERNAL_ERR; @@ -250,8 +272,7 @@ class User if (!$query) return AUTH_WRONGUSER; - self::$passHash = $query['passHash']; - if (!self::verifyCrypt($password)) + if (!self::verifyCrypt($password, $query['passHash'])) return AUTH_WRONGPASS; // successfull auth; clear bans for this IP @@ -261,12 +282,11 @@ class User return AUTH_BANNED; $userId = $query['id']; - $hash = $query['passHash']; return AUTH_OK; } - private static function authRealm(string $name, string $password, int &$userId, string &$hash) : int + private static function authRealm(string $name, string $password, int &$userId) : int { if (!DB::isConnectable(DB_AUTH)) return AUTH_INTERNAL_ERR; @@ -289,7 +309,7 @@ class User return AUTH_OK; } - private static function authExtern(string $nameOrEmail, string $password, int &$userId, string &$hash) : int + private static function authExtern(string $nameOrEmail, string $password, int &$userId) : int { if (!file_exists('config/extAuth.php')) { @@ -334,7 +354,7 @@ class User return $_; } - $newId = DB::Aowow()->query('INSERT IGNORE INTO ?_account (`extId`, `login`, `passHash`, `username`, `email`, `joinDate`, `allowExpire`, `prevIP`, `prevLogin`, `locale`, `status`, `userGroups`) VALUES (?d, "", "", ?, "", UNIX_TIMESTAMP(), 0, ?, UNIX_TIMESTAMP(), ?d, ?d, ?d)', + $newId = DB::Aowow()->query('INSERT IGNORE INTO ?_account (`extId`, `passHash`, `username`, `joinDate`, `prevIP`, `prevLogin`, `locale`, `status`, `userGroups`) VALUES (?d, "", ?, UNIX_TIMESTAMP(), ?, UNIX_TIMESTAMP(), ?d, ?d, ?d)', $extId, $name, $_SERVER["REMOTE_ADDR"] ?? '', @@ -364,12 +384,12 @@ class User return crypt($pass, self::createSalt()); } - public static function verifyCrypt(string $pass, string $hash = '') : string + public static function verifyCrypt(string $pass, string $hash) : bool { - $_ = $hash ?: self::$passHash; - return $_ === crypt($pass, $_); + return $hash === crypt($pass, $hash); } + // SRP6 used by TC private static function verifySRP6(string $user, string $pass, string $salt, string $verifier) : bool { $g = gmp_init(7); @@ -409,7 +429,7 @@ class User return $errCode == 0; } - public static function isValidPass(string $pass, int &$errCode = 0) : bool + public static function isValidPass(string $pass, ?int &$errCode = 0) : bool { $errCode = 0; @@ -511,6 +531,11 @@ class User return self::$banStatus & (ACC_BAN_TEMP | ACC_BAN_PERM | $addBanMask); } + public static function isRecovering() : bool + { + return self::$status == ACC_STATUS_RECOVER_USER || self::$status == ACC_STATUS_RECOVER_PASS; + } + /**************/ /* js-related */ @@ -553,7 +578,7 @@ class User return self::$reputation; } - public static function getUserGlobals() : array + public static function getUserGlobal() : array { $gUser = array( 'id' => self::$id, @@ -577,7 +602,15 @@ class User $gUser['excludegroups'] = self::$excludeGroups; 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; todo - set on per user basis + $gUser['debug'] = true; // csv id-list output option on listviews + + if (self::getPremiumBorder()) + $gUser['settings'] = ['premiumborder' => 1]; + else + $gUser['settings'] = (new \StdClass); // existence is checked in Profiler.js before g_user.excludegroups is applied + + if (self::isPremium()) + $gUser['premium'] = 1; if (self::getPremiumBorder()) $gUser['settings'] = ['premiumborder' => 1]; diff --git a/pages/account.php b/pages/account.php index a1391810..9fbff9af 100644 --- a/pages/account.php +++ b/pages/account.php @@ -137,10 +137,7 @@ class AccountPage extends GenericPage if ($err = $this->doSignIn()) $this->error = $err; else - { - session_regenerate_id(true); // user status changed => regenerate id header('Location: '.$this->getNext(true), true, 302); - } } else if ($this->_get['token'] && ($_ = DB::Aowow()->selectCell('SELECT `username` FROM ?_account WHERE `status` IN (?a) AND `token` = ? AND `statusTimer` > UNIX_TIMESTAMP()', [ACC_STATUS_RECOVER_USER, ACC_STATUS_OK], $this->_get['token']))) $this->user = $_; @@ -184,6 +181,8 @@ class AccountPage extends GenericPage $this->head = sprintf(Lang::account('register'), $nStep); break; case 'signout': + DB::Aowow()->query('UPDATE ?_account_sessions SET `touched` = ?d, `status` = ?d WHERE `sessionId` = ?', time(), SESSION_LOGOUT, session_id()); + User::destroy(); default: header('Location: '.$this->getNext(true), true, 302); @@ -365,15 +364,20 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup return Lang::main('intError'); // reset account status, update expiration - DB::Aowow()->query('UPDATE ?_account SET `prevIP` = IF(`curIp` = ?, `prevIP`, `curIP`), `curIP` = IF(`curIp` = ?, `curIP`, ?), `allowExpire` = ?d, `status` = IF(`status` = ?d, `status`, 0), `statusTimer` = IF(`status` = ?d, `statusTimer`, 0), `token` = IF(`status` = ?d, `token`, "") WHERE LOWER(`username`) = LOWER(?)', + DB::Aowow()->query('UPDATE ?_account SET `prevIP` = IF(`curIp` = ?, `prevIP`, `curIP`), `curIP` = IF(`curIp` = ?, `curIP`, ?), `status` = IF(`status` = ?d, `status`, 0), `statusTimer` = IF(`status` = ?d, `statusTimer`, 0), `token` = IF(`status` = ?d, `token`, "") WHERE LOWER(`username`) = LOWER(?)', User::$ip, User::$ip, User::$ip, - $this->_post['remember_me'] != 'yes', ACC_STATUS_NEW, ACC_STATUS_NEW, ACC_STATUS_NEW, $this->_post['username'] ); - if (User::init()) - User::save(); // overwrites the current user + session_regenerate_id(true); // user status changed => regenerate id + + // create new session entry + DB::Aowow()->query('INSERT INTO ?_account_sessions (`userId`, `sessionId`, `created`, `expires`, `touched`, `deviceInfo`, `ip`, `status`) VALUES (?d, ?, ?d, ?d, ?d, ?, ?, ?d)', + User::$id, session_id(), time(), $this->_post['remember_me'] ? 0 : time() + Cfg::get('SESSION_TIMEOUT_DELAY'), time(), User::$agent, User::$ip, SESSION_ACTIVE); + + if (User::init()) // reinitialize the user + User::save(); return; case AUTH_BANNED: @@ -432,13 +436,12 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup // create.. $token = Util::createHash(); - $ok = DB::Aowow()->query('REPLACE INTO ?_account (`login`, `passHash`, `username`, `email`, `joindate`, `curIP`, `allowExpire`, `locale`, `userGroups`, `status`, `statusTimer`, `token`) VALUES (?, ?, ?, ?, UNIX_TIMESTAMP(), ?, ?d, ?d, ?d, ?d, UNIX_TIMESTAMP() + ?d, ?)', + $ok = DB::Aowow()->query('REPLACE INTO ?_account (`login`, `passHash`, `username`, `email`, `joindate`, `curIP`, `locale`, `userGroups`, `status`, `statusTimer`, `token`) VALUES (?, ?, ?, ?, UNIX_TIMESTAMP(), ?, ?d, ?d, ?d, ?d, UNIX_TIMESTAMP() + ?d, ?)', $this->_post['username'], User::hashCrypt($this->_post['password']), $this->_post['username'], $this->_post['email'], User::$ip, - $this->_post['remember_me'] != 'yes', Lang::getLocale()->value, U_GROUP_PENDING, ACC_STATUS_NEW, @@ -479,18 +482,18 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup if (!Util::isValidEmail($this->_post['email'])) return Lang::account('emailInvalid'); - $uId = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE token = ? AND email = ? AND status = ?d AND statusTimer > UNIX_TIMESTAMP()', + $userData = DB::Aowow()->selectRow('SELECT `id, `passHash` FROM ?_account WHERE `token` = ? AND `email` = ? AND `status` = ?d AND `statusTimer` > UNIX_TIMESTAMP()', $this->_post['token'], $this->_post['email'], ACC_STATUS_RECOVER_PASS ); - if (!$uId) + if (!$userData) return Lang::account('emailNotFound'); // assume they didn't meddle with the token - if (!User::verifyCrypt($this->_post['c_password'])) + if (!User::verifyCrypt($this->_post['c_password'], $userData['passHash'])) return Lang::account('newPassDiff'); - if (!DB::Aowow()->query('UPDATE ?_account SET passHash = ?, status = ?d WHERE id = ?d', User::hashCrypt($this->_post['c_password']), ACC_STATUS_OK, $uId)) + if (!DB::Aowow()->query('UPDATE ?_account SET `passHash` = ?, `status` = ?d WHERE `id` = ?d', User::hashCrypt($this->_post['c_password']), ACC_STATUS_OK, $userData['id'])) return Lang::main('intError'); } diff --git a/pages/genericPage.class.php b/pages/genericPage.class.php index 1919a90f..801f48c0 100644 --- a/pages/genericPage.class.php +++ b/pages/genericPage.class.php @@ -318,7 +318,7 @@ class GenericPage if ($ahl = DB::Aowow()->selectCell('SELECT altHeaderLogo FROM ?_home_featuredbox WHERE ?d BETWEEN startDate AND endDate ORDER BY id DESC', time())) $this->headerLogo = Util::defStatic($ahl); - $this->gUser = User::getUserGlobals(); + $this->gUser = User::getUserGlobal(); $this->gFavorites = User::getFavorites(); $this->pageTemplate['pageName'] = strtolower($pageCall); diff --git a/setup/db_structure.sql b/setup/db_structure.sql index 95284c99..6186d542 100644 --- a/setup/db_structure.sql +++ b/setup/db_structure.sql @@ -30,14 +30,13 @@ CREATE TABLE `aowow_account` ( `username` varchar(64) NOT NULL COMMENT 'unique; used for for links and display', `email` varchar(64) DEFAULT NULL COMMENT 'unique; can be used for login if AUTH_SELF and can be NULL if not', `joinDate` int unsigned NOT NULL COMMENT 'unixtime', - `allowExpire` tinyint unsigned NOT NULL, `dailyVotes` smallint unsigned NOT NULL DEFAULT 0, `consecutiveVisits` smallint unsigned NOT NULL DEFAULT 0, `curIP` varchar(45) NOT NULL DEFAULT '', `prevIP` varchar(45) NOT NULL DEFAULT '', `curLogin` int unsigned NOT NULL DEFAULT 0 COMMENT 'unixtime', `prevLogin` int unsigned NOT NULL DEFAULT 0, - `locale` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '0,2,3,6,8', + `locale` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '0,2,3,4,6,8', `userGroups` smallint unsigned NOT NULL DEFAULT 0 COMMENT 'bitmask', `avatar` varchar(50) NOT NULL DEFAULT '' COMMENT 'icon-string for internal or id for upload', `title` varchar(50) NOT NULL DEFAULT '' COMMENT 'user can obtain custom titles', @@ -181,6 +180,25 @@ CREATE TABLE `aowow_account_reputation` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT COMMENT='reputation log'; /*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `aowow_account_sessions` +-- + +DROP TABLE IF EXISTS `aowow_account_sessions`; +CREATE TABLE `aowow_account_sessions` ( + `userId` int unsigned NOT NULL, + `sessionId` varchar(190) NOT NULL COMMENT 'PHPSESSID', + `created` int unsigned NOT NULL, + `expires` int unsigned NOT NULL COMMENT 'timestamp or 0 (never expires)', + `touched` int unsigned NOT NULL COMMENT 'timestamp - last used', + `deviceInfo` varchar(256) NOT NULL, + `ip` varchar(45) NOT NULL COMMENT 'can change; just last used ip', + `status` enum('ACTIVE', 'LOGOUT', 'FORCEDLOGOUT', 'EXPIRED') NOT NULL, + UNIQUE KEY `sessionId` (`sessionId`) USING BTREE, + KEY `userId` (`userId`) USING BTREE, + CONSTRAINT `FK_acc_sessions` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT; + -- -- Table structure for table `aowow_account_weightscale_data` -- @@ -3323,7 +3341,7 @@ UNLOCK TABLES; LOCK TABLES `aowow_dbversion` WRITE; /*!40000 ALTER TABLE `aowow_dbversion` DISABLE KEYS */; -INSERT INTO `aowow_dbversion` VALUES (1753572320,0,NULL,NULL); +INSERT INTO `aowow_dbversion` VALUES (1753574970,0,NULL,NULL); /*!40000 ALTER TABLE `aowow_dbversion` ENABLE KEYS */; UNLOCK TABLES; diff --git a/setup/tools/clisetup/account.us.php b/setup/tools/clisetup/account.us.php index f6f8027f..937e1e80 100644 --- a/setup/tools/clisetup/account.us.php +++ b/setup/tools/clisetup/account.us.php @@ -103,7 +103,7 @@ CLISetup::registerUtility(new class extends UtilityScript if (!$name || !$passw) return false; - if (DB::Aowow()->query('REPLACE INTO ?_account (`login`, `passHash`, `username`, `joindate`, `email`, `allowExpire`, `userGroups`, `userPerms`) VALUES (?, ?, ?, UNIX_TIMESTAMP(), ?, 0, ?d, 1)', + if (DB::Aowow()->query('REPLACE INTO ?_account (`login`, `passHash`, `username`, `joindate`, `email`, `userGroups`, `userPerms`) VALUES (?, ?, ?, UNIX_TIMESTAMP(), ?, ?d, 1)', $name, User::hashCrypt($passw), $name, $email ?: Cfg::get('CONTACT_EMAIL'), U_GROUP_ADMIN)) { $newId = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE `username` = ?', $name); diff --git a/setup/updates/1753572319_01.sql b/setup/updates/1753572319_01.sql index 869c7953..2e45b083 100644 --- a/setup/updates/1753572319_01.sql +++ b/setup/updates/1753572319_01.sql @@ -3,6 +3,8 @@ ALTER TABLE `aowow_account` CHANGE COLUMN `user` `login` varchar(64) NOT NULL DEFAULT '' COMMENT 'only used for login', CHANGE COLUMN `displayName` `username` varchar(64) NOT NULL COMMENT 'unique; used for for links and display', MODIFY COLUMN `email` varchar(64) DEFAULT NULL COMMENT 'unique; can be used for login if AUTH_SELF and can be NULL if not', + MODIFY COLUMN `token` varchar(40) DEFAULT NULL COMMENT 'identification key for changes to account', + ADD COLUMN `updateValue` varchar(128) DEFAULT NULL COMMENT 'temp store for new passHash / email' AFTER `token`, ADD CONSTRAINT `username` UNIQUE (`username`); UPDATE `aowow_account` diff --git a/setup/updates/1753574969_01.sql b/setup/updates/1753574969_01.sql new file mode 100644 index 00000000..4d599283 --- /dev/null +++ b/setup/updates/1753574969_01.sql @@ -0,0 +1,17 @@ +DROP TABLE IF EXISTS `aowow_account_sessions`; +CREATE TABLE `aowow_account_sessions` ( + `userId` int unsigned NOT NULL, + `sessionId` varchar(190) NOT NULL COMMENT 'PHPSESSID', -- max size (for utf8mb4) to still be a key + `created` int unsigned NOT NULL, + `expires` int unsigned NOT NULL COMMENT 'timestamp or 0 (never expires)', + `touched` int unsigned NOT NULL COMMENT 'timestamp - last used', + `deviceInfo` varchar(256) NOT NULL, + `ip` varchar(45) NOT NULL COMMENT 'can change; just last used ip', -- think mobile switching between WLAN and mobile data + `status` enum('ACTIVE', 'LOGOUT', 'FORCEDLOGOUT', 'EXPIRED') NOT NULL, + UNIQUE KEY `sessionId` (`sessionId`) USING BTREE, + KEY `userId` (`userId`) USING BTREE, + CONSTRAINT `FK_acc_sessions` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT; + +ALTER TABLE `aowow_account` + DROP COLUMN `allowExpire`;