diff options
Diffstat (limited to 'classes')
-rw-r--r-- | classes/pref/prefs.php | 103 | ||||
-rwxr-xr-x | classes/rpc.php | 2 | ||||
-rw-r--r-- | classes/userhelper.php | 79 |
3 files changed, 96 insertions, 88 deletions
diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php index ba63d76b3..9eb27eb85 100644 --- a/classes/pref/prefs.php +++ b/classes/pref/prefs.php @@ -1,4 +1,5 @@ <?php +use chillerlan\QRCode; class Pref_Prefs extends Handler_Protected { @@ -313,7 +314,7 @@ class Pref_Prefs extends Handler_Protected { $authenticator = false; } - $otp_enabled = $this->is_otp_enabled(); + $otp_enabled = UserHelper::is_otp_enabled($_SESSION["uid"]); if ($authenticator && method_exists($authenticator, "change_password")) { ?> @@ -406,20 +407,8 @@ class Pref_Prefs extends Handler_Protected { <?php } - private function is_otp_enabled() { - $sth = $this->pdo->prepare("SELECT otp_enabled FROM ttrss_users - WHERE id = ?"); - $sth->execute([$_SESSION["uid"]]); - - if ($row = $sth->fetch()) { - return sql_bool_to_bool($row["otp_enabled"]); - } - - return false; - } - private function index_auth_2fa() { - $otp_enabled = $this->is_otp_enabled(); + $otp_enabled = UserHelper::is_otp_enabled($_SESSION["uid"]); if ($_SESSION["auth_module"] == "auth_internal") { if ($otp_enabled) { @@ -469,14 +458,13 @@ class Pref_Prefs extends Handler_Protected { if (function_exists("imagecreatefromstring")) { print "<h3>" . __("Scan the following code by the Authenticator application or copy the key manually") . "</h3>"; - $csrf_token_hash = sha1($_SESSION["csrf_token"]); - print "<img alt='otp qr-code' src='backend.php?op=pref-prefs&method=otpqrcode&csrf_token_hash=$csrf_token_hash'>"; + print "<img src=".($this->_get_otp_qrcode_img()).">"; } else { print_error("PHP GD functions are required to generate QR codes."); print "<h3>" . __("Use the following OTP key with a compatible Authenticator application") . "</h3>"; } - $otp_secret = $this->otpsecret(); + $otp_secret = UserHelper::get_otp_secret($_SESSION["uid"]); ?> <form dojoType='dijit.form.Form'> @@ -990,90 +978,39 @@ class Pref_Prefs extends Handler_Protected { $_SESSION["prefs_show_advanced"] = !$_SESSION["prefs_show_advanced"]; } - function otpsecret() { - $sth = $this->pdo->prepare("SELECT salt, otp_enabled - FROM ttrss_users - WHERE id = ?"); - $sth->execute([$_SESSION['uid']]); + function _get_otp_qrcode_img() { + $secret = UserHelper::get_otp_secret($_SESSION["uid"]); + $login = UserHelper::get_login_by_id($_SESSION["uid"]); - if ($row = $sth->fetch()) { - $otp_enabled = sql_bool_to_bool($row["otp_enabled"]); + if ($secret && $login) { + $qrcode = new \chillerlan\QRCode\QRCode(); - if (!$otp_enabled) { - $base32 = new \OTPHP\Base32(); - $secret = $base32->encode(mb_substr(sha1($row["salt"]), 0, 12), false); + $otpurl = "otpauth://totp/".urlencode($login)."?secret=$secret&issuer=".urlencode("Tiny Tiny RSS"); - return $secret; - } + return $qrcode->render($otpurl); } return false; } - function otpqrcode() { - $csrf_token_hash = clean($_REQUEST["csrf_token_hash"]); - - if (sha1($_SESSION["csrf_token"]) === $csrf_token_hash) { - require_once "lib/phpqrcode/phpqrcode.php"; - - $sth = $this->pdo->prepare("SELECT login - FROM ttrss_users - WHERE id = ?"); - $sth->execute([$_SESSION['uid']]); - - if ($row = $sth->fetch()) { - $secret = $this->otpsecret(); - $login = $row['login']; - - if ($secret) { - QRcode::png("otpauth://totp/".urlencode($login). - "?secret=$secret&issuer=".urlencode("Tiny Tiny RSS")); - } - } - } else { - header("Content-Type: text/json"); - print Errors::to_json(Errors::E_UNAUTHORIZED); - } - } - function otpenable() { - $password = clean($_REQUEST["password"]); - $otp = clean($_REQUEST["otp"]); + $otp_check = clean($_REQUEST["otp"]); $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]); if ($authenticator->check_password($_SESSION["uid"], $password)) { - - $secret = $this->otpsecret(); - - if ($secret) { - - $base32 = new \OTPHP\Base32(); - - $topt = new \OTPHP\TOTP($secret); - - $otp_check = $topt->now(); - - if ($otp == $otp_check) { - $sth = $this->pdo->prepare("UPDATE ttrss_users - SET otp_enabled = true WHERE id = ?"); - - $sth->execute([$_SESSION['uid']]); - - print "OK"; - } else { - print "ERROR:".__("Incorrect one time password"); - } + if (UserHelper::enable_otp($_SESSION["uid"], $otp_check)) { + print "OK"; + } else { + print "ERROR:".__("Incorrect one time password"); } - } else { print "ERROR:".__("Incorrect password"); } - } - static function isdefaultpassword() { + static function _is_default_password() { $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]); if ($authenticator && @@ -1116,9 +1053,7 @@ class Pref_Prefs extends Handler_Protected { "message" => $message]); } - $sth = $this->pdo->prepare("UPDATE ttrss_users SET otp_enabled = false WHERE - id = ?"); - $sth->execute([$_SESSION['uid']]); + UserHelper::disable_otp($_SESSION["uid"]); print "OK"; } else { diff --git a/classes/rpc.php b/classes/rpc.php index 630ea50cb..016a3bd03 100755 --- a/classes/rpc.php +++ b/classes/rpc.php @@ -439,7 +439,7 @@ class RPC extends Handler_Protected { $params["default_view_limit"] = (int) get_pref(Prefs::_DEFAULT_VIEW_LIMIT); $params["default_view_order_by"] = get_pref(Prefs::_DEFAULT_VIEW_ORDER_BY); $params["bw_limit"] = (int) $_SESSION["bw_limit"]; - $params["is_default_pw"] = Pref_Prefs::isdefaultpassword(); + $params["is_default_pw"] = Pref_Prefs::_is_default_password(); $params["label_base_index"] = LABEL_BASE_INDEX; $theme = get_pref(Prefs::USER_CSS_THEME); diff --git a/classes/userhelper.php b/classes/userhelper.php index ca673cf58..f366682ef 100644 --- a/classes/userhelper.php +++ b/classes/userhelper.php @@ -1,4 +1,6 @@ <?php +use OTPHP\TOTP; + class UserHelper { static function authenticate(string $login = null, string $password = null, bool $check_only = false, string $service = null) { @@ -141,14 +143,29 @@ class UserHelper { } - static function get_user_ip() { + static function get_user_ip() : string { foreach (["HTTP_X_REAL_IP", "REMOTE_ADDR"] as $hdr) { if (isset($_SERVER[$hdr])) return $_SERVER[$hdr]; } + + return null; + } + + static function get_login_by_id(int $id) : string { + $pdo = Db::pdo(); + + $sth = $pdo->prepare("SELECT login FROM ttrss_users WHERE id = ?"); + $sth->execute([$id]); + + if ($row = $sth->fetch()) { + return $row["login"]; + } + + return null; } - static function find_user_by_login(string $login) { + static function find_user_by_login(string $login) : int { $pdo = Db::pdo(); $sth = $pdo->prepare("SELECT id FROM ttrss_users WHERE @@ -159,7 +176,7 @@ class UserHelper { return $row["id"]; } - return false; + return null; } static function logout() { @@ -203,4 +220,60 @@ class UserHelper { } } + + 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 { + $sth = Db::pdo()->prepare("UPDATE ttrss_users SET otp_enabled = false WHERE id = ?"); + $sth->execute([$owner_uid]); + + return true; + } + + static function enable_otp(int $owner_uid, int $otp_check) : bool { + $secret = self::get_otp_secret($owner_uid); + + if ($secret) { + $otp = TOTP::create($secret); + + if ($otp->now() == $otp_check) { + $sth = Db::pdo()->prepare("UPDATE ttrss_users + SET otp_enabled = true WHERE id = ?"); + + $sth->execute([$owner_uid]); + + return true; + } + } + return false; + } + + + static function is_otp_enabled(int $owner_uid) : bool { + $sth = Db::pdo()->prepare("SELECT otp_enabled FROM ttrss_users WHERE id = ?"); + $sth->execute([$owner_uid]); + + if ($row = $sth->fetch()) { + return sql_bool_to_bool($row["otp_enabled"]); + } + + return false; + } + + static function get_otp_secret(int $owner_uid, bool $show_if_enabled = false) : string { + $sth = Db::pdo()->prepare("SELECT salt, otp_enabled FROM ttrss_users WHERE id = ?"); + $sth->execute([$owner_uid]); + + if ($row = $sth->fetch()) { + if (!sql_bool_to_bool($row["otp_enabled"]) || $show_if_enabled) { + return \ParagonIE\ConstantTime\Base32::encodeUpperUnpadded(mb_substr(sha1($row["salt"]), 0, 12)); + } + } + + return null; + } } |