From 40558ceb39c09838c20335165f3b9d17b234151d Mon Sep 17 00:00:00 2001 From: flashwave Date: Wed, 30 Aug 2023 23:56:30 +0000 Subject: [PATCH] Added targeted permission recalculation. Reduces reliance on full recalculation and actually makes it viable to do from within the browser. --- public-legacy/auth/register.php | 2 +- public-legacy/manage/users/role.php | 1 + public-legacy/manage/users/user.php | 8 +- public-legacy/settings/account.php | 2 +- src/Perms/Permissions.php | 113 ++++++++++++++++------------ 5 files changed, 74 insertions(+), 52 deletions(-) diff --git a/public-legacy/auth/register.php b/public-legacy/auth/register.php index cca5e09..5824807 100644 --- a/public-legacy/auth/register.php +++ b/public-legacy/auth/register.php @@ -101,7 +101,7 @@ while(!$restricted && !empty($register)) { $users->addRoles($userInfo, $defaultRoleInfo); $config->setString('users.newest', $userInfo->getId()); - $config->setBoolean('perms.needsRecalc', true); + $msz->getPerms()->precalculatePermissions($msz->getForum(), [$userInfo->getId()]); url_redirect('auth-login-welcome', ['username' => $userInfo->getName()]); return; diff --git a/public-legacy/manage/users/role.php b/public-legacy/manage/users/role.php index 68fc203..ddc955f 100644 --- a/public-legacy/manage/users/role.php +++ b/public-legacy/manage/users/role.php @@ -132,6 +132,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) { foreach($permsApply as $categoryName => $values) $perms->setPermissions($categoryName, $values['allow'], $values['deny'], roleInfo: $roleInfo); + // could target all users with the role but ech $msz->getConfig()->setBoolean('perms.needsRecalc', true); } diff --git a/public-legacy/manage/users/user.php b/public-legacy/manage/users/user.php index d93e7d0..df94e83 100644 --- a/public-legacy/manage/users/user.php +++ b/public-legacy/manage/users/user.php @@ -51,6 +51,7 @@ $canEditPerms = $canEdit && $canManagePerms; $permsInfos = $perms->getPermissionInfo(userInfo: $userInfo, categoryNames: Perm::INFO_FOR_USER); $permsLists = Perm::createList(Perm::LISTS_FOR_USER); +$permsNeedRecalc = false; if(CSRF::validateRequest() && $canEdit) { if(!empty($_POST['impersonate_user'])) { @@ -143,7 +144,7 @@ if(CSRF::validateRequest() && $canEdit) { $users->addRoles($userInfo, $addRoles); if(!empty($addRoles) || !empty($removeRoles)) - $msz->getConfig()->setBoolean('perms.needsRecalc', true); + $permsNeedRecalc = true; } if(!empty($_POST['user']) && is_array($_POST['user'])) { @@ -214,9 +215,12 @@ if(CSRF::validateRequest() && $canEdit) { foreach($permsApply as $categoryName => $values) $perms->setPermissions($categoryName, $values['allow'], $values['deny'], userInfo: $userInfo); - $msz->getConfig()->setBoolean('perms.needsRecalc', true); + $permsNeedRecalc = true; } + if($permsNeedRecalc) + $perms->precalculatePermissions($msz->getForum(), [$userInfo->getId()]); + url_redirect('manage-user', ['user' => $userInfo->getId()]); return; } diff --git a/public-legacy/settings/account.php b/public-legacy/settings/account.php index 4776b30..e8c54df 100644 --- a/public-legacy/settings/account.php +++ b/public-legacy/settings/account.php @@ -37,7 +37,7 @@ if(!$isRestricted && $isVerifiedRequest && !empty($_POST['role'])) { case 'leave': if($roleInfo->isLeavable()) { $users->removeRoles($userInfo, $roleInfo); - $msz->getConfig()->setBoolean('perms.needsRecalc', true); + $msz->getPerms()->precalculatePermissions($msz->getForum(), [$userInfo->getId()]); } else $errors[] = "You're not allow to leave this role, an administrator has to remove it for you."; break; diff --git a/src/Perms/Permissions.php b/src/Perms/Permissions.php index f5887f7..e50b7f3 100644 --- a/src/Perms/Permissions.php +++ b/src/Perms/Permissions.php @@ -225,35 +225,47 @@ class Permissions { return $sets; } - // precalculates all permissions for fast lookups, don't run this from the browser lol - // TODO: only recalc a subset of users (e.g. personal permission changes/role add/remove) - public function precalculatePermissions(Forum $forum): void { - self::precalculatePermissionsLog('Loading list of user IDs...'); - $userIds = []; - $result = $this->dbConn->query('SELECT user_id FROM msz_users'); - while($result->next()) - $userIds[] = $result->getString(0); + // precalculates all permissions for fast lookups + public function precalculatePermissions(Forum $forum, array $userIds = []): void { + $suppliedUsers = !empty($userIds); + $doGuest = !$suppliedUsers; - self::precalculatePermissionsLog('Clearing existing precalculations...'); - $this->dbConn->execute('TRUNCATE msz_perms_calculated'); + if($suppliedUsers) { + self::precalculatePermissionsLog('Removing calculations for given user ids...'); + $stmt = $this->cache->get('DELETE FROM msz_perms_calculated WHERE user_id = ?'); + foreach($userIds as $userId) { + $stmt->addParameter(1, $userId); + $stmt->execute(); + } + } else { + self::precalculatePermissionsLog('Loading list of user IDs...'); + $result = $this->dbConn->query('SELECT user_id FROM msz_users'); + while($result->next()) + $userIds[] = $result->getString(0); + + self::precalculatePermissionsLog('Clearing existing precalculations...'); + $this->dbConn->execute('TRUNCATE msz_perms_calculated'); + } self::precalculatePermissionsLog('Creating inserter statement...'); $insert = $this->cache->get('INSERT INTO msz_perms_calculated (user_id, forum_id, perms_category, perms_calculated) VALUES (?, ?, ?, ?)'); - self::precalculatePermissionsLog('Calculating guest permissions...'); - $result = $this->dbConn->query('SELECT perms_category, BIT_OR(perms_allow) & ~BIT_OR(perms_deny) FROM msz_perms WHERE forum_id IS NULL AND user_id IS NULL AND role_id IS NULL GROUP BY perms_category'); - $insert->addParameter(1, null); - $insert->addParameter(2, null); - while($result->next()) { - $category = $result->getString(0); - $perms = $result->getInteger(1); - if($perms === 0) - continue; + if($doGuest) { + self::precalculatePermissionsLog('Calculating guest permissions...'); + $result = $this->dbConn->query('SELECT perms_category, BIT_OR(perms_allow) & ~BIT_OR(perms_deny) FROM msz_perms WHERE forum_id IS NULL AND user_id IS NULL AND role_id IS NULL GROUP BY perms_category'); + $insert->addParameter(1, null); + $insert->addParameter(2, null); + while($result->next()) { + $category = $result->getString(0); + $perms = $result->getInteger(1); + if($perms === 0) + continue; - self::precalculatePermissionsLog('Inserting guest permissions for category %s with value %x...', $category, $perms); - $insert->addParameter(3, $category); - $insert->addParameter(4, $perms); - $insert->execute(); + self::precalculatePermissionsLog('Inserting guest permissions for category %s with value %x...', $category, $perms); + $insert->addParameter(3, $category); + $insert->addParameter(4, $perms); + $insert->execute(); + } } self::precalculatePermissionsLog('Calculating user permissions...'); @@ -285,39 +297,41 @@ class Permissions { self::precalculatePermissionsLog('Loading list of forum categories...'); $forumCats = $forum->getCategories(asTree: true); foreach($forumCats as $forumCat) - $this->precalculatePermissionsForForumCategory($insert, $userIds, $forumCat); + $this->precalculatePermissionsForForumCategory($insert, $userIds, $forumCat, $doGuest); self::precalculatePermissionsLog('Finished permission precalculations!'); } - private function precalculatePermissionsForForumCategory(IDbStatement $insert, array $userIds, object $forumCat, array $catIds = []): void { + private function precalculatePermissionsForForumCategory(IDbStatement $insert, array $userIds, object $forumCat, bool $doGuest, array $catIds = []): void { $catIds[] = $currentCatId = $forumCat->info->getId(); self::precalculatePermissionsLog('Precalcuting permissions for forum category #%s (%s)...', $currentCatId, implode(' <- ', $catIds)); - self::precalculatePermissionsLog('Calculating guest permission for forum category #%s...', $currentCatId); - $args = 0; - $stmt = $this->cache->get(sprintf( - 'SELECT perms_category, BIT_OR(perms_allow) & ~BIT_OR(perms_deny) FROM msz_perms WHERE forum_id IN (%s) AND user_id IS NULL AND role_id IS NULL GROUP BY perms_category', - DbTools::prepareListString($catIds) - )); - foreach($catIds as $catId) - $stmt->addParameter(++$args, $catId); - $stmt->execute(); + if($doGuest) { + self::precalculatePermissionsLog('Calculating guest permission for forum category #%s...', $currentCatId); + $args = 0; + $stmt = $this->cache->get(sprintf( + 'SELECT perms_category, BIT_OR(perms_allow) & ~BIT_OR(perms_deny) FROM msz_perms WHERE forum_id IN (%s) AND user_id IS NULL AND role_id IS NULL GROUP BY perms_category', + DbTools::prepareListString($catIds) + )); + foreach($catIds as $catId) + $stmt->addParameter(++$args, $catId); + $stmt->execute(); - $insert->reset(); - $insert->addParameter(1, null); - $insert->addParameter(2, $currentCatId); - $result = $stmt->getResult(); - while($result->next()) { - $category = $result->getString(0); - $perms = $result->getInteger(1); - if($perms === 0) - continue; + $insert->reset(); + $insert->addParameter(1, null); + $insert->addParameter(2, $currentCatId); + $result = $stmt->getResult(); + while($result->next()) { + $category = $result->getString(0); + $perms = $result->getInteger(1); + if($perms === 0) + continue; - self::precalculatePermissionsLog('Inserting guest permissions for category %s with value %x for forum category #%s...', $category, $perms, $currentCatId); - $insert->addParameter(3, $category); - $insert->addParameter(4, $perms); - $insert->execute(); + self::precalculatePermissionsLog('Inserting guest permissions for category %s with value %x for forum category #%s...', $category, $perms, $currentCatId); + $insert->addParameter(3, $category); + $insert->addParameter(4, $perms); + $insert->execute(); + } } $args = 0; @@ -352,10 +366,13 @@ class Permissions { } foreach($forumCat->children as $forumChild) - $this->precalculatePermissionsForForumCategory($insert, $userIds, $forumChild, $catIds); + $this->precalculatePermissionsForForumCategory($insert, $userIds, $forumChild, $doGuest, $catIds); } private static function precalculatePermissionsLog(string $fmt, ...$args): void { + if(!MSZ_CLI) + return; + echo DateTime::now()->format('[H:i:s.u] '); vprintf($fmt, $args); echo PHP_EOL;