Added targeted permission recalculation.

Reduces reliance on full recalculation and actually makes it viable to do from within the browser.
This commit is contained in:
flash 2023-08-30 23:56:30 +00:00
parent f03c8ebfa5
commit 40558ceb39
5 changed files with 74 additions and 52 deletions

View file

@ -101,7 +101,7 @@ while(!$restricted && !empty($register)) {
$users->addRoles($userInfo, $defaultRoleInfo); $users->addRoles($userInfo, $defaultRoleInfo);
$config->setString('users.newest', $userInfo->getId()); $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()]); url_redirect('auth-login-welcome', ['username' => $userInfo->getName()]);
return; return;

View file

@ -132,6 +132,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
foreach($permsApply as $categoryName => $values) foreach($permsApply as $categoryName => $values)
$perms->setPermissions($categoryName, $values['allow'], $values['deny'], roleInfo: $roleInfo); $perms->setPermissions($categoryName, $values['allow'], $values['deny'], roleInfo: $roleInfo);
// could target all users with the role but ech
$msz->getConfig()->setBoolean('perms.needsRecalc', true); $msz->getConfig()->setBoolean('perms.needsRecalc', true);
} }

View file

@ -51,6 +51,7 @@ $canEditPerms = $canEdit && $canManagePerms;
$permsInfos = $perms->getPermissionInfo(userInfo: $userInfo, categoryNames: Perm::INFO_FOR_USER); $permsInfos = $perms->getPermissionInfo(userInfo: $userInfo, categoryNames: Perm::INFO_FOR_USER);
$permsLists = Perm::createList(Perm::LISTS_FOR_USER); $permsLists = Perm::createList(Perm::LISTS_FOR_USER);
$permsNeedRecalc = false;
if(CSRF::validateRequest() && $canEdit) { if(CSRF::validateRequest() && $canEdit) {
if(!empty($_POST['impersonate_user'])) { if(!empty($_POST['impersonate_user'])) {
@ -143,7 +144,7 @@ if(CSRF::validateRequest() && $canEdit) {
$users->addRoles($userInfo, $addRoles); $users->addRoles($userInfo, $addRoles);
if(!empty($addRoles) || !empty($removeRoles)) if(!empty($addRoles) || !empty($removeRoles))
$msz->getConfig()->setBoolean('perms.needsRecalc', true); $permsNeedRecalc = true;
} }
if(!empty($_POST['user']) && is_array($_POST['user'])) { if(!empty($_POST['user']) && is_array($_POST['user'])) {
@ -214,9 +215,12 @@ if(CSRF::validateRequest() && $canEdit) {
foreach($permsApply as $categoryName => $values) foreach($permsApply as $categoryName => $values)
$perms->setPermissions($categoryName, $values['allow'], $values['deny'], userInfo: $userInfo); $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()]); url_redirect('manage-user', ['user' => $userInfo->getId()]);
return; return;
} }

View file

@ -37,7 +37,7 @@ if(!$isRestricted && $isVerifiedRequest && !empty($_POST['role'])) {
case 'leave': case 'leave':
if($roleInfo->isLeavable()) { if($roleInfo->isLeavable()) {
$users->removeRoles($userInfo, $roleInfo); $users->removeRoles($userInfo, $roleInfo);
$msz->getConfig()->setBoolean('perms.needsRecalc', true); $msz->getPerms()->precalculatePermissions($msz->getForum(), [$userInfo->getId()]);
} else } else
$errors[] = "You're not allow to leave this role, an administrator has to remove it for you."; $errors[] = "You're not allow to leave this role, an administrator has to remove it for you.";
break; break;

View file

@ -225,35 +225,47 @@ class Permissions {
return $sets; return $sets;
} }
// precalculates all permissions for fast lookups, don't run this from the browser lol // precalculates all permissions for fast lookups
// TODO: only recalc a subset of users (e.g. personal permission changes/role add/remove) public function precalculatePermissions(Forum $forum, array $userIds = []): void {
public function precalculatePermissions(Forum $forum): void { $suppliedUsers = !empty($userIds);
self::precalculatePermissionsLog('Loading list of user IDs...'); $doGuest = !$suppliedUsers;
$userIds = [];
$result = $this->dbConn->query('SELECT user_id FROM msz_users');
while($result->next())
$userIds[] = $result->getString(0);
self::precalculatePermissionsLog('Clearing existing precalculations...'); if($suppliedUsers) {
$this->dbConn->execute('TRUNCATE msz_perms_calculated'); 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...'); self::precalculatePermissionsLog('Creating inserter statement...');
$insert = $this->cache->get('INSERT INTO msz_perms_calculated (user_id, forum_id, perms_category, perms_calculated) VALUES (?, ?, ?, ?)'); $insert = $this->cache->get('INSERT INTO msz_perms_calculated (user_id, forum_id, perms_category, perms_calculated) VALUES (?, ?, ?, ?)');
self::precalculatePermissionsLog('Calculating guest permissions...'); if($doGuest) {
$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'); self::precalculatePermissionsLog('Calculating guest permissions...');
$insert->addParameter(1, null); $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(2, null); $insert->addParameter(1, null);
while($result->next()) { $insert->addParameter(2, null);
$category = $result->getString(0); while($result->next()) {
$perms = $result->getInteger(1); $category = $result->getString(0);
if($perms === 0) $perms = $result->getInteger(1);
continue; if($perms === 0)
continue;
self::precalculatePermissionsLog('Inserting guest permissions for category %s with value %x...', $category, $perms); self::precalculatePermissionsLog('Inserting guest permissions for category %s with value %x...', $category, $perms);
$insert->addParameter(3, $category); $insert->addParameter(3, $category);
$insert->addParameter(4, $perms); $insert->addParameter(4, $perms);
$insert->execute(); $insert->execute();
}
} }
self::precalculatePermissionsLog('Calculating user permissions...'); self::precalculatePermissionsLog('Calculating user permissions...');
@ -285,39 +297,41 @@ class Permissions {
self::precalculatePermissionsLog('Loading list of forum categories...'); self::precalculatePermissionsLog('Loading list of forum categories...');
$forumCats = $forum->getCategories(asTree: true); $forumCats = $forum->getCategories(asTree: true);
foreach($forumCats as $forumCat) foreach($forumCats as $forumCat)
$this->precalculatePermissionsForForumCategory($insert, $userIds, $forumCat); $this->precalculatePermissionsForForumCategory($insert, $userIds, $forumCat, $doGuest);
self::precalculatePermissionsLog('Finished permission precalculations!'); 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(); $catIds[] = $currentCatId = $forumCat->info->getId();
self::precalculatePermissionsLog('Precalcuting permissions for forum category #%s (%s)...', $currentCatId, implode(' <- ', $catIds)); self::precalculatePermissionsLog('Precalcuting permissions for forum category #%s (%s)...', $currentCatId, implode(' <- ', $catIds));
self::precalculatePermissionsLog('Calculating guest permission for forum category #%s...', $currentCatId); if($doGuest) {
$args = 0; self::precalculatePermissionsLog('Calculating guest permission for forum category #%s...', $currentCatId);
$stmt = $this->cache->get(sprintf( $args = 0;
'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', $stmt = $this->cache->get(sprintf(
DbTools::prepareListString($catIds) '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); foreach($catIds as $catId)
$stmt->execute(); $stmt->addParameter(++$args, $catId);
$stmt->execute();
$insert->reset(); $insert->reset();
$insert->addParameter(1, null); $insert->addParameter(1, null);
$insert->addParameter(2, $currentCatId); $insert->addParameter(2, $currentCatId);
$result = $stmt->getResult(); $result = $stmt->getResult();
while($result->next()) { while($result->next()) {
$category = $result->getString(0); $category = $result->getString(0);
$perms = $result->getInteger(1); $perms = $result->getInteger(1);
if($perms === 0) if($perms === 0)
continue; continue;
self::precalculatePermissionsLog('Inserting guest permissions for category %s with value %x for forum category #%s...', $category, $perms, $currentCatId); self::precalculatePermissionsLog('Inserting guest permissions for category %s with value %x for forum category #%s...', $category, $perms, $currentCatId);
$insert->addParameter(3, $category); $insert->addParameter(3, $category);
$insert->addParameter(4, $perms); $insert->addParameter(4, $perms);
$insert->execute(); $insert->execute();
}
} }
$args = 0; $args = 0;
@ -352,10 +366,13 @@ class Permissions {
} }
foreach($forumCat->children as $forumChild) 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 { private static function precalculatePermissionsLog(string $fmt, ...$args): void {
if(!MSZ_CLI)
return;
echo DateTime::now()->format('[H:i:s.u] '); echo DateTime::now()->format('[H:i:s.u] ');
vprintf($fmt, $args); vprintf($fmt, $args);
echo PHP_EOL; echo PHP_EOL;