diff --git a/app/Models/Setting.php b/app/Models/Setting.php index 212f8364..da6f0cda 100644 --- a/app/Models/Setting.php +++ b/app/Models/Setting.php @@ -15,6 +15,10 @@ class Setting extends NexusModel public static array $permissionMustHaveClass = ['defaultclass', 'staffmem']; + const CLASS_PERMISSION_SET_KEY_PREFIX = 'nexus_class_permissions_'; + const DIRECT_PERMISSION_SET_KEY_PREFIX = 'nexus_direct_permissions_'; + const ROLE_PERMISSION_SET_KEY_PREFIX = 'nexus_role_permissions_'; + /** * get setting autoload = yes with cache * diff --git a/app/Repositories/ToolRepository.php b/app/Repositories/ToolRepository.php index 204063dd..79e7ca65 100644 --- a/app/Repositories/ToolRepository.php +++ b/app/Repositories/ToolRepository.php @@ -9,6 +9,7 @@ use App\Models\Setting; use App\Models\User; use Carbon\Carbon; use Illuminate\Support\Arr; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Storage; use Nexus\Database\NexusDB; use Nexus\Plugin\Plugin; @@ -367,21 +368,50 @@ class ToolRepository extends BaseRepository public static function listUserClassPermissions($uid): array { $userInfo = get_user_row($uid); - $prefix = "authority"; - return Setting::query() - ->where("name", "like", "$prefix.%") - ->where('value', '<=', $userInfo['class']) - ->where('value', '>=', User::CLASS_PEASANT) - ->pluck('name') - ->map(fn ($name) => str_replace("$prefix.", "", $name)) - ->toArray(); + $settings = Setting::get('authority'); + $result = []; + foreach ($settings as $permission => $minClass) { + if ($minClass >= User::CLASS_PEASANT && $minClass <= $userInfo['class']) { + $result[] = $permission; + } + } + return $result; } - public static function listUserAllPermissions($uid): array + public static function listUserAllPermissions($uid, $class = null): array { - return NexusDB::remember("user_{$uid}_permissions", 600, function () use ($uid) { - $classPermissions = self::listUserClassPermissions($uid); - return apply_filter('user_permissions', $classPermissions, $uid); - }); + static $uidPermissionsCached = []; + if (isset($uidPermissionsCached[$uid])) { + return $uidPermissionsCached[$uid]; + } + $log = "uid: $uid"; + if ($class === null) { + $userInfo = get_user_row($uid); + $class = $userInfo['class']; + } + $redis = NexusDB::redis(); + $setKeys = []; + //Class permission, use push mechanism, already prepared,see settings.php + $key = Setting::CLASS_PERMISSION_SET_KEY_PREFIX . $class; + $setKeys[] = $key; + + //Role permission, use push mechanism, already prepared, see plugin role saving + $setKeys = apply_filter("role_permission_set_keys", $setKeys, $uid); + + //Direct permission, use pull mechanism + $key = Setting::DIRECT_PERMISSION_SET_KEY_PREFIX . $uid; + $setKeys[] = $key; + if (!$redis->exists($key)) { + $log .= ", init direct permissions"; + /** @var Collection $userPermissions */ + $userPermissions = apply_filter("user_direct_permissions", $uid); + $userPermissionsArr = $userPermissions->pluck('permission')->toArray(); + $redis->sAddArray($key, $userPermissionsArr); + } + $allPermissions = $redis->sUnion($setKeys); + do_log("$log, allSetKeys: " . json_encode($setKeys) . ", allPermissions: " . json_encode($allPermissions)); + $result = array_combine($allPermissions, $allPermissions); + $uidPermissionsCached[$uid] = $result; + return $result; } } diff --git a/include/constants.php b/include/constants.php index 48e969e8..d641ae1c 100644 --- a/include/constants.php +++ b/include/constants.php @@ -1,6 +1,6 @@ get_value($cacheKey); $totalsm = \App\Repositories\MessageRepository::getStaffMessageCountCache($CURUSER['id'], 'total'); - - if ($totalsm == ""){ + if ($totalsm === false){ $totalsm = \App\Repositories\MessageRepository::countStaffMessage($CURUSER['id']); // $Cache->cache_value($cacheKey, $totalsm, 900); \App\Repositories\MessageRepository::updateStaffMessageCountCache($CURUSER['id'], 'total', $totalsm); @@ -2752,7 +2751,7 @@ if ($msgalert) // $nummessages = $Cache->get_value($cacheKey); $nummessages = \App\Repositories\MessageRepository::getStaffMessageCountCache($CURUSER['id'], 'new'); - if ($nummessages == ""){ + if ($nummessages === false){ $nummessages = \App\Repositories\MessageRepository::countStaffMessage($CURUSER['id'], 0); // $Cache->cache_value($cacheKey, $nummessages, 900); \App\Repositories\MessageRepository::updateStaffMessageCountCache($CURUSER['id'], 'new', $nummessages); @@ -3029,6 +3028,8 @@ function deletetorrent($id) { } sql_query("DELETE FROM hit_and_runs WHERE torrent_id = ".mysql_real_escape_string($id)); sql_query("DELETE FROM claims WHERE torrent_id = ".mysql_real_escape_string($id)); + do_action("delete_torrent", $id); + do_log("delete torrent: $id", "error"); unlink(getFullDirectory("$torrent_dir/$id.torrent")); } @@ -3717,8 +3718,8 @@ function get_username($id, $big = false, $link = true, $bold = true, $target = f $href = getSchemeAndHttpHost() . "/userdetails.php?id=$id"; $options = [ - 'uid' => $id, - 'with_role' => true, +// 'uid' => $id, +// 'with_role' => true, ]; $username = ($link == true ? "" . $username . "" : $username) . $pics . ($withtitle == true ? " (" . ($arr['title'] == "" ? get_user_class_name($arr['class'],false,true,true, $options) : "".htmlspecialchars($arr['title'])) . ")" : ""); @@ -5086,6 +5087,7 @@ function saveSetting($prefix, $nameAndValue, $autoload = 'yes') } $sql .= implode(",", $data) . " on duplicate key update value = values(value)"; \Nexus\Database\NexusDB::statement($sql); + do_action("nexus_setting_update", $prefix, $nameAndValue); } function getFullDirectory($dir) diff --git a/include/globalfunctions.php b/include/globalfunctions.php index 915f0be8..7ba494dd 100644 --- a/include/globalfunctions.php +++ b/include/globalfunctions.php @@ -710,12 +710,13 @@ function get_user_row($id) 'donoruntil', 'leechwarn', 'warned', 'title', 'downloadpos', 'parked', 'clientselect', 'showclienterror', ); $cacheKey = 'user_'.$id.'_content'; - $row = \Nexus\Database\NexusDB::remember($cacheKey, 900, function () use ($id, $neededColumns) { + $row = \Nexus\Database\NexusDB::remember($cacheKey, 3600, function () use ($id, $neededColumns) { $user = \App\Models\User::query()->with(['wearing_medals'])->find($id, $neededColumns); if (!$user) { return null; } $arr = $user->toArray(); + //Rainbow ID $userRep = new \App\Repositories\UserRepository(); $metas = $userRep->listMetas($id, \App\Models\UserMeta::META_KEY_PERSONALIZED_USERNAME); if ($metas->isNotEmpty()) { @@ -723,7 +724,7 @@ function get_user_row($id) } else { $arr['__is_rainbow'] = 0; } - return $arr; + return apply_filter("user_row", $arr); }); // if ($CURUSER && $id == $CURUSER['id']) { @@ -955,17 +956,19 @@ function clear_user_cache($uid, $passkey = '') { do_log("uid: $uid, passkey: $passkey"); \Nexus\Database\NexusDB::cache_del("user_{$uid}_content"); - \Nexus\Database\NexusDB::cache_del("user_{$uid}_permissions"); \Nexus\Database\NexusDB::cache_del("user_{$uid}_roles"); + \Nexus\Database\NexusDB::cache_del("announce_user_passkey_$uid");//announce.php + \Nexus\Database\NexusDB::cache_del(\App\Models\Setting::DIRECT_PERMISSION_SET_KEY_PREFIX . $uid); if ($passkey) { - \Nexus\Database\NexusDB::cache_del('user_passkey_'.$passkey.'_content'); + \Nexus\Database\NexusDB::cache_del('user_passkey_'.$passkey.'_content');//announce.php } } -function clear_setting_cache() +function clear_setting_cache($buildPermissionCache = false) { \Nexus\Database\NexusDB::cache_del('nexus_settings_in_laravel'); \Nexus\Database\NexusDB::cache_del('nexus_settings_in_nexus'); + } function clear_staff_message_cache() @@ -974,7 +977,26 @@ function clear_staff_message_cache() \App\Repositories\MessageRepository::updateStaffMessageCountCache(false); } -function user_can($permission, $fail = false, $uid = 0): bool +function build_class_permission_cache() +{ + $redis = \Nexus\Database\NexusDB::redis(); + $results = []; + $settings = get_setting_from_db("authority"); + foreach (\App\Models\User::$classes as $class => $info) { + foreach ($settings as $permission => $minClass) { + if ($class >= $minClass) { + $results[$class][] = $permission; + } + } + } + foreach ($results as $class => $permissions) { + $classKey = \App\Models\Setting::CLASS_PERMISSION_SET_KEY_PREFIX . $class; + $redis->del($classKey); + $redis->sAddArray($classKey, $permissions); + } +} + +function user_can($permission, $fail = false, $uid = 0, $class = null): bool { $log = "permission: $permission, fail: $fail, user: $uid"; static $userCanCached = []; @@ -983,21 +1005,24 @@ function user_can($permission, $fail = false, $uid = 0): bool $log .= ", set current uid: $uid"; } if ($uid <= 0) { - do_log("$log, no uid, false"); + do_log("$log, no uid, false", 'error'); return false; } if (!$fail && isset($userCanCached[$permission][$uid])) { return $userCanCached[$permission][$uid]; } - $userInfo = get_user_row($uid); - $log .= ", userClass: " . $userInfo['class']; - if ($userInfo['class'] == \App\Models\User::CLASS_STAFF_LEADER) { + if ($class === null) { + $userInfo = get_user_row($uid); + $class = $userInfo['class']; + } + $log .= ", userClass: $class"; + if ($class == \App\Models\User::CLASS_STAFF_LEADER) { do_log("$log, CLASS_STAFF_LEADER, true"); $userCanCached[$permission][$uid] = true; return true; } - $userAllPermissions = \App\Repositories\ToolRepository::listUserAllPermissions($uid); - $result = in_array($permission, $userAllPermissions); + $userAllPermissions = \App\Repositories\ToolRepository::listUserAllPermissions($uid, $class); + $result = isset($userAllPermissions[$permission]); $log .= ", userAllPermissions: " . json_encode($userAllPermissions) . ", result: $result"; if (!$fail || $result) { do_log($log); diff --git a/nexus/Install/Update.php b/nexus/Install/Update.php index b68e17e2..7a8d06d7 100644 --- a/nexus/Install/Update.php +++ b/nexus/Install/Update.php @@ -242,6 +242,11 @@ class Update extends Install NexusDB::cache_del('nexus_rss'); NexusDB::cache_del('nexus_is_ip_seed_box'); + /** + * @since 1.7.23 + */ + build_class_permission_cache(); + } public function runExtraMigrate() diff --git a/public/announce.php b/public/announce.php index db4a5ee3..299374e7 100644 --- a/public/announce.php +++ b/public/announce.php @@ -35,7 +35,7 @@ if (!empty($_REQUEST['authkey'])) { if (empty($decrypted)) { err('Invalid authkey'); } - $userInfo = \Nexus\Database\NexusDB::remember("announce_user_passkey_$uid", 600, function () use ($uid) { + $userInfo = \Nexus\Database\NexusDB::remember("announce_user_passkey_$uid", 3600, function () use ($uid) { return \App\Models\User::query()->where('id', $uid)->first(['id', 'passkey']); }); if (!$userInfo) { @@ -115,7 +115,7 @@ if (!$az = $Cache->get_value('user_passkey_'.$passkey.'_content')){ $res = sql_query("SELECT id, username, downloadpos, enabled, uploaded, downloaded, class, parked, clientselect, showclienterror, passkey, donor, donoruntil FROM users WHERE passkey=". sqlesc($passkey)." LIMIT 1"); $az = mysql_fetch_array($res); do_log("[check passkey], currentUser: " . nexus_json_encode($az)); - $Cache->cache_value('user_passkey_'.$passkey.'_content', $az, 950); + $Cache->cache_value('user_passkey_'.$passkey.'_content', $az, 3600); } if (!$az) err("Invalid passkey! Re-download the .torrent from $BASEURL"); $userid = intval($az['id'] ?? 0); @@ -173,8 +173,7 @@ if (!$torrent) { err("torrent not registered with this tracker"); } -//Do not use user_can(), some func user_can() used is not available IN_TRACKER -if ($az['class'] < $seebanned_class) { +if (!user_can('seebanned', false, $az['id'], $az['class'])) { if ($torrent['banned'] == 'yes') { err("torrent banned"); } elseif ($torrent['approval_status'] != \App\Models\Torrent::APPROVAL_STATUS_ALLOW && get_setting('torrent.approval_status_none_visible') == 'no') { diff --git a/public/settings.php b/public/settings.php index 054e050c..13d1e7b5 100644 --- a/public/settings.php +++ b/public/settings.php @@ -237,7 +237,7 @@ elseif ($action == 'savesettings_authority') // save user authority saveSetting('authority', $AUTHORITY); $actiontime = date("F j, Y, g:i a"); write_log("Tracker USER AUTHORITY settings updated by {$CURUSER['username']}. $actiontime",'mod'); - do_action("nexus_setting_update", $AUTHORITY); + build_class_permission_cache(); go_back(); } elseif ($action == 'savesettings_tweak') // save tweak diff --git a/public/usercp.php b/public/usercp.php index b85a4b3b..813120da 100644 --- a/public/usercp.php +++ b/public/usercp.php @@ -815,6 +815,7 @@ EOD; $to .= "&password=1"; if ($privacyupdated == 1) $to .= "&privacy=1"; + clear_user_cache($CURUSER["id"]); header("Location: $to"); } stdhead($lang_usercp['head_control_panel'].$lang_usercp['head_security_settings']);