diff options
Diffstat (limited to 'classes/userhelper.php')
-rw-r--r-- | classes/userhelper.php | 250 |
1 files changed, 203 insertions, 47 deletions
diff --git a/classes/userhelper.php b/classes/userhelper.php index ca673cf58..ce26e6c71 100644 --- a/classes/userhelper.php +++ b/classes/userhelper.php @@ -1,6 +1,22 @@ <?php +use OTPHP\TOTP; + class UserHelper { + const HASH_ALGO_SSHA512 = 'SSHA-512'; + const HASH_ALGO_SSHA256 = 'SSHA-256'; + const HASH_ALGO_MODE2 = 'MODE2'; + const HASH_ALGO_SHA1X = 'SHA1X'; + const HASH_ALGO_SHA1 = 'SHA1'; + + const HASH_ALGOS = [ + self::HASH_ALGO_SSHA512, + self::HASH_ALGO_SSHA256, + self::HASH_ALGO_MODE2, + self::HASH_ALGO_SHA1X, + self::HASH_ALGO_SHA1 + ]; + static function authenticate(string $login = null, string $password = null, bool $check_only = false, string $service = null) { if (!Config::get(Config::SINGLE_USER_MODE)) { $user_id = false; @@ -23,27 +39,24 @@ class UserHelper { session_regenerate_id(true); - $_SESSION["uid"] = $user_id; - $_SESSION["auth_module"] = $auth_module; + $user = ORM::for_table('ttrss_users')->find_one($user_id); - $pdo = Db::pdo(); - $sth = $pdo->prepare("SELECT login,access_level,pwd_hash FROM ttrss_users - WHERE id = ?"); - $sth->execute([$user_id]); - $row = $sth->fetch(); - - $_SESSION["name"] = $row["login"]; - $_SESSION["access_level"] = $row["access_level"]; - $_SESSION["csrf_token"] = bin2hex(get_random_bytes(16)); + if ($user) { + $_SESSION["uid"] = $user_id; + $_SESSION["auth_module"] = $auth_module; + $_SESSION["name"] = $user->login; + $_SESSION["access_level"] = $user->access_level; + $_SESSION["csrf_token"] = bin2hex(get_random_bytes(16)); + $_SESSION["ip_address"] = UserHelper::get_user_ip(); + $_SESSION["pwd_hash"] = $user->pwd_hash; - $usth = $pdo->prepare("UPDATE ttrss_users SET last_login = NOW() WHERE id = ?"); - $usth->execute([$user_id]); + $user->last_login = Db::NOW(); + $user->save(); - $_SESSION["ip_address"] = UserHelper::get_user_ip(); - $_SESSION["user_agent"] = sha1($_SERVER['HTTP_USER_AGENT']); - $_SESSION["pwd_hash"] = $row["pwd_hash"]; + return true; + } - return true; + return false; } if ($login && $password && !$user_id && !$check_only) @@ -75,7 +88,7 @@ class UserHelper { if (!$pluginhost) $pluginhost = PluginHost::getInstance(); - if ($owner_uid && SCHEMA_VERSION >= 100 && empty($_SESSION["safe_mode"])) { + if ($owner_uid && Config::get_schema_version() >= 100 && empty($_SESSION["safe_mode"])) { $plugins = get_pref(Prefs::_ENABLED_PLUGINS, $owner_uid); $pluginhost->load((string)$plugins, PluginHost::KIND_USER, $owner_uid); @@ -117,8 +130,9 @@ class UserHelper { } else { /* bump login timestamp */ - $sth = $pdo->prepare("UPDATE ttrss_users SET last_login = NOW() WHERE id = ?"); - $sth->execute([$_SESSION['uid']]); + $user = ORM::for_table('ttrss_users')->find_one($_SESSION["uid"]); + $user->last_login = Db::NOW(); + $user->save(); $_SESSION["last_login_update"] = time(); } @@ -146,20 +160,29 @@ class UserHelper { if (isset($_SERVER[$hdr])) return $_SERVER[$hdr]; } - } - static function find_user_by_login(string $login) { - $pdo = Db::pdo(); + return null; + } - $sth = $pdo->prepare("SELECT id FROM ttrss_users WHERE - LOWER(login) = LOWER(?)"); - $sth->execute([$login]); + static function get_login_by_id(int $id) { + $user = ORM::for_table('ttrss_users') + ->find_one($id); - if ($row = $sth->fetch()) { - return $row["id"]; - } + if ($user) + return $user->login; + else + return null; + } - return false; + static function find_user_by_login(string $login) { + $user = ORM::for_table('ttrss_users') + ->where('login', $login) + ->find_one(); + + if ($user) + return $user->id; + else + return null; } static function logout() { @@ -173,34 +196,167 @@ class UserHelper { session_commit(); } - static function reset_password($uid, $format_output = false) { + static function get_salt() { + return substr(bin2hex(get_random_bytes(125)), 0, 250); + } - $pdo = Db::pdo(); + static function reset_password($uid, $format_output = false, $new_password = "") { - $sth = $pdo->prepare("SELECT login FROM ttrss_users WHERE id = ?"); - $sth->execute([$uid]); + $user = ORM::for_table('ttrss_users')->find_one($uid); + $message = ""; - if ($row = $sth->fetch()) { + if ($user) { - $login = $row["login"]; + $login = $user->login; - $new_salt = substr(bin2hex(get_random_bytes(125)), 0, 250); - $tmp_user_pwd = make_password(); + $new_salt = self::get_salt(); + $tmp_user_pwd = $new_password ? $new_password : make_password(); - $pwd_hash = encrypt_password($tmp_user_pwd, $new_salt, true); + $pwd_hash = self::hash_password($tmp_user_pwd, $new_salt, self::HASH_ALGOS[0]); - $sth = $pdo->prepare("UPDATE ttrss_users - SET pwd_hash = ?, salt = ?, otp_enabled = false - WHERE id = ?"); - $sth->execute([$pwd_hash, $new_salt, $uid]); + $user->pwd_hash = $pwd_hash; + $user->salt = $new_salt; + $user->save(); $message = T_sprintf("Changed password of user %s to %s", "<strong>$login</strong>", "<strong>$tmp_user_pwd</strong>"); + } else { + $message = __("User not found"); + } - if ($format_output) - print_notice($message); - else - print $message; + if ($format_output) + print_notice($message); + else + print $message; + } + static function check_otp(int $owner_uid, int $otp_check) : bool { + $otp = TOTP::create(self::get_otp_secret($owner_uid, true)); + + return $otp->now() == $otp_check; + } + + static function disable_otp(int $owner_uid) : bool { + $user = ORM::for_table('ttrss_users')->find_one($owner_uid); + + if ($user) { + $user->otp_enabled = false; + + // force new OTP secret when next enabled + if (Config::get_schema_version() >= 143) { + $user->otp_secret = null; + } + + $user->save(); + + return true; + } else { + return false; } } + + static function enable_otp(int $owner_uid, int $otp_check) : bool { + $secret = self::get_otp_secret($owner_uid); + + if ($secret) { + $otp = TOTP::create($secret); + $user = ORM::for_table('ttrss_users')->find_one($owner_uid); + + if ($otp->now() == $otp_check && $user) { + + $user->otp_enabled = true; + $user->save(); + + return true; + } + } + return false; + } + + + static function is_otp_enabled(int $owner_uid) : bool { + $user = ORM::for_table('ttrss_users')->find_one($owner_uid); + + if ($user) { + return $user->otp_enabled; + } else { + return false; + } + } + + static function get_otp_secret(int $owner_uid, bool $show_if_enabled = false) { + $user = ORM::for_table('ttrss_users')->find_one($owner_uid); + + if ($user) { + + $salt_based_secret = mb_substr(sha1($user->salt), 0, 12); + + if (Config::get_schema_version() >= 143) { + $secret = $user->otp_secret; + + if (empty($secret)) { + + /* migrate secret if OTP is already enabled, otherwise make a new one */ + if ($user->otp_enabled) { + $user->otp_secret = $salt_based_secret; + } else { + $user->otp_secret = bin2hex(get_random_bytes(6)); + } + + $user->save(); + + $secret = $user->otp_secret; + } + } else { + $secret = $salt_based_secret; + } + + if (!$user->otp_enabled || $show_if_enabled) { + return \ParagonIE\ConstantTime\Base32::encodeUpperUnpadded($secret); + } + } + + return null; + } + + static function is_default_password() { + $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]); + + if ($authenticator && + method_exists($authenticator, "check_password") && + $authenticator->check_password($_SESSION["uid"], "password")) { + + return true; + } + return false; + } + + static function hash_password(string $pass, string $salt, string $algo = "") { + + if (!$algo) $algo = self::HASH_ALGOS[0]; + + $pass_hash = ""; + + switch ($algo) { + case self::HASH_ALGO_SHA1: + $pass_hash = sha1($pass); + break; + case self::HASH_ALGO_SHA1X: + $pass_hash = sha1("$salt:$pass"); + break; + case self::HASH_ALGO_MODE2: + case self::HASH_ALGO_SSHA256: + $pass_hash = hash('sha256', $salt . $pass); + break; + case self::HASH_ALGO_SSHA512: + $pass_hash = hash('sha512', $salt . $pass); + break; + default: + user_error("hash_password: unknown hash algo: $algo", E_USER_ERROR); + } + + if ($pass_hash) + return "$algo:$pass_hash"; + else + return false; + } } |