diff options
author | Andrew Dolgov <[email protected]> | 2022-06-10 13:39:00 +0300 |
---|---|---|
committer | Andrew Dolgov <[email protected]> | 2022-06-10 13:39:00 +0300 |
commit | cf1eaeedf3026948cf2210af1c747bdc3522d6ff (patch) | |
tree | 31780f12fb075b6b37be7a8f2fb813ba9db4980a /classes | |
parent | 2975c7297b680e486f326939b9fba82d8cf18035 (diff) |
* add UserHelper methods to manipulate user database (add, modify, delete)
* expose said methods via CLI (update.php)
* fix several invocations of deprecated functions
* set stricter type hints on several method arguments
Diffstat (limited to 'classes')
-rw-r--r-- | classes/debug.php | 20 | ||||
-rwxr-xr-x | classes/feeds.php | 2 | ||||
-rwxr-xr-x | classes/handler/public.php | 14 | ||||
-rw-r--r-- | classes/prefs.php | 6 | ||||
-rw-r--r-- | classes/userhelper.php | 114 |
5 files changed, 141 insertions, 15 deletions
diff --git a/classes/debug.php b/classes/debug.php index fbdf260e0..4777e8c74 100644 --- a/classes/debug.php +++ b/classes/debug.php @@ -68,9 +68,9 @@ class Debug { } /** - * @param int $level Debug::LOG_* + * @param Debug::LOG_* $level */ - public static function set_loglevel(int $level): void { + public static function set_loglevel($level): void { self::$loglevel = $level; } @@ -82,7 +82,21 @@ class Debug { } /** - * @param int $level Debug::LOG_* + * @param int $level integer loglevel value + * @return Debug::LOG_* if valid, warn and return LOG_DISABLED otherwise + */ + public static function map_loglevel(int $level) : int { + if (in_array($level, self::ALL_LOG_LEVELS)) { + /** @phpstan-ignore-next-line */ + return $level; + } else { + user_error("Passed invalid debug log level: $level", E_USER_WARNING); + return self::LOG_DISABLED; + } + } + + /** + * @param Debug::LOG_* $level log level */ public static function log(string $message, int $level = Debug::LOG_NORMAL): bool { diff --git a/classes/feeds.php b/classes/feeds.php index a06486883..197caeedc 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -665,7 +665,7 @@ class Feeds extends Handler_Protected { } Debug::set_enabled(true); - Debug::set_loglevel($xdebug); + Debug::set_loglevel(Debug::map_loglevel($xdebug)); $feed_id = (int)$_REQUEST["feed_id"]; $do_update = ($_REQUEST["action"] ?? "") == "do_update"; diff --git a/classes/handler/public.php b/classes/handler/public.php index 3fef4c2b9..499cf8db2 100755 --- a/classes/handler/public.php +++ b/classes/handler/public.php @@ -76,7 +76,7 @@ class Handler_Public extends Handler { "/public.php?op=rss&id=$feed&key=" . Feeds::_get_access_key($feed, false, $owner_uid); - if (!$feed_site_url) $feed_site_url = get_self_url_prefix(); + if (!$feed_site_url) $feed_site_url = Config::get_self_url(); if ($format == 'atom') { $tpl = new Templator(); @@ -87,7 +87,7 @@ class Handler_Public extends Handler { $tpl->setVariable('VERSION', Config::get_version(), true); $tpl->setVariable('FEED_URL', htmlspecialchars($feed_self_url), true); - $tpl->setVariable('SELF_URL', htmlspecialchars(get_self_url_prefix()), true); + $tpl->setVariable('SELF_URL', htmlspecialchars(Config::get_self_url()), true); while ($line = $result->fetch()) { $line["content_preview"] = Sanitizer::sanitize(truncate_string(strip_tags($line["content"]), 100, '...')); @@ -134,7 +134,7 @@ class Handler_Public extends Handler { $tpl->setVariable('ARTICLE_AUTHOR', htmlspecialchars($line['author']), true); - $tpl->setVariable('ARTICLE_SOURCE_LINK', htmlspecialchars($line['site_url'] ? $line["site_url"] : get_self_url_prefix()), true); + $tpl->setVariable('ARTICLE_SOURCE_LINK', htmlspecialchars($line['site_url'] ? $line["site_url"] : Config::get_self_url()), true); $tpl->setVariable('ARTICLE_SOURCE_TITLE', htmlspecialchars($line['feed_title'] ?? $feed_title), true); foreach ($line["tags"] as $tag) { @@ -312,7 +312,7 @@ class Handler_Public extends Handler { $login, $user_id); if (!$redirect_url) - $redirect_url = get_self_url_prefix() . "/index.php"; + $redirect_url = Config::get_self_url() . "/index.php"; header("Location: " . $redirect_url); } else { @@ -389,11 +389,11 @@ class Handler_Public extends Handler { if (UserHelper::authenticate($login, $password)) { $_POST["password"] = ""; - if (get_schema_version() >= 120) { + if (Config::get_schema_version() >= 120) { $_SESSION["language"] = get_pref(Prefs::USER_LANGUAGE, $_SESSION["uid"]); } - $_SESSION["ref_schema_version"] = get_schema_version(); + $_SESSION["ref_schema_version"] = Config::get_schema_version(); $_SESSION["bw_limit"] = !!clean($_POST["bw_limit"] ?? false); $_SESSION["safe_mode"] = $safe_mode; @@ -563,7 +563,7 @@ class Handler_Public extends Handler { print_notice("Password reset instructions are being sent to your email address."); $resetpass_token = sha1(get_random_bytes(128)); - $resetpass_link = get_self_url_prefix() . "/public.php?op=forgotpass&hash=" . $resetpass_token . + $resetpass_link = Config::get_self_url() . "/public.php?op=forgotpass&hash=" . $resetpass_token . "&login=" . urlencode($login); $tpl = new Templator(); diff --git a/classes/prefs.php b/classes/prefs.php index 7e6033f4d..378fea293 100644 --- a/classes/prefs.php +++ b/classes/prefs.php @@ -230,7 +230,7 @@ class Prefs { } } - if (get_schema_version() >= 141) { + if (Config::get_schema_version() >= 141) { // fill in any overrides from the database $sth = $this->pdo->prepare("SELECT pref_name, value FROM ttrss_user_prefs2 WHERE owner_uid = :uid AND @@ -265,7 +265,7 @@ class Prefs { if ($this->_is_cached($pref_name, $owner_uid, $profile_id)) { $cached_value = $this->_get_cache($pref_name, $owner_uid, $profile_id); return Config::cast_to($cached_value, $type_hint); - } else if (get_schema_version() >= 141) { + } else if (Config::get_schema_version() >= 141) { $sth = $this->pdo->prepare("SELECT value FROM ttrss_user_prefs2 WHERE pref_name = :name AND owner_uid = :uid AND (profile = :profile OR (:profile IS NULL AND profile IS NULL))"); @@ -390,7 +390,7 @@ class Prefs { } function migrate(int $owner_uid, ?int $profile_id): void { - if (get_schema_version() < 141) + if (Config::get_schema_version() < 141) return; if (!$profile_id) $profile_id = null; diff --git a/classes/userhelper.php b/classes/userhelper.php index 91e40665d..caa32a36e 100644 --- a/classes/userhelper.php +++ b/classes/userhelper.php @@ -17,6 +17,15 @@ class UserHelper { self::HASH_ALGO_SHA1 ]; + const ACCESS_LEVELS = [ + self::ACCESS_LEVEL_DISABLED, + self::ACCESS_LEVEL_READONLY, + self::ACCESS_LEVEL_USER, + self::ACCESS_LEVEL_POWERUSER, + self::ACCESS_LEVEL_ADMIN, + self::ACCESS_LEVEL_KEEP_CURRENT + ]; + /** forbidden to login */ const ACCESS_LEVEL_DISABLED = -2; @@ -32,6 +41,23 @@ class UserHelper { /** has administrator permissions */ const ACCESS_LEVEL_ADMIN = 10; + /** used by self::user_modify() to keep current access level */ + const ACCESS_LEVEL_KEEP_CURRENT = -1024; + + /** + * @param int $level integer loglevel value + * @return UserHelper::ACCESS_LEVEL_* if valid, warn and return ACCESS_LEVEL_KEEP_CURRENT otherwise + */ + public static function map_access_level(int $level) : int { + if (in_array($level, self::ACCESS_LEVELS)) { + /** @phpstan-ignore-next-line */ + return $level; + } else { + user_error("Passed invalid user access level: $level", E_USER_WARNING); + return self::ACCESS_LEVEL_KEEP_CURRENT; + } + } + static function authenticate(string $login = null, string $password = null, bool $check_only = false, string $service = null): bool { if (!Config::get(Config::SINGLE_USER_MODE)) { $user_id = false; @@ -133,7 +159,7 @@ class UserHelper { if (empty($_SESSION["uid"])) { if (Config::get(Config::AUTH_AUTO_LOGIN) && self::authenticate(null, null)) { - $_SESSION["ref_schema_version"] = get_schema_version(); + $_SESSION["ref_schema_version"] = Config::get_schema_version(); } else { self::authenticate(null, null, true); } @@ -217,6 +243,7 @@ class UserHelper { return substr(bin2hex(get_random_bytes(125)), 0, 250); } + /** TODO: this should invoke UserHelper::user_modify() */ static function reset_password(int $uid, bool $format_output = false, string $new_password = ""): void { $user = ORM::for_table('ttrss_users')->find_one($uid); @@ -380,4 +407,89 @@ class UserHelper { else return false; } + + /** + * @param string $login Login for new user (case-insensitive) + * @param string $password Password for new user (may not be blank) + * @param UserHelper::ACCESS_LEVEL_* $access_level Access level for new user + * @return bool true if user has been created + */ + static function user_add(string $login, string $password, int $access_level) : bool { + $login = clean($login); + + if ($login && + $password && + !self::find_user_by_login($login) && + self::map_access_level((int)$access_level) != self::ACCESS_LEVEL_KEEP_CURRENT) { + + $user = ORM::for_table('ttrss_users')->create(); + + $user->salt = self::get_salt(); + $user->login = mb_strtolower($login); + $user->pwd_hash = self::hash_password($password, $user->salt); + $user->access_level = $access_level; + $user->created = Db::NOW(); + + return $user->save(); + } + + return false; + } + + /** + * @param int $uid User ID to modify + * @param string $new_password set password to this value if its not blank + * @param UserHelper::ACCESS_LEVEL_* $access_level set user access level to this value if it is set (default ACCESS_LEVEL_KEEP_CURRENT) + * @return bool true if user record has been saved + * + * NOTE: $access_level is of mixed type because of intellephense + */ + static function user_modify(int $uid, string $new_password = '', $access_level = self::ACCESS_LEVEL_KEEP_CURRENT) : bool { + $user = ORM::for_table('ttrss_users')->find_one($uid); + + if ($user) { + if ($new_password != '') { + $new_salt = self::get_salt(); + $pwd_hash = self::hash_password($new_password, $new_salt, self::HASH_ALGOS[0]); + + $user->pwd_hash = $pwd_hash; + $user->salt = $new_salt; + } + + if ($access_level != self::ACCESS_LEVEL_KEEP_CURRENT) { + $user->access_level = (int)$access_level; + } + + return $user->save(); + } + + return false; + } + + /** + * @param int $uid user ID to delete (this won't delete built-in admin user with UID 1) + * @return bool true if user has been deleted + */ + static function user_delete(int $uid) : bool { + if ($uid != 1) { + + $user = ORM::for_table('ttrss_users')->find_one($uid); + + if ($user) { + // TODO: is it still necessary to split those queries? + + ORM::for_table('ttrss_tags') + ->where('owner_uid', $uid) + ->delete_many(); + + ORM::for_table('ttrss_feeds') + ->where('owner_uid', $uid) + ->delete_many(); + + return $user->delete(); + } + } + + return false; + } } |