diff --git a/README.md b/README.md index 845cdba..5697f95 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,6 @@ > Misuzu can and will steal your lunch money. ## Requirements - - PHP 8.2 + - PHP 8.2 (64-bit) - MariaDB 10.6 - [Composer](https://getcomposer.org/) diff --git a/database/2023_08_30_213930_new_permissions_system.php b/database/2023_08_30_213930_new_permissions_system.php new file mode 100644 index 0000000..6697632 --- /dev/null +++ b/database/2023_08_30_213930_new_permissions_system.php @@ -0,0 +1,125 @@ +execute('DELETE FROM msz_config WHERE config_name = "perms.needsRecalc"'); + + $conn->execute(' + CREATE TABLE msz_perms ( + user_id INT(10) UNSIGNED NULL DEFAULT NULL, + role_id INT(10) UNSIGNED NULL DEFAULT NULL, + forum_id INT(10) UNSIGNED NULL DEFAULT NULL, + perms_category VARBINARY(64) NOT NULL, + perms_allow BIGINT(20) UNSIGNED NOT NULL, + perms_deny BIGINT(20) UNSIGNED NOT NULL, + UNIQUE KEY perms_unique (user_id, role_id, forum_id, perms_category), + KEY perms_user_foreign (user_id), + KEY perms_role_foreign (role_id), + KEY perms_forum_foreign (forum_id), + KEY perms_category_index (perms_category), + CONSTRAINT perms_user_foreign + FOREIGN KEY (user_id) + REFERENCES msz_users (user_id) + ON UPDATE CASCADE + ON DELETE CASCADE, + CONSTRAINT perms_role_foreign + FOREIGN KEY (role_id) + REFERENCES msz_roles (role_id) + ON UPDATE CASCADE + ON DELETE CASCADE, + CONSTRAINT perms_forum_foreign + FOREIGN KEY (forum_id) + REFERENCES msz_forum_categories (forum_id) + ON UPDATE CASCADE + ON DELETE CASCADE + ) ENGINE=InnoDB COLLATE=utf8mb4_bin + '); + + $conn->execute(' + ALTER TABLE msz_perms + ADD CONSTRAINT perms_53bit + CHECK (perms_allow >= 0 AND perms_deny >= 0 AND perms_allow <= 9007199254740991 AND perms_deny <= 9007199254740991), + ADD CONSTRAINT perms_only_user_or_role + CHECK ((user_id IS NULL AND role_id IS NULL) OR (user_id IS NULL AND role_id IS NOT NULL) OR (user_id IS NOT NULL AND role_id IS NULL)) + '); + + $conn->execute(' + CREATE TABLE msz_perms_calculated ( + user_id INT(10) UNSIGNED NULL DEFAULT NULL, + forum_id INT(10) UNSIGNED NULL DEFAULT NULL, + perms_category VARBINARY(64) NOT NULL, + perms_calculated BIGINT(20) UNSIGNED NOT NULL, + UNIQUE KEY perms_calculated_unique (user_id, forum_id, perms_category), + KEY perms_calculated_user_foreign (user_id), + KEY perms_calculated_forum_foreign (forum_id), + KEY perms_calculated_category_index (perms_category), + CONSTRAINT perms_calculated_user_foreign + FOREIGN KEY (user_id) + REFERENCES msz_users (user_id) + ON UPDATE CASCADE + ON DELETE CASCADE, + CONSTRAINT perms_calculated_forum_foreign + FOREIGN KEY (forum_id) + REFERENCES msz_forum_categories (forum_id) + ON UPDATE CASCADE + ON DELETE CASCADE + ) ENGINE=InnoDB COLLATE=utf8mb4_bin + '); + + $conn->execute(' + ALTER TABLE msz_perms_calculated + ADD CONSTRAINT perms_calculated_53bit + CHECK (perms_calculated >= 0 AND perms_calculated <= 9007199254740991) + '); + + $insert = $conn->prepare('INSERT INTO msz_perms (user_id, role_id, forum_id, perms_category, perms_allow, perms_deny) VALUES (?, ?, ?, ?, ?, ?)'); + + $result = $conn->query('SELECT user_id, role_id, general_perms_allow, general_perms_deny, user_perms_allow, user_perms_deny, changelog_perms_allow, changelog_perms_deny, news_perms_allow, news_perms_deny, forum_perms_allow, forum_perms_deny, comments_perms_allow, comments_perms_deny FROM msz_permissions'); + while($result->next()) { + $insert->addParameter(1, $result->isNull(0) ? null : $result->getString(0)); + $insert->addParameter(2, $result->isNull(1) ? null : $result->getString(1)); + $insert->addParameter(3, null); + $insert->addParameter(4, 'user'); + $insert->addParameter(5, $result->getInteger(4)); + $insert->addParameter(6, $result->getInteger(5)); + $insert->execute(); + + $allow = $result->getInteger(2); + $allow |= $result->getInteger(6) << 8; + $allow |= $result->getInteger(8) << 16; + $allow |= $result->getInteger(10) << 24; + $allow |= $result->getInteger(12) << 32; + + $deny = $result->getInteger(3); + $deny |= $result->getInteger(7) << 8; + $deny |= $result->getInteger(9) << 16; + $deny |= $result->getInteger(11) << 24; + $deny |= $result->getInteger(13) << 32; + + $insert->addParameter(4, 'global'); + $insert->addParameter(5, $allow); + $insert->addParameter(6, $deny); + $insert->execute(); + } + + $result = $conn->query('SELECT user_id, role_id, forum_id, forum_perms_allow, forum_perms_deny FROM msz_forum_permissions'); + while($result->next()) { + $insert->addParameter(1, $result->isNull(0) ? null : $result->getString(0)); + $insert->addParameter(2, $result->isNull(1) ? null : $result->getString(1)); + $insert->addParameter(3, $result->getString(2)); + $insert->addParameter(4, 'forum'); + $insert->addParameter(5, $result->getInteger(3)); + $insert->addParameter(6, $result->getInteger(4)); + $insert->execute(); + } + + $conn->execute('DROP TABLE msz_forum_permissions'); + $conn->execute('DROP TABLE msz_permissions'); + + // schedule recalc + $conn->execute('INSERT INTO msz_config (config_name, config_value) VALUES ("perms.needsRecalc", "b:1;")'); + } +} diff --git a/misuzu.php b/misuzu.php index 938ebe5..b726268 100644 --- a/misuzu.php +++ b/misuzu.php @@ -23,7 +23,6 @@ mb_internal_encoding('utf-8'); date_default_timezone_set('utc'); require_once MSZ_ROOT . '/utility.php'; -require_once MSZ_SOURCE . '/perms.php'; require_once MSZ_SOURCE . '/url.php'; $dbConfig = parse_ini_file(MSZ_CONFIG . '/config.ini', true, INI_SCANNER_TYPED); diff --git a/public-legacy/auth/login.php b/public-legacy/auth/login.php index c840ad8..6ebc58a 100644 --- a/public-legacy/auth/login.php +++ b/public-legacy/auth/login.php @@ -113,7 +113,7 @@ while(!empty($_POST['login']) && is_array($_POST['login'])) { if($userInfo->passwordNeedsRehash()) $users->updateUser($userInfo, password: $_POST['login']['password']); - if(!empty($loginPermCat) && $loginPermVal > 0 && !perms_check_user($loginPermCat, $userInfo->getId(), $loginPermVal)) { + if(!empty($loginPermCat) && $loginPermVal > 0 && !$msz->getPerms()->checkPermissions($loginPermCat, $loginPermVal, $userInfo)) { $notices[] = "Login succeeded, but you're not allowed to browse the site right now."; $loginAttempts->recordAttempt(true, $ipAddress, $countryCode, $userAgent, $clientInfo, $userInfo); break; diff --git a/public-legacy/comments.php b/public-legacy/comments.php index 83421e9..e31ba1e 100644 --- a/public-legacy/comments.php +++ b/public-legacy/comments.php @@ -30,7 +30,7 @@ if($msz->hasActiveBan()) { $currentUserInfo = $msz->getActiveUser(); $comments = $msz->getComments(); -$commentPerms = perms_for_comments($currentUserInfo->getId()); +$perms = $msz->getAuthInfo()->getPerms('global'); $commentId = (string)filter_input(INPUT_GET, 'c', FILTER_SANITIZE_NUMBER_INT); $commentMode = (string)filter_input(INPUT_GET, 'm'); @@ -55,7 +55,7 @@ if($commentMode !== 'create' && empty($commentInfo)) { switch($commentMode) { case 'pin': case 'unpin': - if(!$commentPerms['can_pin'] && !$categoryInfo->isOwner($currentUserInfo)) { + if(!$perms->check(Perm::G_COMMENTS_PIN) && !$categoryInfo->isOwner($currentUserInfo)) { echo render_info("You're not allowed to pin comments.", 403); break; } @@ -92,7 +92,7 @@ switch($commentMode) { break; case 'vote': - if(!$commentPerms['can_vote'] && !$categoryInfo->isOwner($currentUserInfo)) { + if(!$perms->check(Perm::G_COMMENTS_VOTE) && !$categoryInfo->isOwner($currentUserInfo)) { echo render_info("You're not allowed to vote on comments.", 403); break; } @@ -113,21 +113,23 @@ switch($commentMode) { break; case 'delete': - if(!$commentPerms['can_delete'] && !$categoryInfo->isOwner($currentUserInfo)) { + $canDelete = $perms->check(Perm::G_COMMENTS_DELETE_OWN | Perm::G_COMMENTS_DELETE_ANY); + if(!$canDelete && !$categoryInfo->isOwner($currentUserInfo)) { echo render_info("You're not allowed to delete comments.", 403); break; } + $canDeleteAny = $perms->check(Perm::G_COMMENTS_DELETE_ANY); if($commentInfo->isDeleted()) { echo render_info( - $commentPerms['can_delete_any'] ? 'This comment is already marked for deletion.' : "This comment doesn't exist.", + $canDeleteAny ? 'This comment is already marked for deletion.' : "This comment doesn't exist.", 400 ); break; } $isOwnComment = $commentInfo->getUserId() === $currentUserInfo->getId(); - $isModAction = $commentPerms['can_delete_any'] && !$isOwnComment; + $isModAction = $canDeleteAny && !$isOwnComment; if(!$isModAction && !$isOwnComment) { echo render_info("You're not allowed to delete comments made by others.", 403); @@ -150,7 +152,7 @@ switch($commentMode) { break; case 'restore': - if(!$commentPerms['can_delete_any']) { + if(!$perms->check(Perm::G_COMMENTS_DELETE_ANY)) { echo render_info("You're not allowed to restore deleted comments.", 403); break; } @@ -172,7 +174,7 @@ switch($commentMode) { break; case 'create': - if(!$commentPerms['can_comment'] && !$categoryInfo->isOwner($currentUserInfo)) { + if(!$perms->check(Perm::G_COMMENTS_CREATE) && !$categoryInfo->isOwner($currentUserInfo)) { echo render_info("You're not allowed to post comments.", 403); break; } @@ -192,15 +194,16 @@ switch($commentMode) { break; } - if($categoryInfo->isLocked() && !$commentPerms['can_lock']) { + $canLock = $perms->check(Perm::G_COMMENTS_LOCK); + if($categoryInfo->isLocked() && !$canLock) { echo render_info('This comment category has been locked.', 403); break; } $commentText = !empty($_POST['comment']['text']) && is_string($_POST['comment']['text']) ? $_POST['comment']['text'] : ''; $commentReply = (string)(!empty($_POST['comment']['reply']) && is_string($_POST['comment']['reply']) ? (int)$_POST['comment']['reply'] : 0); - $commentLock = !empty($_POST['comment']['lock']) && $commentPerms['can_lock']; - $commentPin = !empty($_POST['comment']['pin']) && $commentPerms['can_pin']; + $commentLock = !empty($_POST['comment']['lock']) && $canLock; + $commentPin = !empty($_POST['comment']['pin']) && $perms->check(Perm::G_COMMENTS_PIN); if($commentLock) { if($categoryInfo->isLocked()) @@ -212,7 +215,7 @@ switch($commentMode) { if(strlen($commentText) > 0) { $commentText = preg_replace("/[\r\n]{2,}/", "\n", $commentText); } else { - if($commentPerms['can_lock']) { + if($canLock) { echo render_info('The action has been processed.', 400); } else { echo render_info('Your comment is too short.', 400); diff --git a/public-legacy/forum/forum.php b/public-legacy/forum/forum.php index 9c397e0..87f30df 100644 --- a/public-legacy/forum/forum.php +++ b/public-legacy/forum/forum.php @@ -3,6 +3,7 @@ namespace Misuzu; use stdClass; use RuntimeException; +use Index\XArray; $forum = $msz->getForum(); $users = $msz->getUsers(); @@ -16,18 +17,18 @@ try { return; } +$perms = $msz->getAuthInfo()->getPerms('forum', $categoryInfo); + $currentUser = $msz->getActiveUser(); $currentUserId = $currentUser === null ? '0' : $currentUser->getId(); -$perms = forum_perms_get_user($categoryInfo->getId(), $currentUserId)[MSZ_FORUM_PERMS_GENERAL]; - -if(!perms_check($perms, MSZ_FORUM_PERM_VIEW_FORUM)) { +if(!$perms->check(Perm::F_CATEGORY_VIEW)) { echo render_error(403); return; } -if(isset($currentUser) && $msz->hasActiveBan($currentUser)) - $perms &= MSZ_FORUM_PERM_LIST_FORUM | MSZ_FORUM_PERM_VIEW_FORUM; +if($msz->hasActiveBan()) + $perms = $perms->apply(fn($calc) => $calc & (Perm::F_CATEGORY_LIST | Perm::F_CATEGORY_VIEW)); if($categoryInfo->isLink()) { if($categoryInfo->hasLinkTarget()) { @@ -40,7 +41,7 @@ if($categoryInfo->isLink()) { $forumPagination = new Pagination($forum->countTopics( categoryInfo: $categoryInfo, global: true, - deleted: perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST) ? null : false + deleted: $perms->check(Perm::F_POST_DELETE_ANY) ? null : false ), 20); if(!$forumPagination->hasValidOffset()) { @@ -56,9 +57,9 @@ $topics = []; if($categoryInfo->mayHaveChildren()) { $children = $forum->getCategoryChildren($categoryInfo, hidden: false, asTree: true); - foreach($children as $child) { - $childPerms = forum_perms_get_user($child->info->getId(), (int)$currentUserId)[MSZ_FORUM_PERMS_GENERAL]; - if(!perms_check($childPerms, MSZ_FORUM_PERM_LIST_FORUM)) { + foreach($children as $childId => $child) { + $childPerms = $msz->getAuthInfo()->getPerms('forum', $child->info); + if(!$childPerms->check(Perm::F_CATEGORY_LIST)) { unset($category->children[$childId]); continue; } @@ -67,8 +68,8 @@ if($categoryInfo->mayHaveChildren()) { if($child->info->mayHaveChildren()) { foreach($child->children as $grandChildId => $grandChild) { - $grandChildPerms = forum_perms_get_user($grandChild->info->getId(), (int)$currentUserId)[MSZ_FORUM_PERMS_GENERAL]; - if(!perms_check($grandChildPerms, MSZ_FORUM_PERM_LIST_FORUM)) { + $grandChildPerms = $msz->getAuthInfo()->getPerms('forum', $grandChild->info); + if(!$grandChildPerms->check(Perm::F_CATEGORY_LIST)) { unset($child->children[$grandChildId]); continue; } @@ -78,8 +79,8 @@ if($categoryInfo->mayHaveChildren()) { if($grandChild->info->mayHaveTopics()) { $catIds = [$grandChild->info->getId()]; foreach($grandChild->childIds as $greatGrandChildId) { - $greatGrandChildPerms = forum_perms_get_user($greatGrandChildId, (int)$currentUserId)[MSZ_FORUM_PERMS_GENERAL]; - if(perms_check($greatGrandChildPerms, MSZ_FORUM_PERM_LIST_FORUM)) + $greatGrandChildPerms = $msz->getAuthInfo()->getPerms('forum', $greatGrandChildId); + if(!$greatGrandChildPerms->check(Perm::F_CATEGORY_LIST)) $catIds[] = $greatGrandChildId; } @@ -96,8 +97,8 @@ if($categoryInfo->mayHaveChildren()) { if($child->info->mayHaveChildren() || $child->info->mayHaveTopics()) { $catIds = [$child->info->getId()]; foreach($child->childIds as $grandChildId) { - $grandChildPerms = forum_perms_get_user($grandChildId, (int)$currentUserId)[MSZ_FORUM_PERMS_GENERAL]; - if(perms_check($grandChildPerms, MSZ_FORUM_PERM_LIST_FORUM)) + $grandChildPerms = $msz->getAuthInfo()->getPerms('forum', $grandChildId); + if($grandChildPerms->check(Perm::F_CATEGORY_LIST)) $catIds[] = $grandChildId; } @@ -138,7 +139,7 @@ if($categoryInfo->mayHaveTopics()) { $topicInfos = $forum->getTopics( categoryInfo: $categoryInfo, global: true, - deleted: perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST) ? null : false, + deleted: $perms->check(Perm::F_POST_DELETE_ANY) ? null : false, pagination: $forumPagination, ); @@ -183,8 +184,8 @@ if($categoryInfo->mayHaveTopics()) { } } -$perms = perms_check_bulk($perms, [ - 'can_create_topic' => MSZ_FORUM_PERM_CREATE_TOPIC, +$perms = $perms->checkMany([ + 'can_create_topic' => Perm::F_TOPIC_CREATE, ]); Template::render('forum.forum', [ diff --git a/public-legacy/forum/index.php b/public-legacy/forum/index.php index 2fcc64a..61819f5 100644 --- a/public-legacy/forum/index.php +++ b/public-legacy/forum/index.php @@ -25,8 +25,8 @@ if($mode === 'mark') { : $forum->getCategoryChildren(parentInfo: $categoryId, includeSelf: true); foreach($categoryInfos as $categoryInfo) { - $perms = forum_perms_get_user($categoryInfo->getId(), (int)$currentUserId)[MSZ_FORUM_PERMS_GENERAL]; - if(perms_check($perms, MSZ_FORUM_PERM_LIST_FORUM)) + $perms = $msz->getAuthInfo()->getPerms('forum', $categoryInfo); + if($perms->check(Perm::F_CATEGORY_LIST)) $forum->updateUserReadCategory($userInfo, $categoryInfo); } @@ -55,8 +55,8 @@ $userColours = []; $categories = $forum->getCategories(hidden: false, asTree: true); foreach($categories as $categoryId => $category) { - $perms = forum_perms_get_user($category->info->getId(), (int)$currentUserId)[MSZ_FORUM_PERMS_GENERAL]; - if(!perms_check($perms, MSZ_FORUM_PERM_LIST_FORUM)) { + $perms = $msz->getAuthInfo()->getPerms('forum', $category->info); + if(!$perms->check(Perm::F_CATEGORY_LIST)) { unset($categories[$categoryId]); continue; } @@ -65,8 +65,8 @@ foreach($categories as $categoryId => $category) { if($category->info->mayHaveChildren()) foreach($category->children as $childId => $child) { - $childPerms = forum_perms_get_user($child->info->getId(), (int)$currentUserId)[MSZ_FORUM_PERMS_GENERAL]; - if(!perms_check($childPerms, MSZ_FORUM_PERM_LIST_FORUM)) { + $childPerms = $msz->getAuthInfo()->getPerms('forum', $child->info); + if(!$childPerms->check(Perm::F_CATEGORY_LIST)) { unset($category->children[$childId]); continue; } @@ -76,8 +76,8 @@ foreach($categories as $categoryId => $category) { if($category->info->isListing()) { if($child->info->mayHaveChildren()) { foreach($child->children as $grandChildId => $grandChild) { - $grandChildPerms = forum_perms_get_user($grandChild->info->getId(), (int)$currentUserId)[MSZ_FORUM_PERMS_GENERAL]; - if(!perms_check($grandChildPerms, MSZ_FORUM_PERM_LIST_FORUM)) { + $grandChildPerms = $msz->getAuthInfo()->getPerms('forum', $grandChild->info); + if(!$grandChildPerms->check(Perm::F_CATEGORY_LIST)) { unset($child->children[$grandChildId]); continue; } @@ -87,8 +87,8 @@ foreach($categories as $categoryId => $category) { if($grandChild->info->mayHaveTopics()) { $catIds = [$grandChild->info->getId()]; foreach($grandChild->childIds as $greatGrandChildId) { - $greatGrandChildPerms = forum_perms_get_user($greatGrandChildId, (int)$currentUserId)[MSZ_FORUM_PERMS_GENERAL]; - if(perms_check($greatGrandChildPerms, MSZ_FORUM_PERM_LIST_FORUM)) + $greatGrandChildPerms = $msz->getAuthInfo()->getPerms('forum', $greatGrandChildId); + if($greatGrandChildPerms->check(Perm::F_CATEGORY_LIST)) $catIds[] = $greatGrandChildId; } @@ -105,8 +105,8 @@ foreach($categories as $categoryId => $category) { if($child->info->mayHaveChildren() || $child->info->mayHaveTopics()) { $catIds = [$child->info->getId()]; foreach($child->childIds as $grandChildId) { - $grandChildPerms = forum_perms_get_user($grandChildId, (int)$currentUserId)[MSZ_FORUM_PERMS_GENERAL]; - if(perms_check($grandChildPerms, MSZ_FORUM_PERM_LIST_FORUM)) + $grandChildPerms = $msz->getAuthInfo()->getPerms('forum', $grandChildId); + if($grandChildPerms->check(Perm::F_CATEGORY_LIST)) $catIds[] = $grandChildId; } @@ -165,8 +165,8 @@ foreach($categories as $categoryId => $category) { if($category->info->mayHaveChildren() || $category->info->mayHaveTopics()) { $catIds = [$category->info->getId()]; foreach($category->childIds as $childId) { - $childPerms = forum_perms_get_user($childId, (int)$currentUserId)[MSZ_FORUM_PERMS_GENERAL]; - if(perms_check($childPerms, MSZ_FORUM_PERM_LIST_FORUM)) + $childPerms = $msz->getAuthInfo()->getPerms('forum', $childId); + if($childPerms->check(Perm::F_CATEGORY_LIST)) $catIds[] = $childId; } diff --git a/public-legacy/forum/leaderboard.php b/public-legacy/forum/leaderboard.php index fdd0e04..f780b67 100644 --- a/public-legacy/forum/leaderboard.php +++ b/public-legacy/forum/leaderboard.php @@ -3,7 +3,7 @@ namespace Misuzu; use RuntimeException; -if(!$msz->isLoggedIn() || !perms_check_user(MSZ_PERMS_FORUM, $msz->getActiveUser()->getId(), MSZ_PERM_FORUM_VIEW_LEADERBOARD)) { +if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_FORUM_LEADERBOARD_VIEW)) { echo render_error(403); return; } diff --git a/public-legacy/forum/post.php b/public-legacy/forum/post.php index d22c6e6..a893bc5 100644 --- a/public-legacy/forum/post.php +++ b/public-legacy/forum/post.php @@ -31,14 +31,14 @@ try { return; } -$perms = forum_perms_get_user($postInfo->getCategoryId(), $currentUserId)[MSZ_FORUM_PERMS_GENERAL]; +$perms = $msz->getAuthInfo()->getPerms('forum', $postInfo->getCategoryId()); -if(!perms_check($perms, MSZ_FORUM_PERM_VIEW_FORUM)) { +if(!$perms->check(Perm::F_CATEGORY_VIEW)) { echo render_error(403); return; } -$canDeleteAny = perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST); +$canDeleteAny = $perms->check(Perm::F_POST_DELETE_ANY); switch($postMode) { case 'delete': @@ -53,7 +53,7 @@ switch($postMode) { return; } - if(!perms_check($perms, MSZ_FORUM_PERM_DELETE_POST)) { + if(!$perms->check(Perm::F_POST_DELETE_OWN)) { echo render_info('You are not allowed to delete posts.', 403); return; } diff --git a/public-legacy/forum/posting.php b/public-legacy/forum/posting.php index 668952d..ca7dc40 100644 --- a/public-legacy/forum/posting.php +++ b/public-legacy/forum/posting.php @@ -121,12 +121,13 @@ if(empty($forumId)) { $hasCategoryInfo = true; } -$perms = forum_perms_get_user($categoryInfo->getId(), $currentUserId)[MSZ_FORUM_PERMS_GENERAL]; +$perms = $msz->getAuthInfo()->getPerms('forum', $categoryInfo); if($categoryInfo->isArchived() - || (isset($topicInfo) && $topicInfo->isLocked() && !perms_check($perms, MSZ_FORUM_PERM_LOCK_TOPIC)) - || !perms_check($perms, MSZ_FORUM_PERM_VIEW_FORUM | MSZ_FORUM_PERM_CREATE_POST) - || (!isset($topicInfo) && !perms_check($perms, MSZ_FORUM_PERM_CREATE_TOPIC))) { + || (isset($topicInfo) && $topicInfo->isLocked() && !$perms->check(Perm::F_TOPIC_LOCK)) + || !$perms->check(Perm::F_CATEGORY_VIEW) + || !$perms->check(Perm::F_POST_CREATE) + || (!isset($topicInfo) && !$perms->check(Perm::F_TOPIC_CREATE))) { echo render_error(403); return; } @@ -141,16 +142,16 @@ $topicTypes = []; if($mode === 'create' || $mode === 'edit') { $topicTypes['discussion'] = 'Normal discussion'; - if(perms_check($perms, MSZ_FORUM_PERM_STICKY_TOPIC)) + if($perms->check(Perm::F_TOPIC_STICKY)) $topicTypes['sticky'] = 'Sticky topic'; - if(perms_check($perms, MSZ_FORUM_PERM_ANNOUNCE_TOPIC)) + if($perms->check(Perm::F_TOPIC_ANNOUNCE_LOCAL)) $topicTypes['announce'] = 'Announcement'; - if(perms_check($perms, MSZ_FORUM_PERM_GLOBAL_ANNOUNCE_TOPIC)) + if($perms->check(Perm::F_TOPIC_ANNOUNCE_GLOBAL)) $topicTypes['global'] = 'Global Announcement'; } // edit mode stuff -if($mode === 'edit' && !perms_check($perms, $postInfo->getUserId() === $currentUserId ? MSZ_FORUM_PERM_EDIT_POST : MSZ_FORUM_PERM_EDIT_ANY_POST)) { +if($mode === 'edit' && !$perms->check($postInfo->getUserId() === $currentUserId ? Perm::F_POST_EDIT_OWN : Perm::F_POST_EDIT_ANY)) { echo render_error(403); return; } diff --git a/public-legacy/forum/topic.php b/public-legacy/forum/topic.php index 7a0a36f..8a8ae75 100644 --- a/public-legacy/forum/topic.php +++ b/public-legacy/forum/topic.php @@ -25,8 +25,8 @@ if($topicId < 1 && $postId > 0) { } $categoryId = $postInfo->getCategoryId(); - $perms = forum_perms_get_user($categoryId, $currentUserId)[MSZ_FORUM_PERMS_GENERAL]; - $canDeleteAny = !perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST); + $perms = $msz->getAuthInfo()->getPerms('forum', $postInfo->getCategoryId()); + $canDeleteAny = $perms->check(Perm::F_POST_DELETE_ANY); if($postInfo->isDeleted() && !$canDeleteAny) { echo render_error(404); @@ -53,13 +53,13 @@ if(!$topicIsNuked) { if($categoryId !== (int)$topicInfo->getCategoryId()) { $categoryId = (int)$topicInfo->getCategoryId(); - $perms = forum_perms_get_user($categoryId, $currentUserId)[MSZ_FORUM_PERMS_GENERAL]; + $perms = $msz->getAuthInfo()->getPerms('forum', $topicInfo->getCategoryId()); } - if(isset($currentUser) && $msz->hasActiveBan($currentUser)) - $perms &= MSZ_FORUM_PERM_LIST_FORUM | MSZ_FORUM_PERM_VIEW_FORUM; + if($msz->hasActiveBan()) + $perms = $perms->apply(fn($calc) => $calc & (Perm::F_CATEGORY_LIST | Perm::F_CATEGORY_VIEW)); - $canDeleteAny = perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST); + $canDeleteAny = $perms->check(Perm::F_POST_DELETE_ANY); } if(($topicIsNuked || $topicIsDeleted) && $forum->hasTopicRedirect($topicId)) { @@ -75,7 +75,7 @@ if(($topicIsNuked || $topicIsDeleted) && $forum->hasTopicRedirect($topicId)) { } } -if(!perms_check($perms, MSZ_FORUM_PERM_VIEW_FORUM)) { +if(!$perms->check(Perm::F_CATEGORY_VIEW)) { echo render_error(403); return; } @@ -89,9 +89,9 @@ $topicIsLocked = $topicInfo->isLocked(); $topicIsArchived = $categoryInfo->isArchived(); $topicPostsTotal = $topicInfo->getTotalPostsCount(); $topicIsFrozen = $topicIsArchived || $topicIsDeleted; -$canDeleteOwn = !$topicIsFrozen && !$topicIsLocked && perms_check($perms, MSZ_FORUM_PERM_DELETE_POST); -$canBumpTopic = !$topicIsFrozen && perms_check($perms, MSZ_FORUM_PERM_BUMP_TOPIC); -$canLockTopic = !$topicIsFrozen && perms_check($perms, MSZ_FORUM_PERM_LOCK_TOPIC); +$canDeleteOwn = !$topicIsFrozen && !$topicIsLocked && $perms->check(Perm::F_POST_DELETE_OWN); +$canBumpTopic = !$topicIsFrozen && $perms->check(Perm::F_TOPIC_BUMP); +$canLockTopic = !$topicIsFrozen && $perms->check(Perm::F_TOPIC_LOCK); $canNukeOrRestore = $canDeleteAny && $topicIsDeleted; $canDelete = !$topicIsDeleted && ( $canDeleteAny || ( @@ -304,7 +304,7 @@ if(!$topicPagination->hasValidOffset()) { $postInfos = $forum->getPosts( topicInfo: $topicInfo, - deleted: perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST) ? null : false, + deleted: $perms->check(Perm::F_POST_DELETE_ANY) ? null : false, pagination: $topicPagination, ); @@ -343,19 +343,19 @@ foreach($postInfos as $postInfo) { && $originalPostInfo->getUserId() === $postInfo->getUserId(); } -$canReply = !$topicIsArchived && !$topicIsLocked && !$topicIsDeleted && perms_check($perms, MSZ_FORUM_PERM_CREATE_POST); +$canReply = !$topicIsArchived && !$topicIsLocked && !$topicIsDeleted && $perms->check(Perm::F_POST_CREATE); if(!$forum->checkUserHasReadTopic($userInfo, $topicInfo)) $forum->incrementTopicView($topicInfo); $forum->updateUserReadTopic($currentUser, $topicInfo); -$perms = perms_check_bulk($perms, [ - 'can_create_post' => MSZ_FORUM_PERM_CREATE_POST, - 'can_edit_post' => MSZ_FORUM_PERM_EDIT_POST, - 'can_edit_any_post' => MSZ_FORUM_PERM_EDIT_ANY_POST, - 'can_delete_post' => MSZ_FORUM_PERM_DELETE_POST, - 'can_delete_any_post' => MSZ_FORUM_PERM_DELETE_ANY_POST, +$perms = $perms->checkMany([ + 'can_create_post' => Perm::F_POST_CREATE, + 'can_edit_post' => Perm::F_POST_EDIT_OWN, + 'can_edit_any_post' => Perm::F_POST_EDIT_ANY, + 'can_delete_post' => Perm::F_POST_DELETE_OWN, + 'can_delete_any_post' => Perm::F_POST_DELETE_ANY, ]); Template::render('forum.topic', [ diff --git a/public-legacy/manage/changelog/change.php b/public-legacy/manage/changelog/change.php index a7768da..948081f 100644 --- a/public-legacy/manage/changelog/change.php +++ b/public-legacy/manage/changelog/change.php @@ -7,7 +7,7 @@ use Index\DateTime; use Index\XArray; use Misuzu\Changelog\Changelog; -if(!$msz->isLoggedIn() || !perms_check_user(MSZ_PERMS_CHANGELOG, $msz->getActiveUser()->getId(), MSZ_PERM_CHANGELOG_MANAGE_CHANGES)) { +if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_CL_CHANGES_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/changelog/index.php b/public-legacy/manage/changelog/index.php index ca9c82f..69d64c3 100644 --- a/public-legacy/manage/changelog/index.php +++ b/public-legacy/manage/changelog/index.php @@ -3,7 +3,7 @@ namespace Misuzu; use RuntimeException; -if(!$msz->isLoggedIn() || !perms_check_user(MSZ_PERMS_CHANGELOG, $msz->getActiveUser()->getId(), MSZ_PERM_CHANGELOG_MANAGE_CHANGES)) { +if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_CL_CHANGES_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/changelog/tag.php b/public-legacy/manage/changelog/tag.php index abb957e..1199b44 100644 --- a/public-legacy/manage/changelog/tag.php +++ b/public-legacy/manage/changelog/tag.php @@ -3,7 +3,7 @@ namespace Misuzu; use RuntimeException; -if(!$msz->isLoggedIn() || !perms_check_user(MSZ_PERMS_CHANGELOG, $msz->getActiveUser()->getId(), MSZ_PERM_CHANGELOG_MANAGE_TAGS)) { +if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_CL_TAGS_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/changelog/tags.php b/public-legacy/manage/changelog/tags.php index 05e016d..0742adf 100644 --- a/public-legacy/manage/changelog/tags.php +++ b/public-legacy/manage/changelog/tags.php @@ -1,7 +1,7 @@ isLoggedIn() || !perms_check_user(MSZ_PERMS_CHANGELOG, $msz->getActiveUser()->getId(), MSZ_PERM_CHANGELOG_MANAGE_TAGS)) { +if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_CL_TAGS_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/forum/index.php b/public-legacy/manage/forum/index.php index 9c71392..19136ee 100644 --- a/public-legacy/manage/forum/index.php +++ b/public-legacy/manage/forum/index.php @@ -1,18 +1,24 @@ isLoggedIn() || !perms_check_user(MSZ_PERMS_GENERAL, $msz->getActiveUser()->getId(), MSZ_PERM_FORUM_MANAGE_FORUMS)) { +use Misuzu\Perm; + +if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_FORUM_CATEGORIES_MANAGE)) { echo render_error(403); return; } -$rawPerms = perms_create(MSZ_FORUM_PERM_MODES); -$perms = manage_forum_perms_list($rawPerms); +$perms = $msz->getPerms(); +$permsInfos = $perms->getPermissionInfo(categoryNames: Perm::INFO_FOR_FORUM_CATEGORY); +$permsLists = Perm::createList(Perm::LISTS_FOR_FORUM_CATEGORY); -if(!empty($_POST['perms']) && is_array($_POST['perms'])) { - $finalPerms = manage_perms_apply($perms, $_POST['perms'], $rawPerms); - $perms = manage_forum_perms_list($finalPerms); - Template::set('calculated_perms', $finalPerms); -} +if(filter_has_var(INPUT_POST, 'perms')) + Template::set('calculated_perms', Perm::convertSubmission( + filter_input(INPUT_POST, 'perms', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY), + Perm::INFO_FOR_FORUM_CATEGORY + )); -Template::render('manage.forum.listing', compact('perms')); +Template::render('manage.forum.listing', [ + 'perms_lists' => $permsLists, + 'perms_infos' => $permsInfos, +]); diff --git a/public-legacy/manage/forum/redirs.php b/public-legacy/manage/forum/redirs.php index a892a0f..06ddfec 100644 --- a/public-legacy/manage/forum/redirs.php +++ b/public-legacy/manage/forum/redirs.php @@ -1,7 +1,7 @@ isLoggedIn() || !perms_check_user(MSZ_PERMS_GENERAL, $msz->getActiveUser()->getId(), MSZ_PERM_FORUM_TOPIC_REDIRS)) { +if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_FORUM_TOPIC_REDIRS_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/general/emoticon.php b/public-legacy/manage/general/emoticon.php index 57ca1e5..b2d1bcd 100644 --- a/public-legacy/manage/general/emoticon.php +++ b/public-legacy/manage/general/emoticon.php @@ -4,7 +4,7 @@ namespace Misuzu; use RuntimeException; use Index\XArray; -if(!$msz->isLoggedIn() || !perms_check_user(MSZ_PERMS_GENERAL, $msz->getActiveUser()->getId(), MSZ_PERM_GENERAL_MANAGE_EMOTES)) { +if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_EMOTES_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/general/emoticons.php b/public-legacy/manage/general/emoticons.php index c8ed5c1..2a3a002 100644 --- a/public-legacy/manage/general/emoticons.php +++ b/public-legacy/manage/general/emoticons.php @@ -3,7 +3,7 @@ namespace Misuzu; use RuntimeException; -if(!$msz->isLoggedIn() || !perms_check_user(MSZ_PERMS_GENERAL, $msz->getActiveUser()->getId(), MSZ_PERM_GENERAL_MANAGE_EMOTES)) { +if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_EMOTES_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/general/logs.php b/public-legacy/manage/general/logs.php index f113124..66e2988 100644 --- a/public-legacy/manage/general/logs.php +++ b/public-legacy/manage/general/logs.php @@ -3,7 +3,7 @@ namespace Misuzu; use Misuzu\Pagination; -if(!$msz->isLoggedIn() || !perms_check_user(MSZ_PERMS_GENERAL, $msz->getActiveUser()->getId(), MSZ_PERM_GENERAL_VIEW_LOGS)) { +if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_LOGS_VIEW)) { echo render_error(403); return; } diff --git a/public-legacy/manage/general/setting-delete.php b/public-legacy/manage/general/setting-delete.php index 0d3224a..bd92db8 100644 --- a/public-legacy/manage/general/setting-delete.php +++ b/public-legacy/manage/general/setting-delete.php @@ -3,7 +3,7 @@ namespace Misuzu; use Misuzu\Config\CfgTools; -if(!$msz->isLoggedIn() || !perms_check_user(MSZ_PERMS_GENERAL, $msz->getActiveUser()->getId(), MSZ_PERM_GENERAL_MANAGE_CONFIG)) { +if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_CONFIG_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/general/setting.php b/public-legacy/manage/general/setting.php index 496b3f3..8354630 100644 --- a/public-legacy/manage/general/setting.php +++ b/public-legacy/manage/general/setting.php @@ -3,7 +3,7 @@ namespace Misuzu; use Misuzu\Config\DbConfig; -if(!$msz->isLoggedIn() || !perms_check_user(MSZ_PERMS_GENERAL, $msz->getActiveUser()->getId(), MSZ_PERM_GENERAL_MANAGE_CONFIG)) { +if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_CONFIG_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/general/settings.php b/public-legacy/manage/general/settings.php index e63eafe..4acaa7c 100644 --- a/public-legacy/manage/general/settings.php +++ b/public-legacy/manage/general/settings.php @@ -1,7 +1,7 @@ isLoggedIn() || !perms_check_user(MSZ_PERMS_GENERAL, $msz->getActiveUser()->getId(), MSZ_PERM_GENERAL_MANAGE_CONFIG)) { +if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_CONFIG_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/news/categories.php b/public-legacy/manage/news/categories.php index c348f5e..df1584a 100644 --- a/public-legacy/manage/news/categories.php +++ b/public-legacy/manage/news/categories.php @@ -1,7 +1,7 @@ isLoggedIn() || !perms_check_user(MSZ_PERMS_NEWS, $msz->getActiveUser()->getId(), MSZ_PERM_NEWS_MANAGE_CATEGORIES)) { +if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_NEWS_CATEGORIES_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/news/category.php b/public-legacy/manage/news/category.php index 502993a..98af6e1 100644 --- a/public-legacy/manage/news/category.php +++ b/public-legacy/manage/news/category.php @@ -3,7 +3,7 @@ namespace Misuzu; use RuntimeException; -if(!$msz->isLoggedIn() || !perms_check_user(MSZ_PERMS_NEWS, $msz->getActiveUser()->getId(), MSZ_PERM_NEWS_MANAGE_CATEGORIES)) { +if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_NEWS_CATEGORIES_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/news/post.php b/public-legacy/manage/news/post.php index bbb5b22..6f2a6f4 100644 --- a/public-legacy/manage/news/post.php +++ b/public-legacy/manage/news/post.php @@ -3,7 +3,7 @@ namespace Misuzu; use RuntimeException; -if(!$msz->isLoggedIn() || !perms_check_user(MSZ_PERMS_NEWS, $msz->getActiveUser()->getId(), MSZ_PERM_NEWS_MANAGE_POSTS)) { +if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_NEWS_POSTS_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/news/posts.php b/public-legacy/manage/news/posts.php index cd65f07..3183259 100644 --- a/public-legacy/manage/news/posts.php +++ b/public-legacy/manage/news/posts.php @@ -1,7 +1,7 @@ isLoggedIn() || !perms_check_user(MSZ_PERMS_NEWS, $msz->getActiveUser()->getId(), MSZ_PERM_NEWS_MANAGE_POSTS)) { +if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_NEWS_POSTS_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/users/ban.php b/public-legacy/manage/users/ban.php index a3bd208..6e724a1 100644 --- a/public-legacy/manage/users/ban.php +++ b/public-legacy/manage/users/ban.php @@ -5,7 +5,7 @@ use DateTimeInterface; use RuntimeException; use Index\DateTime; -if(!$msz->isLoggedIn() || !perms_check_user(MSZ_PERMS_USER, $msz->getActiveUser()->getId(), MSZ_PERM_USER_MANAGE_BANS)) { +if(!$msz->getAuthInfo()->getPerms('user')->check(Perm::U_BANS_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/users/bans.php b/public-legacy/manage/users/bans.php index a1b6a0b..3eb35c1 100644 --- a/public-legacy/manage/users/bans.php +++ b/public-legacy/manage/users/bans.php @@ -3,7 +3,7 @@ namespace Misuzu; use RuntimeException; -if(!$msz->isLoggedIn() || !perms_check_user(MSZ_PERMS_USER, $msz->getActiveUser()->getId(), MSZ_PERM_USER_MANAGE_BANS)) { +if(!$msz->getAuthInfo()->getPerms('user')->check(Perm::U_BANS_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/users/index.php b/public-legacy/manage/users/index.php index 89601d5..c7fb189 100644 --- a/public-legacy/manage/users/index.php +++ b/public-legacy/manage/users/index.php @@ -1,7 +1,7 @@ isLoggedIn() || !perms_check_user(MSZ_PERMS_USER, $msz->getActiveUser()->getId(), MSZ_PERM_USER_MANAGE_USERS)) { +if(!$msz->getAuthInfo()->getPerms('user')->check(Perm::U_USERS_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/users/note.php b/public-legacy/manage/users/note.php index 21f8d21..3bdb41f 100644 --- a/public-legacy/manage/users/note.php +++ b/public-legacy/manage/users/note.php @@ -3,7 +3,7 @@ namespace Misuzu; use RuntimeException; -if(!$msz->isLoggedIn() || !perms_check_user(MSZ_PERMS_USER, $msz->getActiveUser()->getId(), MSZ_PERM_USER_MANAGE_NOTES)) { +if(!$msz->getAuthInfo()->getPerms('user')->check(Perm::U_NOTES_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/users/notes.php b/public-legacy/manage/users/notes.php index 9199f61..1488869 100644 --- a/public-legacy/manage/users/notes.php +++ b/public-legacy/manage/users/notes.php @@ -3,7 +3,7 @@ namespace Misuzu; use RuntimeException; -if(!$msz->isLoggedIn() || !perms_check_user(MSZ_PERMS_USER, $msz->getActiveUser()->getId(), MSZ_PERM_USER_MANAGE_NOTES)) { +if(!$msz->getAuthInfo()->getPerms('user')->check(Perm::U_NOTES_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/users/role.php b/public-legacy/manage/users/role.php index 9304515..68fc203 100644 --- a/public-legacy/manage/users/role.php +++ b/public-legacy/manage/users/role.php @@ -4,14 +4,17 @@ namespace Misuzu; use RuntimeException; use Index\Colour\Colour; use Index\Colour\ColourRGB; +use Misuzu\Perm; -if(!$msz->isLoggedIn() || !perms_check_user(MSZ_PERMS_USER, $msz->getActiveUser()->getId(), MSZ_PERM_USER_MANAGE_ROLES)) { +$viewerPerms = $msz->getAuthInfo()->getPerms('user'); +if(!$viewerPerms->check(Perm::U_ROLES_MANAGE)) { echo render_error(403); return; } $users = $msz->getUsers(); $roles = $msz->getRoles(); +$perms = $msz->getPerms(); if(filter_has_var(INPUT_GET, 'r')) { $roleId = (string)filter_input(INPUT_GET, 'r', FILTER_SANITIZE_NUMBER_INT); @@ -26,10 +29,10 @@ if(filter_has_var(INPUT_GET, 'r')) { } else $isNew = true; $currentUser = $msz->getActiveUser(); -$canEditPerms = perms_check_user(MSZ_PERMS_USER, $currentUser->getId(), MSZ_PERM_USER_MANAGE_PERMS); +$canEditPerms = $viewerPerms->check(Perm::U_PERMS_MANAGE); -if($canEditPerms) - $permissions = manage_perms_list(perms_get_role_raw($roleId ?? 0)); +$permsInfos = $perms->getPermissionInfo(roleInfo: $roleInfo, categoryNames: Perm::INFO_FOR_ROLE); +$permsLists = Perm::createList(Perm::LISTS_FOR_ROLE); while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) { $userRank = $users->getUserRank($currentUser); @@ -120,27 +123,16 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) { [$roleInfo->getId()] ); - if(!empty($permissions) && !empty($_POST['perms']) && is_array($_POST['perms'])) { - $perms = manage_perms_apply($permissions, $_POST['perms']); + if($canEditPerms && filter_has_var(INPUT_POST, 'perms')) { + $permsApply = Perm::convertSubmission( + filter_input(INPUT_POST, 'perms', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY), + Perm::INFO_FOR_ROLE + ); - if($perms !== null) { - $permKeys = array_keys($perms); - $setPermissions = DB::prepare(' - REPLACE INTO `msz_permissions` (`role_id`, `user_id`, `' . implode('`, `', $permKeys) . '`) - VALUES (:role_id, NULL, :' . implode(', :', $permKeys) . ') - '); - $setPermissions->bind('role_id', $roleInfo->getId()); + foreach($permsApply as $categoryName => $values) + $perms->setPermissions($categoryName, $values['allow'], $values['deny'], roleInfo: $roleInfo); - foreach($perms as $key => $value) { - $setPermissions->bind($key, $value); - } - - $setPermissions->execute(); - } else { - $deletePermissions = DB::prepare('DELETE FROM `msz_permissions` WHERE `role_id` = :role_id AND `user_id` IS NULL'); - $deletePermissions->bind('role_id', $roleInfo->getId()); - $deletePermissions->execute(); - } + $msz->getConfig()->setBoolean('perms.needsRecalc', true); } url_redirect('manage-role', ['role' => $roleInfo->getId()]); @@ -150,6 +142,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) { Template::render('manage.users.role', [ 'role_new' => $isNew, 'role_info' => $roleInfo ?? null, - 'can_manage_perms' => $canEditPerms, - 'permissions' => $permissions ?? [], + 'can_edit_perms' => $canEditPerms, + 'perms_lists' => $permsLists, + 'perms_infos' => $permsInfos, ]); diff --git a/public-legacy/manage/users/roles.php b/public-legacy/manage/users/roles.php index c11652f..31574bc 100644 --- a/public-legacy/manage/users/roles.php +++ b/public-legacy/manage/users/roles.php @@ -1,7 +1,7 @@ isLoggedIn() || !perms_check_user(MSZ_PERMS_USER, $msz->getActiveUser()->getId(), MSZ_PERM_USER_MANAGE_ROLES)) { +if(!$msz->getAuthInfo()->getPerms('user')->check(Perm::U_ROLES_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/users/user.php b/public-legacy/manage/users/user.php index 1fcea86..eff398d 100644 --- a/public-legacy/manage/users/user.php +++ b/public-legacy/manage/users/user.php @@ -3,9 +3,11 @@ namespace Misuzu; use RuntimeException; use Index\Colour\Colour; +use Misuzu\Perm; use Misuzu\Auth\AuthTokenCookie; use Misuzu\Users\User; +$viewerPerms = $msz->getAuthInfo()->getPerms('user'); if(!$msz->isLoggedIn()) { echo render_error(403); return; @@ -13,15 +15,16 @@ if(!$msz->isLoggedIn()) { $users = $msz->getUsers(); $roles = $msz->getRoles(); +$perms = $msz->getPerms(); $currentUser = $msz->getActiveUser(); -$canManageUsers = perms_check_user(MSZ_PERMS_USER, $currentUser->getId(), MSZ_PERM_USER_MANAGE_USERS); -$canManagePerms = perms_check_user(MSZ_PERMS_USER, $currentUser->getId(), MSZ_PERM_USER_MANAGE_PERMS); -$canManageNotes = perms_check_user(MSZ_PERMS_USER, $currentUser->getId(), MSZ_PERM_USER_MANAGE_NOTES); -$canManageWarnings = perms_check_user(MSZ_PERMS_USER, $currentUser->getId(), MSZ_PERM_USER_MANAGE_WARNINGS); -$canManageBans = perms_check_user(MSZ_PERMS_USER, $currentUser->getId(), MSZ_PERM_USER_MANAGE_BANS); -$canImpersonate = perms_check_user(MSZ_PERMS_USER, $currentUser->getId(), MSZ_PERM_USER_IMPERSONATE); +$canManageUsers = $viewerPerms->check(Perm::U_USERS_MANAGE); +$canManagePerms = $viewerPerms->check(Perm::U_PERMS_MANAGE); +$canManageNotes = $viewerPerms->check(Perm::U_NOTES_MANAGE); +$canManageWarnings = $viewerPerms->check(Perm::U_WARNINGS_MANAGE); +$canManageBans = $viewerPerms->check(Perm::U_BANS_MANAGE); +$canImpersonate = $viewerPerms->check(Perm::U_CAN_IMPERSONATE); $canSendTestMail = $currentUser->isSuperUser(); $hasAccess = $canManageUsers || $canManageNotes || $canManageWarnings || $canManageBans; @@ -45,7 +48,9 @@ $userRank = $users->getUserRank($userInfo); $canEdit = $canManageUsers && ($currentUser->isSuperUser() || (string)$currentUser->getId() === $userInfo->getId() || $currentUserRank > $userRank); $canEditPerms = $canEdit && $canManagePerms; -$permissions = $canEditPerms ? manage_perms_list(perms_get_user_raw($userId)) : []; + +$permsInfos = $perms->getPermissionInfo(userInfo: $userInfo, categoryNames: Perm::INFO_FOR_USER); +$permsLists = Perm::createList(Perm::LISTS_FOR_USER); if(CSRF::validateRequest() && $canEdit) { if(!empty($_POST['impersonate_user'])) { @@ -136,11 +141,14 @@ if(CSRF::validateRequest() && $canEdit) { if(!empty($addRoles)) $users->addRoles($userInfo, $addRoles); + + if(!empty($addRoles) || !empty($removeRoles)) + $msz->getConfig()->setBoolean('perms.needsRecalc', true); } if(!empty($_POST['user']) && is_array($_POST['user'])) { - $setCountry = (string)($_POST['user']['country'] ?? ''); - $setTitle = (string)($_POST['user']['title'] ?? ''); + $setCountry = (string)($_POST['user']['country'] ?? ''); + $setTitle = (string)($_POST['user']['title'] ?? ''); $displayRole = (string)($_POST['user']['display_role'] ?? 0); if(!$users->hasRole($userInfo, $displayRole)) @@ -193,19 +201,16 @@ if(CSRF::validateRequest() && $canEdit) { } } - if($canEditPerms && !empty($_POST['perms']) && is_array($_POST['perms'])) { - $perms = manage_perms_apply($permissions, $_POST['perms']); + if($canEditPerms && filter_has_var(INPUT_POST, 'perms')) { + $permsApply = Perm::convertSubmission( + filter_input(INPUT_POST, 'perms', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY), + Perm::INFO_FOR_USER + ); - if($perms !== null) { - if(!perms_set_user_raw($userId, $perms)) - $notices[] = 'Failed to update permissions.'; - } else { - if(!perms_delete_user($userId)) - $notices[] = 'Failed to remove permissions.'; - } + foreach($permsApply as $categoryName => $values) + $perms->setPermissions($categoryName, $values['allow'], $values['deny'], userInfo: $userInfo); - // this smells, make it refresh/apply in a non-retarded way - $permissions = manage_perms_list(perms_get_user_raw($userId)); + $msz->getConfig()->setBoolean('perms.needsRecalc', true); } url_redirect('manage-user', ['user' => $userInfo->getId()]); @@ -227,5 +232,6 @@ Template::render('manage.users.user', [ 'can_manage_bans' => $canManageBans, 'can_impersonate' => $canImpersonate, 'can_send_test_mail' => $canSendTestMail, - 'permissions' => $permissions ?? [], + 'perms_lists' => $permsLists, + 'perms_infos' => $permsInfos, ]); diff --git a/public-legacy/manage/users/warning.php b/public-legacy/manage/users/warning.php index 623953b..9bfc6dc 100644 --- a/public-legacy/manage/users/warning.php +++ b/public-legacy/manage/users/warning.php @@ -3,7 +3,7 @@ namespace Misuzu; use RuntimeException; -if(!$msz->isLoggedIn() || !perms_check_user(MSZ_PERMS_USER, $msz->getActiveUser()->getId(), MSZ_PERM_USER_MANAGE_WARNINGS)) { +if(!$msz->getAuthInfo()->getPerms('user')->check(Perm::U_WARNINGS_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/manage/users/warnings.php b/public-legacy/manage/users/warnings.php index d022e9f..8694144 100644 --- a/public-legacy/manage/users/warnings.php +++ b/public-legacy/manage/users/warnings.php @@ -3,7 +3,7 @@ namespace Misuzu; use RuntimeException; -if(!$msz->isLoggedIn() || !perms_check_user(MSZ_PERMS_USER, $msz->getActiveUser()->getId(), MSZ_PERM_USER_MANAGE_WARNINGS)) { +if(!$msz->getAuthInfo()->getPerms('user')->check(Perm::U_WARNINGS_MANAGE)) { echo render_error(403); return; } diff --git a/public-legacy/members.php b/public-legacy/members.php index 293f025..9b74c45 100644 --- a/public-legacy/members.php +++ b/public-legacy/members.php @@ -67,8 +67,6 @@ if(empty($orderDir)) { return; } -$canManageUsers = perms_check_user(MSZ_PERMS_USER, $msz->getActiveUser()->getId(), MSZ_PERM_USER_MANAGE_USERS); - if($roleId === null) { $roleInfo = $roles->getDefaultRole(); } else { @@ -80,6 +78,7 @@ if($roleId === null) { } } +$canManageUsers = $msz->getAuthInfo()->getPerms('user')->check(Perm::U_USERS_MANAGE); $deleted = $canManageUsers ? null : false; $rolesAll = $roles->getRoles(hidden: false); diff --git a/public-legacy/profile.php b/public-legacy/profile.php index 49a807b..fd997a7 100644 --- a/public-legacy/profile.php +++ b/public-legacy/profile.php @@ -65,15 +65,15 @@ $notices = []; $userRank = $users->getUserRank($userInfo); $viewerRank = $viewingAsGuest ? 0 : $users->getUserRank($viewerInfo); +$viewerPerms = $msz->getAuthInfo()->getPerms('user'); + $activeBanInfo = $msz->tryGetActiveBan($userInfo); $isBanned = $activeBanInfo !== null; $profileFields = $msz->getProfileFields(); $viewingOwnProfile = (string)$viewerId === $userInfo->getId(); -$userPerms = perms_get_user($viewerId)[MSZ_PERMS_USER]; -$canManageWarnings = perms_check($userPerms, MSZ_PERM_USER_MANAGE_WARNINGS); +$canManageWarnings = $viewerPerms->check(Perm::U_WARNINGS_MANAGE); $canEdit = !$viewingAsGuest && ((!$isBanned && $viewingOwnProfile) || $viewerInfo->isSuperUser() || ( - perms_check($userPerms, MSZ_PERM_USER_MANAGE_USERS) - && ($viewingOwnProfile || $viewerRank > $userRank) + $viewerPerms->check(Perm::U_USERS_MANAGE) && ($viewingOwnProfile || $viewerRank > $userRank) )); $avatarInfo = new UserAvatarAsset($userInfo); $backgroundInfo = new UserBackgroundAsset($userInfo); @@ -84,13 +84,13 @@ if($isEditing) { return; } - $perms = perms_check_bulk($userPerms, [ - 'edit_profile' => MSZ_PERM_USER_EDIT_PROFILE, - 'edit_avatar' => MSZ_PERM_USER_CHANGE_AVATAR, - 'edit_background' => MSZ_PERM_USER_CHANGE_BACKGROUND, - 'edit_about' => MSZ_PERM_USER_EDIT_ABOUT, - 'edit_birthdate' => MSZ_PERM_USER_EDIT_BIRTHDATE, - 'edit_signature' => MSZ_PERM_USER_EDIT_SIGNATURE, + $perms = $viewerPerms->checkMany([ + 'edit_profile' => Perm::U_PROFILE_EDIT, + 'edit_avatar' => Perm::U_AVATAR_CHANGE, + 'edit_background' => PERM::U_PROFILE_BACKGROUND_CHANGE, + 'edit_about' => Perm::U_PROFILE_ABOUT_EDIT, + 'edit_birthdate' => Perm::U_PROFILE_BIRTHDATE_EDIT, + 'edit_signature' => Perm::U_FORUM_SIGNATURE_EDIT, ]); Template::set([ @@ -105,7 +105,7 @@ if($isEditing) { $profileFieldsSubmit = filter_input(INPUT_POST, 'profile', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY); if(!empty($profileFieldsSubmit)) { - if(!$perms['edit_profile']) { + if(!$perms->edit_profile) { $notices[] = 'You\'re not allowed to edit your profile'; } else { $profileFieldInfos = $profileFields->getFields(); @@ -139,7 +139,7 @@ if($isEditing) { } if(!empty($_POST['about']) && is_array($_POST['about'])) { - if(!$perms['edit_about']) { + if(!$perms->edit_about) { $notices[] = 'You\'re not allowed to edit your about page.'; } else { $aboutText = (string)($_POST['about']['text'] ?? ''); @@ -163,7 +163,7 @@ if($isEditing) { } if(!empty($_POST['signature']) && is_array($_POST['signature'])) { - if(!$perms['edit_signature']) { + if(!$perms->edit_signature) { $notices[] = 'You\'re not allowed to edit your forum signature.'; } else { $sigText = (string)($_POST['signature']['text'] ?? ''); @@ -187,7 +187,7 @@ if($isEditing) { } if(!empty($_POST['birthdate']) && is_array($_POST['birthdate'])) { - if(!$perms['edit_birthdate']) { + if(!$perms->edit_birthdate) { $notices[] = "You aren't allow to change your birthdate."; } else { $birthYear = (int)($_POST['birthdate']['year'] ?? 0); @@ -215,7 +215,7 @@ if($isEditing) { if(!empty($_POST['avatar']['delete'])) { $avatarInfo->delete(); } else { - if(!$perms['edit_avatar']) { + if(!$perms->edit_avatar) { $notices[] = 'You aren\'t allow to change your avatar.'; } elseif(!empty($_FILES['avatar']) && is_array($_FILES['avatar']) @@ -260,7 +260,7 @@ if($isEditing) { if((int)($_POST['background']['attach'] ?? -1) === 0) { $backgroundInfo->delete(); } else { - if(!$perms['edit_background']) { + if(!$perms->edit_background) { $notices[] = 'You aren\'t allow to change your background.'; } elseif(!empty($_FILES['background']) && is_array($_FILES['background'])) { if(!empty($_FILES['background']['name']['file'])) { diff --git a/public-legacy/search.php b/public-legacy/search.php index 715a27e..851a9a0 100644 --- a/public-legacy/search.php +++ b/public-legacy/search.php @@ -83,7 +83,7 @@ if(!empty($searchQuery)) { $forumCategoryIds = XArray::where( $forum->getCategories(hidden: false), - fn($categoryInfo) => $categoryInfo->mayHaveTopics() && forum_perms_check_user(MSZ_FORUM_PERMS_GENERAL, $categoryInfo->getId(), $currentUserId, MSZ_FORUM_PERM_VIEW_FORUM) + fn($categoryInfo) => $categoryInfo->mayHaveTopics() && $msz->getAuthInfo()->getPerms('forum', $categoryInfo)->check(Perm::F_CATEGORY_VIEW) ); $forumTopicInfos = $forum->getTopics(categoryInfo: $forumCategoryIds, deleted: false, searchQuery: $searchQueryEvaluated); diff --git a/public-legacy/settings/account.php b/public-legacy/settings/account.php index 277bca2..1ab6204 100644 --- a/public-legacy/settings/account.php +++ b/public-legacy/settings/account.php @@ -35,9 +35,10 @@ if(!$isRestricted && $isVerifiedRequest && !empty($_POST['role'])) { break; case 'leave': - if($roleInfo->isLeavable()) + if($roleInfo->isLeavable()) { $users->removeRoles($userInfo, $roleInfo); - else + $msz->getConfig()->setBoolean('perms.needsRecalc', true); + } else $errors[] = "You're not allow to leave this role, an administrator has to remove it for you."; break; } diff --git a/public-legacy/settings/data.php b/public-legacy/settings/data.php index 21edd72..b867c7a 100644 --- a/public-legacy/settings/data.php +++ b/public-legacy/settings/data.php @@ -125,14 +125,14 @@ if(isset($_POST['action']) && is_string($_POST['action'])) { $tmpFiles[] = db_to_zip($archive, $userInfo, 'comments_categories', ['category_id:s', 'category_name:s', 'owner_id:s:n', 'category_created:t', 'category_locked:t:n'], 'owner_id'); $tmpFiles[] = db_to_zip($archive, $userInfo, 'comments_posts', ['comment_id:s', 'category_id:s', 'user_id:s:n', 'comment_reply_to:s:n', 'comment_text:s', 'comment_created:t', 'comment_pinned:t:n', 'comment_edited:t:n', 'comment_deleted:t:n']); $tmpFiles[] = db_to_zip($archive, $userInfo, 'comments_votes', ['comment_id:s', 'user_id:s', 'comment_vote:i']); - $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_permissions', ['user_id:s:n', 'role_id:s:n', 'forum_id:s', 'forum_perms_allow:i', 'forum_perms_deny:i']); $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_posts', ['post_id:s', 'topic_id:s', 'forum_id:s', 'user_id:s:n', 'post_ip:a', 'post_text:s', 'post_parse:i', 'post_display_signature:b', 'post_created:t', 'post_edited:t:n', 'post_deleted:t:n']); $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_topics', ['topic_id:s', 'forum_id:s', 'user_id:s:n', 'topic_type:i', 'topic_title:s', 'topic_count_views:i', 'topic_created:t', 'topic_bumped:t', 'topic_deleted:t:n', 'topic_locked:t:n']); $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_topics_redirects', ['topic_id:s', 'user_id:s:n', 'topic_redir_url:s', 'topic_redir_created:t']); $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_topics_track', ['user_id:s', 'topic_id:s', 'forum_id:s', 'track_last_read:t']); $tmpFiles[] = db_to_zip($archive, $userInfo, 'login_attempts', ['user_id:s:n', 'attempt_success:b', 'attempt_ip:a', 'attempt_country:s', 'attempt_created:t', 'attempt_user_agent:s']); $tmpFiles[] = db_to_zip($archive, $userInfo, 'news_posts', ['post_id:s', 'category_id:s', 'user_id:s:n', 'comment_section_id:s:n', 'post_is_featured:b', 'post_title:s', 'post_text:s', 'post_scheduled:t', 'post_created:t', 'post_updated:t', 'post_deleted:t:n']); - $tmpFiles[] = db_to_zip($archive, $userInfo, 'permissions', ['user_id:s:n', 'role_id:s:n', 'general_perms_allow:i', 'general_perms_deny:i', 'user_perms_allow:i', 'user_perms_deny:i', 'changelog_perms_allow:i', 'changelog_perms_deny:i', 'news_perms_allow:i', 'news_perms_deny:i', 'forum_perms_allow:i', 'forum_perms_deny:i', 'comments_perms_allow:i', 'comments_perms_deny:i']); + $tmpFiles[] = db_to_zip($archive, $userInfo, 'perms', ['user_id:s:n', 'role_id:s:n', 'forum_id:s:n', 'perms_category:s', 'perms_allow:i', 'perms_deny:i']); + $tmpFiles[] = db_to_zip($archive, $userInfo, 'perms_calculated', ['user_id:s:n', 'forum_id:s:n', 'perms_category:s', 'perms_calculated:i']); $tmpFiles[] = db_to_zip($archive, $userInfo, 'profile_fields_values', ['field_id:s', 'user_id:s', 'format_id:s', 'field_value:s']); $tmpFiles[] = db_to_zip($archive, $userInfo, 'sessions', ['session_id:s', 'user_id:s', 'session_key:n', 'session_ip:a', 'session_ip_last:a:n', 'session_user_agent:s', 'session_country:s', 'session_expires:t', 'session_expires_bump:b', 'session_created:t', 'session_active:t:n']); $tmpFiles[] = db_to_zip($archive, $userInfo, 'users', ['user_id:s', 'username:s', 'password:n', 'email:s', 'register_ip:a', 'last_ip:a', 'user_super:b', 'user_country:s', 'user_colour:i:n', 'user_created:t', 'user_active:t:n', 'user_deleted:t:n', 'display_role:s:n', 'user_totp_key:n', 'user_about_content:s:n', 'user_about_parser:i', 'user_signature_content:s:n', 'user_signature_parser:i', 'user_birthdate:s:n', 'user_background_settings:i:n', 'user_title:s:n']); diff --git a/public/index.php b/public/index.php index 4ea1c26..456548a 100644 --- a/public/index.php +++ b/public/index.php @@ -188,8 +188,9 @@ if($inManageMode) { if($msz->isLoggedIn() && !$msz->hasActiveBan()) { $manageUser = $msz->getActiveUser(); $manageUserId = $manageUser->getId(); + $manageGlobalPerms = $msz->getAuthInfo()->getPerms('global'); - if(perms_check_user(MSZ_PERMS_GENERAL, $manageUserId, MSZ_PERM_GENERAL_CAN_MANAGE)) { + if($manageGlobalPerms->check(Perm::G_IS_JANITOR)) { $hasManageAccess = true; $manageMenu = [ 'General' => [ @@ -197,37 +198,38 @@ if($inManageMode) { ], ]; - if(perms_check_user(MSZ_PERMS_GENERAL, $manageUserId, MSZ_PERM_GENERAL_VIEW_LOGS)) + if($manageGlobalPerms->check(Perm::G_LOGS_VIEW)) $manageMenu['General']['Logs'] = url('manage-general-logs'); - if(perms_check_user(MSZ_PERMS_GENERAL, $manageUserId, MSZ_PERM_GENERAL_MANAGE_EMOTES)) + if($manageGlobalPerms->check(Perm::G_EMOTES_MANAGE)) $manageMenu['General']['Emoticons'] = url('manage-general-emoticons'); - if(perms_check_user(MSZ_PERMS_GENERAL, $manageUserId, MSZ_PERM_GENERAL_MANAGE_CONFIG)) + if($manageGlobalPerms->check(Perm::G_CONFIG_MANAGE)) $manageMenu['General']['Settings'] = url('manage-general-settings'); - if(perms_check_user(MSZ_PERMS_USER, $manageUserId, MSZ_PERM_USER_MANAGE_USERS)) + $manageUserPerms = $msz->getAuthInfo()->getPerms('user'); + if($manageUserPerms->check(Perm::U_USERS_MANAGE)) $manageMenu['Users & Roles']['Users'] = url('manage-users'); - if(perms_check_user(MSZ_PERMS_USER, $manageUserId, MSZ_PERM_USER_MANAGE_ROLES)) + if($manageUserPerms->check(Perm::U_ROLES_MANAGE)) $manageMenu['Users & Roles']['Roles'] = url('manage-roles'); - if(perms_check_user(MSZ_PERMS_USER, $manageUserId, MSZ_PERM_USER_MANAGE_NOTES)) + if($manageUserPerms->check(Perm::U_NOTES_MANAGE)) $manageMenu['Users & Roles']['Notes'] = url('manage-users-notes'); - if(perms_check_user(MSZ_PERMS_USER, $manageUserId, MSZ_PERM_USER_MANAGE_WARNINGS)) + if($manageUserPerms->check(Perm::U_WARNINGS_MANAGE)) $manageMenu['Users & Roles']['Warnings'] = url('manage-users-warnings'); - if(perms_check_user(MSZ_PERMS_USER, $manageUserId, MSZ_PERM_USER_MANAGE_BANS)) + if($manageUserPerms->check(Perm::U_BANS_MANAGE)) $manageMenu['Users & Roles']['Bans'] = url('manage-users-bans'); - if(perms_check_user(MSZ_PERMS_NEWS, $manageUserId, MSZ_PERM_NEWS_MANAGE_POSTS)) + if($manageGlobalPerms->check(Perm::G_NEWS_POSTS_MANAGE)) $manageMenu['News']['Posts'] = url('manage-news-posts'); - if(perms_check_user(MSZ_PERMS_NEWS, $manageUserId, MSZ_PERM_NEWS_MANAGE_CATEGORIES)) + if($manageGlobalPerms->check(Perm::G_NEWS_CATEGORIES_MANAGE)) $manageMenu['News']['Categories'] = url('manage-news-categories'); - if(perms_check_user(MSZ_PERMS_FORUM, $manageUserId, MSZ_PERM_FORUM_MANAGE_FORUMS)) + if($manageGlobalPerms->check(Perm::G_FORUM_CATEGORIES_MANAGE)) $manageMenu['Forum']['Permission Calculator'] = url('manage-forum-categories'); - if(perms_check_user(MSZ_PERMS_FORUM, $manageUserId, MSZ_PERM_FORUM_TOPIC_REDIRS)) + if($manageGlobalPerms->check(Perm::G_FORUM_TOPIC_REDIRS_MANAGE)) $manageMenu['Forum']['Topic Redirects'] = url('manage-forum-topic-redirs'); - if(perms_check_user(MSZ_PERMS_CHANGELOG, $manageUserId, MSZ_PERM_CHANGELOG_MANAGE_CHANGES)) + if($manageGlobalPerms->check(Perm::G_CL_CHANGES_MANAGE)) $manageMenu['Changelog']['Changes'] = url('manage-changelog-changes'); - if(perms_check_user(MSZ_PERMS_CHANGELOG, $manageUserId, MSZ_PERM_CHANGELOG_MANAGE_TAGS)) + if($manageGlobalPerms->check(Perm::G_CL_TAGS_MANAGE)) $manageMenu['Changelog']['Tags'] = url('manage-changelog-tags'); Template::set('manage_menu', $manageMenu); diff --git a/src/Auth/AuthInfo.php b/src/Auth/AuthInfo.php index b672f4f..0769997 100644 --- a/src/Auth/AuthInfo.php +++ b/src/Auth/AuthInfo.php @@ -1,27 +1,24 @@ setInfo( - $tokenInfo ?? AuthTokenInfo::empty(), - $userInfo, - $sessionInfo, - $realUserInfo - ); + public function __construct(Permissions $permissions) { + $this->permissions = $permissions; + $this->setInfo(AuthTokenInfo::empty()); } public function setInfo( @@ -34,6 +31,7 @@ class AuthInfo { $this->userInfo = $userInfo; $this->sessionInfo = $sessionInfo; $this->realUserInfo = $realUserInfo; + $this->perms = []; } public function removeInfo(): void { @@ -76,15 +74,17 @@ class AuthInfo { return $this->realUserInfo; } - private static AuthInfo $empty; + public function getPerms( + string $category, + ForumCategoryInfo|string|null $forumCategoryInfo = null + ): IPermissionResult { + $cacheKey = $category; + if($forumCategoryInfo !== null) + $cacheKey .= '|' . ($forumCategoryInfo instanceof ForumCategoryInfo ? $forumCategoryInfo->getId() : $forumCategoryInfo); - public static function init(): void { - self::$empty = new AuthInfo(AuthTokenInfo::empty()); - } + if(array_key_exists($cacheKey, $this->perms)) + return $this->perms[$cacheKey]; - public static function empty(): self { - return self::$empty; + return $this->perms[$cacheKey] = $this->permissions->getPermissions($category, $this->userInfo, $forumCategoryInfo); } } - -AuthInfo::init(); diff --git a/src/Comments/CommentsEx.php b/src/Comments/CommentsEx.php index 0df1b4b..e5891bd 100644 --- a/src/Comments/CommentsEx.php +++ b/src/Comments/CommentsEx.php @@ -4,6 +4,7 @@ namespace Misuzu\Comments; use stdClass; use RuntimeException; use Misuzu\MisuzuContext; +use Misuzu\Perm; use Misuzu\Auth\AuthInfo; use Misuzu\Users\Users; @@ -24,7 +25,14 @@ class CommentsEx { $hasUser = $this->authInfo->isLoggedIn(); $info->user = $hasUser ? $this->authInfo->getUserInfo() : null; $info->colour = $hasUser ? $this->users->getUserColour($info->user) : null; - $info->perms = $hasUser ? perms_for_comments($info->user->getId()) : []; + $info->perms = $this->authInfo->getPerms('global')->checkMany([ + 'can_post' => Perm::G_COMMENTS_CREATE, + 'can_delete' => Perm::G_COMMENTS_DELETE_OWN | Perm::G_COMMENTS_DELETE_ANY, + 'can_delete_any' => Perm::G_COMMENTS_DELETE_ANY, + 'can_pin' => Perm::G_COMMENTS_PIN, + 'can_lock' => Perm::G_COMMENTS_LOCK, + 'can_vote' => Perm::G_COMMENTS_VOTE, + ]); $info->category = $category; $info->posts = []; diff --git a/src/MisuzuContext.php b/src/MisuzuContext.php index 9e25484..0b0a983 100644 --- a/src/MisuzuContext.php +++ b/src/MisuzuContext.php @@ -27,6 +27,7 @@ use Misuzu\Home\HomeRoutes; use Misuzu\Info\InfoRoutes; use Misuzu\News\News; use Misuzu\News\NewsRoutes; +use Misuzu\Perms\Permissions; use Misuzu\Profile\ProfileFields; use Misuzu\Satori\SatoriRoutes; use Misuzu\SharpChat\SharpChatRoutes; @@ -66,13 +67,15 @@ class MisuzuContext { private Counters $counters; private ProfileFields $profileFields; private Forum $forum; + private Permissions $perms; private AuthInfo $authInfo; public function __construct(IDbConnection $dbConn, IConfig $config) { $this->dbConn = $dbConn; $this->config = $config; + $this->perms = new Permissions($this->dbConn); + $this->authInfo = new AuthInfo($this->perms); $this->auditLog = new AuditLog($this->dbConn); - $this->authInfo = new AuthInfo; $this->bans = new Bans($this->dbConn); $this->changelog = new Changelog($this->dbConn); $this->comments = new Comments($this->dbConn); @@ -184,6 +187,10 @@ class MisuzuContext { return $this->forum; } + public function getPerms(): Permissions { + return $this->perms; + } + public function createAuthTokenPacker(): AuthTokenPacker { return new AuthTokenPacker($this->config->getString('auth.secret', 'meow')); } @@ -276,7 +283,7 @@ class MisuzuContext { 'menu' => [], ]; - if($hasUserInfo && perms_check_user(MSZ_PERMS_GENERAL, $userInfo->getId(), MSZ_PERM_FORUM_VIEW_LEADERBOARD)) + if($this->authInfo->getPerms('global')->check(Perm::G_FORUM_LEADERBOARD_VIEW)) $forum['menu'][] = [ 'title' => 'Leaderboard', 'url' => url('forum-leaderboard'), @@ -325,7 +332,7 @@ class MisuzuContext { 'icon' => 'fas fa-search fa-fw', ]; - if(!$this->hasActiveBan($userInfo) && perms_check_user(MSZ_PERMS_GENERAL, $userInfo->getId(), MSZ_PERM_GENERAL_CAN_MANAGE)) { + if(!$this->hasActiveBan($userInfo) && $this->authInfo->getPerms('global')->check(Perm::G_IS_JANITOR)) { // restore behaviour where clicking this button switches between // site version and broom version if($inBroomCloset) @@ -408,7 +415,7 @@ class MisuzuContext { new SharpChatRoutes( $this->router, $this->config->scopeTo('sockChat'), $this->bans, $this->emotes, $this->users, - $this->sessions, $this->authInfo, + $this->sessions, $this->perms, $this->authInfo, $this->createAuthTokenPacker(...) ); diff --git a/src/Perm.php b/src/Perm.php new file mode 100644 index 0000000..f9eda95 --- /dev/null +++ b/src/Perm.php @@ -0,0 +1,355 @@ + General global permissions + // L -> Changelog permissions + // N -> News permissions + // F -> Global Forum perms + // C -> Global Comments perms + // X -> unallocated + public const G_IS_JANITOR = 0b00000_00000000_00000000_00000000_00000000_00000000_00000001; + public const G_LOGS_VIEW = 0b00000_00000000_00000000_00000000_00000000_00000000_00000010; + public const G_EMOTES_MANAGE = 0b00000_00000000_00000000_00000000_00000000_00000000_00000100; + public const G_CONFIG_MANAGE = 0b00000_00000000_00000000_00000000_00000000_00000000_00001000; + //public const G_IS_TESTER = 0b00000_00000000_00000000_00000000_00000000_00000000_00010000; // deprecated: tester status went unused + public const G_BLACKLIST_MANAGE = 0b00000_00000000_00000000_00000000_00000000_00000000_00100000; // unused: blacklist is currently removed to reduce overhead and it seemed like it was broken + //public const G_TWITTER_MANAGE = 0b00000_00000000_00000000_00000000_00000000_00000000_01000000; // deprecated: twitter integration has been removed + + public const G_CL_CHANGES_MANAGE = 0b00000_00000000_00000000_00000000_00000000_00000001_00000000; + public const G_CL_TAGS_MANAGE = 0b00000_00000000_00000000_00000000_00000000_00000010_00000000; + //public const G_CL_ACTIONS_MANAGE = 0b00000_00000000_00000000_00000000_00000000_00000100_00000000; // deprecated: actions are hardcoded now + + public const G_NEWS_POSTS_MANAGE = 0b00000_00000000_00000000_00000000_00000001_00000000_00000000; + public const G_NEWS_CATEGORIES_MANAGE = 0b00000_00000000_00000000_00000000_00000010_00000000_00000000; + + public const G_FORUM_CATEGORIES_MANAGE = 0b00000_00000000_00000000_00000001_00000000_00000000_00000000; + public const G_FORUM_LEADERBOARD_VIEW = 0b00000_00000000_00000000_00000010_00000000_00000000_00000000; + public const G_FORUM_TOPIC_REDIRS_MANAGE = 0b00000_00000000_00000000_00000100_00000000_00000000_00000000; + + public const G_COMMENTS_CREATE = 0b00000_00000000_00000001_00000000_00000000_00000000_00000000; + public const G_COMMENTS_EDIT_OWN = 0b00000_00000000_00000010_00000000_00000000_00000000_00000000; // unused: editing not implemented + public const G_COMMENTS_EDIT_ANY = 0b00000_00000000_00000100_00000000_00000000_00000000_00000000; // unused: editing not implemented + public const G_COMMENTS_DELETE_OWN = 0b00000_00000000_00001000_00000000_00000000_00000000_00000000; + public const G_COMMENTS_DELETE_ANY = 0b00000_00000000_00010000_00000000_00000000_00000000_00000000; + public const G_COMMENTS_PIN = 0b00000_00000000_00100000_00000000_00000000_00000000_00000000; + public const G_COMMENTS_LOCK = 0b00000_00000000_01000000_00000000_00000000_00000000_00000000; + public const G_COMMENTS_VOTE = 0b00000_00000000_10000000_00000000_00000000_00000000_00000000; + + // USER ALLOCATION: + // There's no rules here, manage perms started abouts halfway through the 31-bit integer + // Maybe formally define the octets regardless later? + public const U_PROFILE_EDIT = 0b00000_00000000_00000000_00000000_00000000_00000000_00000001; + public const U_AVATAR_CHANGE = 0b00000_00000000_00000000_00000000_00000000_00000000_00000010; + public const U_PROFILE_BACKGROUND_CHANGE = 0b00000_00000000_00000000_00000000_00000000_00000000_00000100; + public const U_PROFILE_ABOUT_EDIT = 0b00000_00000000_00000000_00000000_00000000_00000000_00001000; + public const U_PROFILE_BIRTHDATE_EDIT = 0b00000_00000000_00000000_00000000_00000000_00000000_00010000; + public const U_FORUM_SIGNATURE_EDIT = 0b00000_00000000_00000000_00000000_00000000_00000000_00100000; + public const U_USERS_MANAGE = 0b00000_00000000_00000000_00000000_00010000_00000000_00000000; + public const U_ROLES_MANAGE = 0b00000_00000000_00000000_00000000_00100000_00000000_00000000; + public const U_PERMS_MANAGE = 0b00000_00000000_00000000_00000000_01000000_00000000_00000000; + public const U_REPORTS_MANAGE = 0b00000_00000000_00000000_00000000_10000000_00000000_00000000; // unused: reports are not implemented + public const U_WARNINGS_MANAGE = 0b00000_00000000_00000000_00000001_00000000_00000000_00000000; + //public const U_BLACKLISTS_MANAGE = 0b00000_00000000_00000000_00000010_00000000_00000000_00000000; // deprecated: replaced with GLOBAL_BLACKLIST_MANAGE + public const U_NOTES_MANAGE = 0b00000_00000000_00000000_00000100_00000000_00000000_00000000; + public const U_BANS_MANAGE = 0b00000_00000000_00000000_00001000_00000000_00000000_00000000; + public const U_CAN_IMPERSONATE = 0b00000_00000000_00000000_00010000_00000000_00000000_00000000; + + // FORUM ALLOCATION: + // 0bXXXXX_XXXXXXXX_XXXXXXXX_PPPPPPPP_PPPPTTTT_TTTTTTTT_CCCCCCCC + // C -> Category related + // T -> Topic related + // N -> Post related + // X -> unallocated + public const F_CATEGORY_LIST = 0b00000_00000000_00000000_00000000_00000000_00000000_00000001; + public const F_CATEGORY_VIEW = 0b00000_00000000_00000000_00000000_00000000_00000000_00000010; + public const F_TOPIC_CREATE = 0b00000_00000000_00000000_00000000_00000000_00000100_00000000; + //public const F_TOPIC_DELETE = 0b00000_00000000_00000000_00000000_00000000_00001000_00000000; // deprecated: use F_POST_DELETE_ANY instead + public const F_TOPIC_MOVE = 0b00000_00000000_00000000_00000000_00000000_00010000_00000000; // unused: topic moving not implemented + public const F_TOPIC_LOCK = 0b00000_00000000_00000000_00000000_00000000_00100000_00000000; + public const F_TOPIC_STICKY = 0b00000_00000000_00000000_00000000_00000000_01000000_00000000; + public const F_TOPIC_ANNOUNCE_LOCAL = 0b00000_00000000_00000000_00000000_00000000_10000000_00000000; + public const F_TOPIC_ANNOUNCE_GLOBAL = 0b00000_00000000_00000000_00000000_00000001_00000000_00000000; + public const F_TOPIC_BUMP = 0b00000_00000000_00000000_00000000_00000010_00000000_00000000; + public const F_TOPIC_PRIORITY_VOTE = 0b00000_00000000_00000000_00000000_00000100_00000000_00000000; // unused: feature postponed, reuse if it makes sense otherwise deprecate + public const F_POST_CREATE = 0b00000_00000000_00000000_00000000_00010000_00000000_00000000; + public const F_POST_EDIT_OWN = 0b00000_00000000_00000000_00000000_00100000_00000000_00000000; + public const F_POST_EDIT_ANY = 0b00000_00000000_00000000_00000000_01000000_00000000_00000000; + public const F_POST_DELETE_OWN = 0b00000_00000000_00000000_00000000_10000000_00000000_00000000; + public const F_POST_DELETE_ANY = 0b00000_00000000_00000000_00000001_00000000_00000000_00000000; + + public const INFO_FOR_USER = ['global', 'user']; + public const INFO_FOR_ROLE = self::INFO_FOR_USER; // just alias for now, no clue if this will ever desync + public const INFO_FOR_FORUM_CATEGORY = ['forum']; + + public const LISTS_FOR_USER = ['global:general', 'global:changelog', 'global:news', 'global:forum', 'global:comments', 'user:personal', 'user:manage']; + public const LISTS_FOR_ROLE = self::LISTS_FOR_USER; // idem + public const LISTS_FOR_FORUM_CATEGORY = ['forum:category', 'forum:topic', 'forum:post']; + + public const LISTS = [ + 'global:general' => [ + 'title' => 'Global Permissions', + 'perms' => [ + 'global', + self::G_IS_JANITOR, + self::G_LOGS_VIEW, + self::G_EMOTES_MANAGE, + self::G_CONFIG_MANAGE, + self::G_BLACKLIST_MANAGE, + ], + ], + + 'global:changelog' => [ + 'title' => 'Changelog Permissions', + 'perms' => [ + 'global', + self::G_CL_CHANGES_MANAGE, + self::G_CL_TAGS_MANAGE, + ], + ], + + 'global:news' => [ + 'title' => 'News Permissions', + 'perms' => [ + 'global', + self::G_NEWS_POSTS_MANAGE, + self::G_NEWS_CATEGORIES_MANAGE, + ], + ], + + 'global:forum' => [ + 'title' => 'Global Forum Permissions', + 'perms' => [ + 'global', + self::G_FORUM_CATEGORIES_MANAGE, + self::G_FORUM_LEADERBOARD_VIEW, + self::G_FORUM_TOPIC_REDIRS_MANAGE, + ], + ], + + 'global:comments' => [ + 'title' => 'Comments Permissions', + 'perms' => [ + 'global', + self::G_COMMENTS_CREATE, + self::G_COMMENTS_EDIT_OWN, + self::G_COMMENTS_EDIT_ANY, + self::G_COMMENTS_DELETE_OWN, + self::G_COMMENTS_DELETE_ANY, + self::G_COMMENTS_PIN, + self::G_COMMENTS_LOCK, + self::G_COMMENTS_VOTE, + ], + ], + + 'user:personal' => [ + 'title' => 'User Permissions', + 'perms' => [ + 'user', + self::U_PROFILE_EDIT, + self::U_AVATAR_CHANGE, + self::U_PROFILE_BACKGROUND_CHANGE, + self::U_PROFILE_ABOUT_EDIT, + self::U_FORUM_SIGNATURE_EDIT, + self::U_PROFILE_BIRTHDATE_EDIT, + ], + ], + + 'user:manage' => [ + 'title' => 'User Management Permissions', + 'perms' => [ + 'user', + self::U_REPORTS_MANAGE, + self::U_NOTES_MANAGE, + self::U_WARNINGS_MANAGE, + self::U_BANS_MANAGE, + self::U_USERS_MANAGE, + self::U_ROLES_MANAGE, + self::U_PERMS_MANAGE, + self::U_CAN_IMPERSONATE, + ], + ], + + 'forum:category' => [ + 'title' => 'Forum Category Permissions', + 'perms' => [ + 'forum', + self::F_CATEGORY_LIST, + self::F_CATEGORY_VIEW, + ], + ], + + 'forum:topic' => [ + 'title' => 'Forum Topic Permissions', + 'perms' => [ + 'forum', + self::F_TOPIC_CREATE, + self::F_TOPIC_MOVE, + self::F_TOPIC_LOCK, + self::F_TOPIC_STICKY, + self::F_TOPIC_ANNOUNCE_LOCAL, + self::F_TOPIC_ANNOUNCE_GLOBAL, + self::F_TOPIC_BUMP, + self::F_TOPIC_PRIORITY_VOTE, + ], + ], + + 'forum:post' => [ + 'title' => 'Forum Topic Permissions', + 'perms' => [ + 'forum', + self::F_POST_CREATE, + self::F_POST_EDIT_OWN, + self::F_POST_EDIT_ANY, + self::F_POST_DELETE_OWN, + self::F_POST_DELETE_ANY, + ], + ], + ]; + + public const LABELS = [ + 'global' => [ + self::G_IS_JANITOR => 'Can access the Broom closet.', + self::G_LOGS_VIEW => 'Can view global audit logs.', + self::G_EMOTES_MANAGE => 'Can manage emoticons.', + self::G_CONFIG_MANAGE => 'Can manage global configuration.', + //self::G_IS_TESTER => 'Can test experimental features.', + self::G_BLACKLIST_MANAGE => 'Can manage registration IP address blacklist.', + //self::G_TWITTER_MANAGE => 'Can manage Twitter integration settings.', + + self::G_CL_CHANGES_MANAGE => 'Can manage changelog entries.', + self::G_CL_TAGS_MANAGE => 'Can manage changelog tags.', + //self::G_CL_ACTIONS_MANAGE => 'Can manage changelog action types.', + + self::G_NEWS_POSTS_MANAGE => 'Can manage news posts.', + self::G_NEWS_CATEGORIES_MANAGE => 'Can manage news categories.', + + self::G_FORUM_CATEGORIES_MANAGE => 'Can manage forum categories.', + self::G_FORUM_LEADERBOARD_VIEW => 'Can view forum leaderboard.', + self::G_FORUM_TOPIC_REDIRS_MANAGE => 'Can create redirects for deleted forum topics.', + + self::G_COMMENTS_CREATE => 'Can post comments.', + self::G_COMMENTS_EDIT_OWN => 'Can edit own comments.', + self::G_COMMENTS_EDIT_ANY => 'Can edit ANY comment.', + self::G_COMMENTS_DELETE_OWN => 'Can delete own comments.', + self::G_COMMENTS_DELETE_ANY => 'Can delete ANY comment.', + self::G_COMMENTS_PIN => 'Can pin commments.', + self::G_COMMENTS_LOCK => 'Can lock comment categories.', + self::G_COMMENTS_VOTE => 'Can vote (like or dislike) on comments.', + ], + + 'user' => [ + self::U_PROFILE_EDIT => 'Can edit own profile.', + self::U_AVATAR_CHANGE => 'Can change own avatar.', + self::U_PROFILE_BACKGROUND_CHANGE => 'Can change own profile background.', + self::U_PROFILE_ABOUT_EDIT => 'Can edit own profile about section.', + self::U_PROFILE_BIRTHDATE_EDIT => 'Can edit own profile birthdate.', + self::U_FORUM_SIGNATURE_EDIT => 'Can edit own forum signature.', + + self::U_USERS_MANAGE => 'Can manage other users.', + self::U_ROLES_MANAGE => 'Can manage user roles.', + self::U_PERMS_MANAGE => 'Can manage permissions.', + self::U_REPORTS_MANAGE => 'Can handle reports.', + self::U_WARNINGS_MANAGE => 'Can manage user warnings.', + //self::U_BLACKLISTS_MANAGE => 'Can manage registration IP address blacklist.', + self::U_NOTES_MANAGE => 'Can manage user notes.', + self::U_BANS_MANAGE => 'Can manage user bans.', + self::U_CAN_IMPERSONATE => 'Can impersonate select other users. Requires whitelisting in the configuration or super user status.', + ], + + 'forum' => [ + self::F_CATEGORY_LIST => 'Can see the forum category listed but cannot access it.', + self::F_CATEGORY_VIEW => 'Can access the forum category.', + + self::F_TOPIC_CREATE => 'Can create forum topics.', + //self::F_TOPIC_DELETE => 'Can delete forum topics.', + self::F_TOPIC_MOVE => 'Can move forum topics to other categories.', + self::F_TOPIC_LOCK => 'Can lock forum topics.', + self::F_TOPIC_STICKY => 'Can make sticky topics.', + self::F_TOPIC_ANNOUNCE_LOCAL => 'Can make local announcement topics.', + self::F_TOPIC_ANNOUNCE_GLOBAL => 'Can make global announcement topics.', + self::F_TOPIC_BUMP => 'Can bump topics without posting a reply.', + self::F_TOPIC_PRIORITY_VOTE => 'Can use the priority voting system.', + + self::F_POST_CREATE => 'Can create forum posts (replies only, if not allowed to create topics).', + self::F_POST_EDIT_OWN => 'Can edit own forum posts.', + self::F_POST_EDIT_ANY => 'Can edit ANY forum post.', + self::F_POST_DELETE_OWN => 'Can delete own forum posts.', + self::F_POST_DELETE_ANY => 'Can delete ANY forum post.', + ], + ]; + + public static function label(string $category, int $permission): string { + return array_key_exists($category, self::LABELS) + && array_key_exists($permission, self::LABELS[$category]) + ? self::LABELS[$category][$permission] : ''; + } + + public static function createList(array $sections): array { + $list = []; + + foreach($sections as $sectionName) { + if(!array_key_exists($sectionName, self::LISTS)) + continue; + + $currentCategoryName = ''; + $sectionInfo = self::LISTS[$sectionName]; + + $list[] = $item = new stdClass; + $item->name = $sectionName; + $item->title = $sectionInfo['title']; + $item->perms = []; + + foreach($sectionInfo['perms'] as $permInfo) { + if(is_string($permInfo)) { + $currentCategoryName = $permInfo; + continue; + } + + $categoryName = $currentCategoryName; + $perm = 0; + if(is_array($permInfo)) + [$categoryName, $perm] = $permInfo; + elseif(is_int($permInfo)) + $perm = $permInfo; + + $item->perms[] = $permItem = new stdClass; + $permItem->category = $categoryName; + $permItem->name = sprintf('perms[%s:%d]', $categoryName, $perm); + $permItem->title = self::label($categoryName, $perm); + $permItem->value = $perm; + } + } + + return $list; + } + + public static function convertSubmission(array $raw, array $categories): array { + $apply = []; + + foreach($raw as $name => $mode) { + $nameParts = explode(':', $name, 2); + if(count($nameParts) !== 2 || !ctype_alpha($nameParts[0]) || !ctype_digit($nameParts[1])) + continue; + + [$category, $value] = $nameParts; + if(!in_array($category, $categories)) + continue; + + if($mode === 'yes' || $mode === 'never') { + if(!array_key_exists($category, $apply)) + $apply[$category] = ['allow' => 0, 'deny' => 0]; + + $apply[$category][$mode === 'yes' ? 'allow' : 'deny'] |= (int)$value; + } + } + + return $apply; + } +} diff --git a/src/Perms/IPermissionResult.php b/src/Perms/IPermissionResult.php new file mode 100644 index 0000000..e6701b5 --- /dev/null +++ b/src/Perms/IPermissionResult.php @@ -0,0 +1,9 @@ +userId = $result->isNull(0) ? null : $result->getInteger(0); + $this->roleId = $result->isNull(1) ? null : $result->getInteger(1); + $this->forumCategoryId = $result->isNull(2) ? null : $result->getInteger(2); + $this->category = $result->getString(3); + $this->allow = $result->getInteger(4); + $this->deny = $result->getInteger(5); + $this->calculated = $this->allow & ~$this->deny; + } + + public function hasUserId(): bool { + return $this->userId !== null; + } + + public function getUserId(): ?string { + return $this->userId; + } + + public function hasRoleId(): bool { + return $this->roleId !== null; + } + + public function getRoleId(): ?string { + return $this->roleId; + } + + public function hasForumCategoryId(): bool { + return $this->forumCategoryId !== null; + } + + public function getForumCategoryId(): ?string { + return $this->forumCategoryId; + } + + public function getCategory(): string { + return $this->category; + } + + public function getAllow(): int { + return $this->allow; + } + + public function getDeny(): int { + return $this->deny; + } + + public function getCalculated(): int { + return $this->calculated; + } + + public function check(int $perm): bool { + return ($this->calculated & $perm) > 0; + } + + public function checkAllow(int $perm): bool { + return ($this->allow & $perm) > 0; + } + + public function checkDeny(int $perm): bool { + return ($this->deny & $perm) > 0; + } + + public function checkNeutral(int $perm): bool { + return ($this->allow & $perm) === 0 + && ($this->deny & $perm) === 0; + } +} diff --git a/src/Perms/PermissionResult.php b/src/Perms/PermissionResult.php new file mode 100644 index 0000000..d7f79f0 --- /dev/null +++ b/src/Perms/PermissionResult.php @@ -0,0 +1,16 @@ +calculated; + } + + public function check(int $perm): bool { + return ($this->calculated & $perm) > 0; + } +} diff --git a/src/Perms/PermissionResultShared.php b/src/Perms/PermissionResultShared.php new file mode 100644 index 0000000..bf10679 --- /dev/null +++ b/src/Perms/PermissionResultShared.php @@ -0,0 +1,24 @@ +getCalculated(); + + foreach($perms as $name => $perm) + $result->{$name} = ($calculated & $perm) > 0; + + return $result; + } + + public function apply(callable $callable): IPermissionResult { + return new PermissionResult($callable($this->getCalculated())); + } +} diff --git a/src/Perms/Permissions.php b/src/Perms/Permissions.php new file mode 100644 index 0000000..f5887f7 --- /dev/null +++ b/src/Perms/Permissions.php @@ -0,0 +1,363 @@ +dbConn = $dbConn; + $this->cache = new DbStatementCache($dbConn); + } + + // this method is purely intended for getting the permission data for a single entity + // it should not be used to do actual permission checks + public function getPermissionInfo( + UserInfo|string|null $userInfo = null, + RoleInfo|string|null $roleInfo = null, + ForumCategoryInfo|string|null $forumCategoryInfo = null, + array|string|null $categoryNames = null, + ): PermissionInfo|array|null { + $hasUserInfo = $userInfo !== null; + $hasRoleInfo = $roleInfo !== null; + if($hasUserInfo && $hasRoleInfo) + throw new InvalidArgumentException('$userInfo and $roleInfo may not be set at the same time.'); + + $hasForumCategoryInfo = $forumCategoryInfo !== null; + $hasCategoryName = $categoryNames !== null; + $categoryNamesIsArray = $hasCategoryName && is_array($categoryNames); + if($categoryNamesIsArray && empty($categoryNames)) + throw new InvalidArgumentException('$categoryNames may not be empty if it is an array.'); + + $query = 'SELECT user_id, role_id, forum_id, perms_category, perms_allow, perms_deny FROM msz_perms'; + $query .= sprintf(' WHERE user_id %s', $hasUserInfo ? '= ?' : 'IS NULL'); + $query .= sprintf(' AND role_id %s', $hasRoleInfo ? '= ?' : 'IS NULL'); + $query .= sprintf(' AND forum_id %s', $hasForumCategoryInfo ? '= ?' : 'IS NULL'); + if($hasCategoryName) + $query .= ' AND perms_category ' . ($categoryNamesIsArray ? sprintf('IN (%s)', DbTools::prepareListString($categoryNames)) : '= ?'); + + $args = 0; + $stmt = $this->cache->get($query); + if($hasUserInfo) + $stmt->addParameter(++$args, $userInfo instanceof UserInfo ? $userInfo->getId() : $userInfo); + if($hasRoleInfo) + $stmt->addParameter(++$args, $roleInfo instanceof RoleInfo ? $roleInfo->getId() : $roleInfo); + if($hasForumCategoryInfo) + $stmt->addParameter(++$args, $forumCategoryInfo instanceof ForumCategoryInfo ? $forumCategoryInfo->getId() : $forumCategoryInfo); + if($hasCategoryName) { + if($categoryNamesIsArray) { + foreach($categoryNames as $name) + $stmt->addParameter(++$args, $name); + } else + $stmt->addParameter(++$args, $categoryNames); + } + $stmt->execute(); + + $result = $stmt->getResult(); + + if(is_string($categoryNames)) + return $result->next() ? new PermissionInfo($result) : null; + + $perms = []; + while($result->next()) + $perms[$result->getString(3)] = new PermissionInfo($result); + + return $perms; + } + + public function setPermissions( + string $categoryName, + int $allow, + int $deny, + UserInfo|string|null $userInfo = null, + RoleInfo|string|null $roleInfo = null, + ForumCategoryInfo|string|null $forumCategoryInfo = null + ): void { + if($allow < self::PERMS_MIN || $allow > self::PERMS_MAX) + throw new InvalidArgumentException('$allow must be an positive 53-bit integer.'); + if($deny < self::PERMS_MIN || $deny > self::PERMS_MAX) + throw new InvalidArgumentException('$allow must be an positive 53-bit integer.'); + if($userInfo !== null && $roleInfo !== null) + throw new InvalidArgumentException('$userInfo and $roleInfo may not be set at the same time.'); + + // because of funny technical reasons we have to delete separately + $this->removePermissions($categoryName, $userInfo, $roleInfo, $forumCategoryInfo); + + // don't insert zeroes + if($allow === 0 && $deny === 0) + return; + + $stmt = $this->cache->get('INSERT INTO msz_perms (user_id, role_id, forum_id, perms_category, perms_allow, perms_deny) VALUES (?, ?, ?, ?, ?, ?)'); + $stmt->addParameter(1, $userInfo instanceof UserInfo ? $userInfo->getId() : $userInfo); + $stmt->addParameter(2, $roleInfo instanceof RoleInfo ? $roleInfo->getId() : $roleInfo); + $stmt->addParameter(3, $forumCategoryInfo instanceof ForumCategoryInfo ? $forumCategoryInfo->getId() : $forumCategoryInfo); + $stmt->addParameter(4, $categoryName); + $stmt->addParameter(5, $allow); + $stmt->addParameter(6, $deny); + $stmt->execute(); + } + + public function removePermissions( + array|string|null $categoryNames, + UserInfo|string|null $userInfo = null, + RoleInfo|string|null $roleInfo = null, + ForumCategoryInfo|string|null $forumCategoryInfo = null + ): void { + $hasUserInfo = $userInfo !== null; + $hasRoleInfo = $roleInfo !== null; + $hasForumCategoryInfo = $forumCategoryInfo !== null; + $hasCategoryNames = $categoryNames !== null; + $categoryNamesIsArray = $hasCategoryNames && is_array($categoryNames); + + $query = 'DELETE FROM msz_perms'; + $query .= sprintf(' WHERE user_id %s', $hasUserInfo ? '= ?' : 'IS NULL'); + $query .= sprintf(' AND role_id %s', $hasRoleInfo ? '= ?' : 'IS NULL'); + $query .= sprintf(' AND forum_id %s', $hasForumCategoryInfo ? '= ?' : 'IS NULL'); + if($hasCategoryNames) + $query .= ' AND perms_category ' . ($categoryNamesIsArray ? sprintf('IN (%s)', DbTools::prepareListString($categoryNames)) : '= ?'); + + $args = 0; + $stmt = $this->cache->get($query); + if($hasUserInfo) + $stmt->addParameter(++$args, $userInfo instanceof UserInfo ? $userInfo->getId() : $userInfo); + if($hasRoleInfo) + $stmt->addParameter(++$args, $roleInfo instanceof RoleInfo ? $roleInfo->getId() : $roleInfo); + if($hasForumCategoryInfo) + $stmt->addParameter(++$args, $forumCategoryInfo instanceof ForumCategoryInfo ? $forumCategoryInfo->getId() : $forumCategoryInfo); + if($categoryNamesIsArray) { + foreach($categoryNames as $name) + $stmt->addParameter(++$args, $name); + } else + $stmt->addParameter(++$args, $categoryNames); + $stmt->execute(); + } + + public function checkPermissions( + string $categoryName, + int $perms, + UserInfo|string|null $userInfo = null, + ForumCategoryInfo|string|null $forumCategoryInfo = null + ): int { + $hasUserInfo = $userInfo !== null; + $hasForumCategoryInfo = $forumCategoryInfo !== null; + + $query = 'SELECT perms_calculated & ? FROM msz_perms_calculated WHERE perms_category = ?'; + $query .= sprintf(' AND forum_id %s', $hasForumCategoryInfo ? '= ?' : 'IS NULL'); + $query .= sprintf(' AND user_id %s', $hasUserInfo ? '= ?' : 'IS NULL'); + + $args = 0; + $stmt = $this->cache->get($query); + $stmt->addParameter(++$args, $perms); + $stmt->addParameter(++$args, $categoryName); + if($hasForumCategoryInfo) + $stmt->addParameter(++$args, $forumCategoryInfo instanceof ForumCategoryInfo ? $forumCategoryInfo->getId() : $forumCategoryInfo); + if($hasUserInfo) + $stmt->addParameter(++$args, $userInfo instanceof UserInfo ? $userInfo->getId() : $userInfo); + $stmt->execute(); + + $result = $stmt->getResult(); + return $result->next() ? $result->getInteger(0) : 0; + } + + public function getPermissions( + string|array $categoryNames, + UserInfo|string|null $userInfo = null, + ForumCategoryInfo|string|null $forumCategoryInfo = null + ): PermissionResult|stdClass { + $categoryNamesIsArray = is_array($categoryNames); + if($categoryNamesIsArray && empty($categoryNames)) + throw new InvalidArgumentException('$categoryNames may not be an empty array.'); + + $hasUserInfo = $userInfo !== null; + $hasForumCategoryInfo = $forumCategoryInfo !== null; + + $query = 'SELECT perms_category, perms_calculated FROM msz_perms_calculated'; + $query .= ' WHERE perms_category ' . ($categoryNamesIsArray ? sprintf('IN (%s)', DbTools::prepareListString($categoryNames)) : '= ?'); + $query .= sprintf(' AND forum_id %s', $hasForumCategoryInfo ? '= ?' : 'IS NULL'); + $query .= sprintf(' AND user_id %s', $hasUserInfo ? '= ?' : 'IS NULL'); + $query .= ' GROUP BY perms_category'; + + $args = 0; + $stmt = $this->cache->get($query); + + if($categoryNamesIsArray) { + foreach($categoryNames as $name) + $stmt->addParameter(++$args, $name); + } else + $stmt->addParameter(++$args, $categoryNames); + if($hasForumCategoryInfo) + $stmt->addParameter(++$args, $forumCategoryInfo instanceof ForumCategoryInfo ? $forumCategoryInfo->getId() : $forumCategoryInfo); + if($hasUserInfo) + $stmt->addParameter(++$args, $userInfo instanceof UserInfo ? $userInfo->getId() : $userInfo); + $stmt->execute(); + + $result = $stmt->getResult(); + if(!$categoryNamesIsArray) + return new PermissionResult($result->next() ? $result->getInteger(1) : 0); + + $results = []; + while($result->next()) + $results[$result->getString(0)] = $result->getInteger(1); + + $sets = new stdClass; + foreach($categoryNames as $categoryName) + $sets->{$categoryName} = new PermissionResult($results[$categoryName] ?? 0); + + 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); + + 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; + + 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...'); + $stmt = $this->cache->get('SELECT perms_category, BIT_OR(perms_allow) & ~BIT_OR(perms_deny) FROM msz_perms WHERE forum_id IS NULL AND (user_id = ? OR role_id IN (SELECT role_id FROM msz_users_roles WHERE user_id = ?)) GROUP BY perms_category'); + foreach($userIds as $userId) { + $insert->reset(); + $insert->addParameter(1, $userId); + $insert->addParameter(2, null); + + $stmt->reset(); + $stmt->addParameter(1, $userId); + $stmt->addParameter(2, $userId); + $stmt->execute(); + + $result = $stmt->getResult(); + while($result->next()) { + $category = $result->getString(0); + $perms = $result->getInteger(1); + if($perms === 0) + continue; + + self::precalculatePermissionsLog('Inserting user #%s permissions for category %s with value %x...', $userId, $category, $perms); + $insert->addParameter(3, $category); + $insert->addParameter(4, $perms); + $insert->execute(); + } + } + + self::precalculatePermissionsLog('Loading list of forum categories...'); + $forumCats = $forum->getCategories(asTree: true); + foreach($forumCats as $forumCat) + $this->precalculatePermissionsForForumCategory($insert, $userIds, $forumCat); + + self::precalculatePermissionsLog('Finished permission precalculations!'); + } + + private function precalculatePermissionsForForumCategory(IDbStatement $insert, array $userIds, object $forumCat, 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(); + + $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(); + } + + $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 = ? OR role_id IN (SELECT role_id FROM msz_users_roles WHERE user_id = ?)) GROUP BY perms_category', + DbTools::prepareListString($catIds) + )); + foreach($catIds as $catId) + $stmt->addParameter(++$args, $catId); + $startArgs = $args; + foreach($userIds as $userId) { + $args = $startArgs; + $stmt->addParameter(++$args, $userId); + $stmt->addParameter(++$args, $userId); + $stmt->execute(); + + $insert->reset(); + $insert->addParameter(1, $userId); + $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 user #%s permissions for category %s with value %x for forum category #%s...', $userId, $category, $perms, $currentCatId); + $insert->addParameter(3, $category); + $insert->addParameter(4, $perms); + $insert->execute(); + } + } + + foreach($forumCat->children as $forumChild) + $this->precalculatePermissionsForForumCategory($insert, $userIds, $forumChild, $catIds); + } + + private static function precalculatePermissionsLog(string $fmt, ...$args): void { + echo DateTime::now()->format('[H:i:s.u] '); + vprintf($fmt, $args); + echo PHP_EOL; + } +} diff --git a/src/SharpChat/SharpChatPerms.php b/src/SharpChat/SharpChatPerms.php index 3ce6fd2..d83699c 100644 --- a/src/SharpChat/SharpChatPerms.php +++ b/src/SharpChat/SharpChatPerms.php @@ -1,6 +1,8 @@ getId(); - $perms = self::PERMS_DEFAULT; + public static function convert(Permissions $perms, UserInfo $userInfo): int { + $perms = $perms->getPermissions(['global', 'user'], $userInfo); + $convert = self::PERMS_DEFAULT; - if(perms_check_user(MSZ_PERMS_USER, $userInfo, MSZ_PERM_USER_MANAGE_USERS)) - $perms |= self::PERMS_MANAGE_USERS; + if($perms->user->check(Perm::U_USERS_MANAGE)) + $convert |= self::PERMS_MANAGE_USERS; - if(perms_check_user(MSZ_PERMS_USER, $userInfo, MSZ_PERM_USER_MANAGE_WARNINGS)) - $perms |= self::P_KICK_USER; + if($perms->user->check(Perm::U_WARNINGS_MANAGE)) + $convert |= self::P_KICK_USER; - if(perms_check_user(MSZ_PERMS_USER, $userInfo, MSZ_PERM_USER_MANAGE_BANS)) - $perms |= self::P_BAN_USER; + if($perms->user->check(Perm::U_BANS_MANAGE)) + $convert |= self::P_BAN_USER; - if(perms_check_user(MSZ_PERMS_USER, $userInfo, MSZ_PERM_USER_CHANGE_BACKGROUND)) - $perms |= self::PERMS_CHANGE_BACKG; + if($perms->user->check(Perm::U_PROFILE_BACKGROUND_CHANGE)) + $convert |= self::PERMS_CHANGE_BACKG; - if(perms_check_user(MSZ_PERMS_FORUM, $userInfo, MSZ_PERM_FORUM_MANAGE_FORUMS)) - $perms |= self::PERMS_MANAGE_FORUM; + if($perms->global->check(Perm::G_FORUM_CATEGORIES_MANAGE)) + $convert |= self::PERMS_MANAGE_FORUM; - return $perms; + return $convert; } } diff --git a/src/SharpChat/SharpChatRoutes.php b/src/SharpChat/SharpChatRoutes.php index c683ffb..08595d3 100644 --- a/src/SharpChat/SharpChatRoutes.php +++ b/src/SharpChat/SharpChatRoutes.php @@ -10,6 +10,7 @@ use Misuzu\Auth\AuthInfo; use Misuzu\Auth\Sessions; use Misuzu\Config\IConfig; use Misuzu\Emoticons\Emotes; +use Misuzu\Perms\Permissions; use Misuzu\Users\Bans; use Misuzu\Users\Users; @@ -19,6 +20,7 @@ final class SharpChatRoutes { private Emotes $emotes; private Users $users; private Sessions $sessions; + private Permissions $perms; private AuthInfo $authInfo; private Closure $createAuthTokenPacker; private string $hashKey; @@ -30,6 +32,7 @@ final class SharpChatRoutes { Emotes $emotes, Users $users, Sessions $sessions, + Permissions $perms, AuthInfo $authInfo, Closure $createAuthTokenPacker // this sucks lol ) { @@ -38,6 +41,7 @@ final class SharpChatRoutes { $this->emotes = $emotes; $this->users = $users; $this->sessions = $sessions; + $this->perms = $perms; $this->authInfo = $authInfo; $this->createAuthTokenPacker = $createAuthTokenPacker; $this->hashKey = $this->config->getString('hashKey', 'woomy'); @@ -268,7 +272,7 @@ final class SharpChatRoutes { 'colour_raw' => Colour::toMisuzu($userColour), 'rank' => $userRank, 'hierarchy' => $userRank, - 'perms' => SharpChatPerms::convert($userInfo), + 'perms' => SharpChatPerms::convert($this->perms, $userInfo), ]; } diff --git a/src/Users/Assets/AssetsRoutes.php b/src/Users/Assets/AssetsRoutes.php index 1d88490..5397541 100644 --- a/src/Users/Assets/AssetsRoutes.php +++ b/src/Users/Assets/AssetsRoutes.php @@ -4,6 +4,7 @@ namespace Misuzu\Users\Assets; use InvalidArgumentException; use RuntimeException; use Index\Routing\IRouter; +use Misuzu\Perm; use Misuzu\Auth\AuthInfo; use Misuzu\Users\Bans; use Misuzu\Users\Users; @@ -28,8 +29,8 @@ class AssetsRoutes { private function canViewAsset($request, UserInfo $assetUser): bool { if($this->bans->countActiveBans($assetUser)) - return $this->authInfo->isLoggedIn() // allow staff viewing profile to still see banned user assets - && perms_check_user(MSZ_PERMS_USER, (int)$this->authInfo->getUserId(), MSZ_PERM_USER_MANAGE_USERS) + // allow staff viewing profile to still see banned user assets + return $this->authInfo->getPerms('user')->check(Perm::U_USERS_MANAGE) && parse_url($request->getHeaderFirstLine('Referer'), PHP_URL_PATH) === url('user-profile'); return true; diff --git a/src/perms.php b/src/perms.php deleted file mode 100644 index 13eed9a..0000000 --- a/src/perms.php +++ /dev/null @@ -1,729 +0,0 @@ -bind('user_id_1', $user); - $getPerms->bind('user_id_2', $user); - - return $memo[$user] = $getPerms->fetch(); -} - -function perms_delete_user(int|string $user): bool { - if(is_string($user)) - $user = (int)$user; - if($user < 1) - return false; - - $deletePermissions = \Misuzu\DB::prepare(' - DELETE FROM `msz_permissions` - WHERE `role_id` IS NULL - AND `user_id` = :user_id - '); - $deletePermissions->bind('user_id', $user); - return $deletePermissions->execute(); -} - -function perms_get_role(int $role): array { - if($role < 1) { - return perms_get_blank(); - } - - static $memo = []; - - if(array_key_exists($role, $memo)) { - return $memo[$role]; - } - - $getPerms = \Misuzu\DB::prepare(sprintf( - ' - SELECT %s - FROM `msz_permissions` - WHERE `role_id` = :role_id - AND `user_id` IS NULL - ', - perms_get_select() - )); - $getPerms->bind('role_id', $role); - - return $memo[$role] = $getPerms->fetch(); -} - -function perms_get_user_raw(int|string $user): array { - if(is_string($user)) - $user = (int)$user; - if($user < 1) - return perms_create(); - - $getPerms = \Misuzu\DB::prepare(sprintf(' - SELECT `%s` - FROM `msz_permissions` - WHERE `user_id` = :user_id - AND `role_id` IS NULL - ', implode('`, `', perms_get_keys()))); - $getPerms->bind('user_id', $user); - $perms = $getPerms->fetch(); - - if(empty($perms)) { - return perms_create(); - } - - return $perms; -} - -function perms_set_user_raw(int|string $user, array $perms): bool { - if(is_string($user)) - $user = (int)$user; - if($user < 1) - return false; - - $realPerms = perms_create(); - $permKeys = array_keys($realPerms); - - foreach($permKeys as $perm) { - $realPerms[$perm] = (int)($perms[$perm] ?? 0); - } - - $setPermissions = \Misuzu\DB::prepare(sprintf( - ' - REPLACE INTO `msz_permissions` - (`role_id`, `user_id`, `%s`) - VALUES - (NULL, :user_id, :%s) - ', - implode('`, `', $permKeys), - implode(', :', $permKeys) - )); - $setPermissions->bind('user_id', $user); - - foreach($realPerms as $key => $value) { - $setPermissions->bind($key, $value); - } - - return $setPermissions->execute(); -} - -function perms_get_role_raw(int $role): array { - if($role < 1) { - return perms_create(); - } - - $getPerms = \Misuzu\DB::prepare(sprintf(' - SELECT `%s` - FROM `msz_permissions` - WHERE `user_id` IS NULL - AND `role_id` = :role_id - ', implode('`, `', perms_get_keys()))); - $getPerms->bind('role_id', $role); - $perms = $getPerms->fetch(); - - if(empty($perms)) { - return perms_create(); - } - - return $perms; -} - -function perms_check(?int $perms, ?int $perm, bool $strict = false): bool { - $and = ($perms ?? 0) & ($perm ?? 0); - return $strict ? $and === $perm : $and > 0; -} - -function perms_check_user(string $prefix, int|string|null $userId, int $perm, bool $strict = false): bool { - if(is_string($userId)) - $userId = (int)$userId; - return $userId > 0 && perms_check(perms_get_user($userId)[$prefix] ?? 0, $perm, $strict); -} - -function perms_check_bulk(int $perms, array $set, bool $strict = false): array { - foreach($set as $key => $perm) { - $set[$key] = perms_check($perms, $perm, $strict); - } - - return $set; -} - -function perms_check_user_bulk(string $prefix, int|string|null $userId, array $set, bool $strict = false): array { - $perms = perms_get_user($userId)[$prefix] ?? 0; - return perms_check_bulk($perms, $set, $strict); -} - -function perms_for_comments(string|int $userId): array { - return perms_check_user_bulk(MSZ_PERMS_COMMENTS, $userId, [ - 'can_comment' => MSZ_PERM_COMMENTS_CREATE, - 'can_delete' => MSZ_PERM_COMMENTS_DELETE_OWN | MSZ_PERM_COMMENTS_DELETE_ANY, - 'can_delete_any' => MSZ_PERM_COMMENTS_DELETE_ANY, - 'can_pin' => MSZ_PERM_COMMENTS_PIN, - 'can_lock' => MSZ_PERM_COMMENTS_LOCK, - 'can_vote' => MSZ_PERM_COMMENTS_VOTE, - ]); -} - -function forum_get_parent_id(int $forumId): int { - if($forumId < 1) - return 0; - - static $memoized = []; - - if(array_key_exists($forumId, $memoized)) - return $memoized[$forumId]; - - $getParent = \Misuzu\DB::prepare(' - SELECT `forum_parent` - FROM `msz_forum_categories` - WHERE `forum_id` = :forum_id - '); - $getParent->bind('forum_id', $forumId); - - return (int)$getParent->fetchColumn(); -} - -function forum_perms_get_user(?int $forum, int $user): array { - $perms = perms_get_blank(MSZ_FORUM_PERM_MODES); - - if($user < 0 || $forum < 0) - return $perms; - - static $memo = []; - $memoId = "{$forum}-{$user}"; - - if(array_key_exists($memoId, $memo)) - return $memo[$memoId]; - - if($forum > 0) - $perms = forum_perms_get_user( - forum_get_parent_id($forum), - $user - ); - - $getPerms = \Misuzu\DB::prepare(sprintf( - ' - SELECT %s - FROM `msz_forum_permissions` - WHERE (`forum_id` = :forum_id OR `forum_id` IS NULL) - AND ( - (`user_id` IS NULL AND `role_id` IS NULL) - OR (`user_id` = :user_id_1 AND `role_id` IS NULL) - OR ( - `user_id` IS NULL - AND `role_id` IN ( - SELECT `role_id` - FROM `msz_users_roles` - WHERE `user_id` = :user_id_2 - ) - ) - ) - ', - perms_get_select(MSZ_FORUM_PERM_MODES) - )); - $getPerms->bind('forum_id', $forum); - $getPerms->bind('user_id_1', $user); - $getPerms->bind('user_id_2', $user); - - $userPerms = $getPerms->fetch(); - foreach($perms as $key => $value) - $perms[$key] |= $userPerms[$key] ?? 0; - - return $memo[$memoId] = $perms; -} - -function forum_perms_check_user( - string $prefix, - ?int $forumId, - ?int $userId, - int $perm, - bool $strict = false -): bool { - return perms_check(forum_perms_get_user($forumId, $userId)[$prefix] ?? 0, $perm, $strict); -} - -define('MSZ_MANAGE_PERM_YES', 'yes'); -define('MSZ_MANAGE_PERM_NO', 'no'); -define('MSZ_MANAGE_PERM_NEVER', 'never'); - -function manage_perms_value(int $perm, int $allow, int $deny): string { - if(perms_check($deny, $perm)) - return MSZ_MANAGE_PERM_NEVER; - if(perms_check($allow, $perm)) - return MSZ_MANAGE_PERM_YES; - return MSZ_MANAGE_PERM_NO; -} - -function manage_perms_apply(array $list, array $post, ?array $raw = null): ?array { - $perms = $raw !== null ? $raw : perms_create(); - - foreach($list as $section) { - if(empty($post[$section['section']]) || !is_array($post[$section['section']])) - continue; - - $allowKey = perms_get_key($section['section'], MSZ_PERMS_ALLOW); - $denyKey = perms_get_key($section['section'], MSZ_PERMS_DENY); - - foreach($section['perms'] as $perm) { - if(empty($post[$section['section']][$perm['section']]['value'])) - continue; - $perm['perm'] = (int)$perm['perm']; // makes phpstan happy - - switch($post[$section['section']][$perm['section']]['value']) { - case MSZ_MANAGE_PERM_YES: - $perms[$allowKey] |= $perm['perm']; - $perms[$denyKey] &= ~$perm['perm']; - break; - - case MSZ_MANAGE_PERM_NEVER: - $perms[$allowKey] &= ~$perm['perm']; - $perms[$denyKey] |= $perm['perm']; - break; - - case MSZ_MANAGE_PERM_NO: - default: - $perms[$allowKey] &= ~$perm['perm']; - $perms[$denyKey] &= ~$perm['perm']; - break; - } - } - } - - $returnNothing = 0; - foreach($perms as $perm) - $returnNothing |= $perm; - - return $returnNothing === 0 ? null : $perms; -} - -function manage_perms_calculate(array $rawPerms, array $perms): array { - for($i = 0; $i < count($perms); $i++) { - $section = $perms[$i]['section']; - $allowKey = perms_get_key($section, MSZ_PERMS_ALLOW); - $denyKey = perms_get_key($section, MSZ_PERMS_DENY); - - for($j = 0; $j < count($perms[$i]['perms']); $j++) { - $permission = $perms[$i]['perms'][$j]['perm']; - $perms[$i]['perms'][$j]['value'] = manage_perms_value($permission, $rawPerms[$allowKey], $rawPerms[$denyKey]); - } - } - - return $perms; -} - -function manage_perms_list(array $rawPerms): array { - return manage_perms_calculate($rawPerms, [ - [ - 'section' => MSZ_PERMS_GENERAL, - 'title' => 'General', - 'perms' => [ - [ - 'section' => 'can-manage', - 'title' => 'Can access the management panel.', - 'perm' => MSZ_PERM_GENERAL_CAN_MANAGE, - ], - [ - 'section' => 'view-logs', - 'title' => 'Can view audit logs.', - 'perm' => MSZ_PERM_GENERAL_VIEW_LOGS, - ], - [ - 'section' => 'manage-emotes', - 'title' => 'Can manage emoticons.', - 'perm' => MSZ_PERM_GENERAL_MANAGE_EMOTES, - ], - [ - 'section' => 'manage-settings', - 'title' => 'Can manage general Misuzu settings.', - 'perm' => MSZ_PERM_GENERAL_MANAGE_CONFIG, - ], - ], - ], - [ - 'section' => MSZ_PERMS_USER, - 'title' => 'User', - 'perms' => [ - [ - 'section' => 'edit-profile', - 'title' => 'Can edit own profile.', - 'perm' => MSZ_PERM_USER_EDIT_PROFILE, - ], - [ - 'section' => 'change-avatar', - 'title' => 'Can change own avatar.', - 'perm' => MSZ_PERM_USER_CHANGE_AVATAR, - ], - [ - 'section' => 'change-background', - 'title' => 'Can change own background.', - 'perm' => MSZ_PERM_USER_CHANGE_BACKGROUND, - ], - [ - 'section' => 'edit-about', - 'title' => 'Can change own about section.', - 'perm' => MSZ_PERM_USER_EDIT_ABOUT, - ], - [ - 'section' => 'edit-birthdate', - 'title' => 'Can change own birthdate.', - 'perm' => MSZ_PERM_USER_EDIT_BIRTHDATE, - ], - [ - 'section' => 'edit-signature', - 'title' => 'Can change own signature.', - 'perm' => MSZ_PERM_USER_EDIT_SIGNATURE, - ], - [ - 'section' => 'manage-users', - 'title' => 'Can manage other users.', - 'perm' => MSZ_PERM_USER_MANAGE_USERS, - ], - [ - 'section' => 'manage-roles', - 'title' => 'Can manage roles.', - 'perm' => MSZ_PERM_USER_MANAGE_ROLES, - ], - [ - 'section' => 'manage-perms', - 'title' => 'Can manage permissions.', - 'perm' => MSZ_PERM_USER_MANAGE_PERMS, - ], - [ - 'section' => 'manage-reports', - 'title' => 'Can handle reports.', - 'perm' => MSZ_PERM_USER_MANAGE_REPORTS, - ], - [ - 'section' => 'manage-notes', - 'title' => 'Can manage user notes.', - 'perm' => MSZ_PERM_USER_MANAGE_NOTES, - ], - [ - 'section' => 'manage-warnings', - 'title' => 'Can manage user warnings.', - 'perm' => MSZ_PERM_USER_MANAGE_WARNINGS, - ], - [ - 'section' => 'manage-bans', - 'title' => 'Can manage user bans.', - 'perm' => MSZ_PERM_USER_MANAGE_BANS, - ], - [ - 'section' => 'impersonate', - 'title' => 'Can impersonate select users.', - 'perm' => MSZ_PERM_USER_IMPERSONATE, - ], - ], - ], - [ - 'section' => MSZ_PERMS_NEWS, - 'title' => 'News', - 'perms' => [ - [ - 'section' => 'manage-posts', - 'title' => 'Can manage posts.', - 'perm' => MSZ_PERM_NEWS_MANAGE_POSTS, - ], - [ - 'section' => 'manage-cats', - 'title' => 'Can manage catagories.', - 'perm' => MSZ_PERM_NEWS_MANAGE_CATEGORIES, - ], - ], - ], - [ - 'section' => MSZ_PERMS_FORUM, - 'title' => 'Forum', - 'perms' => [ - [ - 'section' => 'manage-forums', - 'title' => 'Can manage forum sections.', - 'perm' => MSZ_PERM_FORUM_MANAGE_FORUMS, - ], - [ - 'section' => 'view-leaderboard', - 'title' => 'Can view the forum leaderboard live.', - 'perm' => MSZ_PERM_FORUM_VIEW_LEADERBOARD, - ], - [ - 'section' => 'topic-redirs', - 'title' => 'Can create redirects for deleted topics.', - 'perm' => MSZ_PERM_FORUM_TOPIC_REDIRS, - ], - ], - ], - [ - 'section' => MSZ_PERMS_COMMENTS, - 'title' => 'Comments', - 'perms' => [ - [ - 'section' => 'create', - 'title' => 'Can post comments.', - 'perm' => MSZ_PERM_COMMENTS_CREATE, - ], - [ - 'section' => 'delete-own', - 'title' => 'Can delete own comments.', - 'perm' => MSZ_PERM_COMMENTS_DELETE_OWN, - ], - [ - 'section' => 'delete-any', - 'title' => 'Can delete anyone\'s comments.', - 'perm' => MSZ_PERM_COMMENTS_DELETE_ANY, - ], - [ - 'section' => 'pin', - 'title' => 'Can pin comments.', - 'perm' => MSZ_PERM_COMMENTS_PIN, - ], - [ - 'section' => 'lock', - 'title' => 'Can lock comment threads.', - 'perm' => MSZ_PERM_COMMENTS_LOCK, - ], - [ - 'section' => 'vote', - 'title' => 'Can like or dislike comments.', - 'perm' => MSZ_PERM_COMMENTS_VOTE, - ], - ], - ], - [ - 'section' => MSZ_PERMS_CHANGELOG, - 'title' => 'Changelog', - 'perms' => [ - [ - 'section' => 'manage-changes', - 'title' => 'Can manage changes.', - 'perm' => MSZ_PERM_CHANGELOG_MANAGE_CHANGES, - ], - [ - 'section' => 'manage-tags', - 'title' => 'Can manage tags.', - 'perm' => MSZ_PERM_CHANGELOG_MANAGE_TAGS, - ], - ], - ], - ]); -} - -function manage_forum_perms_list(array $rawPerms): array { - return manage_perms_calculate($rawPerms, [ - [ - 'section' => MSZ_FORUM_PERMS_GENERAL, - 'title' => 'Forum', - 'perms' => [ - [ - 'section' => 'can-list', - 'title' => 'Can see the forum listed, but not access it.', - 'perm' => MSZ_FORUM_PERM_LIST_FORUM, - ], - [ - 'section' => 'can-view', - 'title' => 'Can view and access the forum.', - 'perm' => MSZ_FORUM_PERM_VIEW_FORUM, - ], - [ - 'section' => 'can-create-topic', - 'title' => 'Can create topics.', - 'perm' => MSZ_FORUM_PERM_CREATE_TOPIC, - ], - [ - 'section' => 'can-move-topic', - 'title' => 'Can move topics between forums.', - 'perm' => MSZ_FORUM_PERM_MOVE_TOPIC, - ], - [ - 'section' => 'can-lock-topic', - 'title' => 'Can lock topics.', - 'perm' => MSZ_FORUM_PERM_LOCK_TOPIC, - ], - [ - 'section' => 'can-sticky-topic', - 'title' => 'Can make topics sticky.', - 'perm' => MSZ_FORUM_PERM_STICKY_TOPIC, - ], - [ - 'section' => 'can-announce-topic', - 'title' => 'Can make topics announcements.', - 'perm' => MSZ_FORUM_PERM_ANNOUNCE_TOPIC, - ], - [ - 'section' => 'can-global-announce-topic', - 'title' => 'Can make topics global announcements.', - 'perm' => MSZ_FORUM_PERM_GLOBAL_ANNOUNCE_TOPIC, - ], - [ - 'section' => 'can-bump-topic', - 'title' => 'Can bump topics without posting a reply.', - 'perm' => MSZ_FORUM_PERM_BUMP_TOPIC, - ], - [ - 'section' => 'can-create-post', - 'title' => 'Can make posts (reply only, if create topic is disallowed).', - 'perm' => MSZ_FORUM_PERM_CREATE_POST, - ], - [ - 'section' => 'can-edit-post', - 'title' => 'Can edit their own posts.', - 'perm' => MSZ_FORUM_PERM_EDIT_POST, - ], - [ - 'section' => 'can-edit-any-post', - 'title' => 'Can edit any posts.', - 'perm' => MSZ_FORUM_PERM_EDIT_ANY_POST, - ], - [ - 'section' => 'can-delete-post', - 'title' => 'Can delete own posts.', - 'perm' => MSZ_FORUM_PERM_DELETE_POST, - ], - [ - 'section' => 'can-delete-any-post', - 'title' => 'Can delete any posts.', - 'perm' => MSZ_FORUM_PERM_DELETE_ANY_POST, - ], - ], - ], - ]); -} diff --git a/templates/_layout/comments.twig b/templates/_layout/comments.twig index 2ed6df0..e940fae 100644 --- a/templates/_layout/comments.twig +++ b/templates/_layout/comments.twig @@ -127,7 +127,7 @@ {% endif %} {% endif %} - {% if perms.can_comment|default(false) %} + {% if perms.can_post|default(false) %} {% endif %} {% if perms.can_delete_any|default(false) or (poster.id|default(0) == user.id and perms.can_delete|default(false)) %} @@ -148,7 +148,7 @@
{% from _self import comments_entry, comments_input %} - {% if user|default(null) is not null and category|default(null) is not null and perms.can_comment|default(false) %} + {% if user|default(null) is not null and category|default(null) is not null and perms.can_post|default(false) %} {{ input_checkbox_raw('', false, 'comment__reply-toggle', '', false, {'id':'comment-reply-toggle-' ~ comment.id}) }} {{ comments_input(category, user, perms, comment) }} {% endif %} @@ -183,7 +183,7 @@
This comment section was locked, .
- {% elseif not perms.can_comment|default(false) %} + {% elseif not perms.can_post|default(false) %}
You are not allowed to post comments.
@@ -199,12 +199,6 @@
{% endif %} - {##} -
{% if posts|length > 0 %} {% from _self import comments_entry %} diff --git a/templates/manage/forum/listing.twig b/templates/manage/forum/listing.twig index 5435c6c..a8247d7 100644 --- a/templates/manage/forum/listing.twig +++ b/templates/manage/forum/listing.twig @@ -10,17 +10,23 @@ {% if calculated_perms is defined %} + + + + + {% for key, value in calculated_perms %} - - + + + {% endfor %}
CategoryAllowDeny
{{ key }}{{ value }}{{ key }}{{ value.allow }}{{ value.deny }}
{% endif %}
- {{ permissions_table(perms) }} + {{ permissions_table(perms_lists, perms_infos) }}
diff --git a/templates/manage/macros.twig b/templates/manage/macros.twig index 95e86d4..5c6572a 100644 --- a/templates/manage/macros.twig +++ b/templates/manage/macros.twig @@ -14,14 +14,14 @@ {% endfor %} {% endmacro %} -{% macro permissions_table(permissions, readonly) %} +{% macro permissions_table(lists, infos, readonly) %} {% from '_layout/input.twig' import input_checkbox %}
- {% for perms in permissions %} + {% for list in lists %}
- {{ perms.title }} + {{ list.title }}
Yes @@ -34,19 +34,19 @@
- {% for perm in perms.perms %} + {% for perm in list.perms %}
{{ perm.title }}
- {{ input_checkbox('perms[' ~ perms.section ~ '][' ~ perm.section ~ '][value]', '', perm.value == 'yes', 'permissions__choice permissions__choice--radio permissions__choice--yes', 'yes', true, null, readonly) }} + {{ input_checkbox(perm.name, '', infos[perm.category].checkAllow(perm.value) ?? false, 'permissions__choice permissions__choice--radio permissions__choice--yes', 'yes', true, null, readonly) }}
- {{ input_checkbox('perms[' ~ perms.section ~ '][' ~ perm.section ~ '][value]', '', perm.value == 'no', 'permissions__choice permissions__choice--radio permissions__choice--no', 'no', true, null, readonly) }} + {{ input_checkbox(perm.name, '', infos[perm.category].checkNeutral(perm.value) ?? true, 'permissions__choice permissions__choice--radio permissions__choice--no', 'no', true, null, readonly) }}
- {{ input_checkbox('perms[' ~ perms.section ~ '][' ~ perm.section ~ '][value]', '', perm.value == 'never', 'permissions__choice permissions__choice--radio permissions__choice--never', 'never', true, null, readonly) }} + {{ input_checkbox(perm.name, '', infos[perm.category].checkDeny(perm.value) ?? false, 'permissions__choice permissions__choice--radio permissions__choice--never', 'never', true, null, readonly) }}
{% endfor %} diff --git a/templates/manage/users/role.twig b/templates/manage/users/role.twig index 4668cb7..3ee317c 100644 --- a/templates/manage/users/role.twig +++ b/templates/manage/users/role.twig @@ -93,7 +93,7 @@
{{ container_title('Permissions') }} - {{ permissions_table(permissions, not can_manage_perms) }} + {{ permissions_table(perms_lists, perms_infos, not can_edit_perms) }}
diff --git a/templates/manage/users/user.twig b/templates/manage/users/user.twig index 49e6e8d..d718e47 100644 --- a/templates/manage/users/user.twig +++ b/templates/manage/users/user.twig @@ -183,11 +183,11 @@ {% endif %} - {% if permissions is not empty %} + {% if perms_lists is defined and perms_infos is defined %}
{{ container_title('Permissions for ' ~ user_info.name ~ ' (' ~ user_info.id ~ ')') }} - {{ permissions_table(permissions, not can_edit_perms) }} + {{ permissions_table(perms_lists, perms_infos, not can_edit_perms) }} {% if can_edit_perms %} {{ input_csrf() }} diff --git a/tools/cron b/tools/cron index bfadfb1..1a83020 100755 --- a/tools/cron +++ b/tools/cron @@ -81,7 +81,8 @@ msz_sched_task_func('Recount forum topics and posts.', true, function() use ($ms msz_sched_task_sql('Clean up expired 2fa tokens.', false, 'DELETE FROM msz_auth_tfa WHERE tfa_created < NOW() - INTERVAL 15 MINUTE'); -// make sure this one remains last +// very heavy stuff that should + msz_sched_task_func('Resync statistics counters.', true, function() use ($msz) { $dbConn = $msz->getDbConn(); $counters = $msz->getCounters(); @@ -139,6 +140,15 @@ msz_sched_task_func('Resync statistics counters.', true, function() use ($msz) { } }); +msz_sched_task_func('Recalculate permissions (maybe)...', false, function() use ($msz) { + $needsRecalc = $msz->getConfig()->getBoolean('perms.needsRecalc'); + if(!$needsRecalc) + return; + + $msz->getConfig()->removeValues('perms.needsRecalc'); + $msz->getPerms()->precalculatePermissions($msz->getForum()); +}); + echo 'Running ' . count($schedTasks) . ' tasks...' . PHP_EOL; $dbConn = $msz->getDbConn(); diff --git a/tools/recalc-perms b/tools/recalc-perms new file mode 100755 index 0000000..ce94adf --- /dev/null +++ b/tools/recalc-perms @@ -0,0 +1,8 @@ +#!/usr/bin/env php +getConfig()->removeValues('perms.needsRecalc'); +$msz->getPerms()->precalculatePermissions($msz->getForum());