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);
$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;

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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;