* 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
This commit is contained in:
@@ -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 {
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user