From 3bca2779ecdb1607b8c853cd251537dfbb5fe18b Mon Sep 17 00:00:00 2001 From: Spine Date: Sat, 28 Jun 2025 00:17:37 +0000 Subject: [PATCH] toolbox to show privileges added and removed between successive userclasses --- app/Manager/Privilege.php | 34 ++++++++++++ lib/config.php | 16 ++++++ sass/global.scss | 23 ++++++++ sections/tools/index.php | 3 ++ sections/tools/managers/privilege_audit.php | 17 ++++++ templates/admin/privilege-audit.twig | 60 +++++++++++++++++++++ templates/admin/privilege-edit.twig | 3 +- templates/admin/privilege-matrix.twig | 5 +- templates/admin/privilege-usage.twig | 3 +- templates/admin/staff-group.twig | 3 +- tests/phpunit/PrivilegeTest.php | 20 +++++++ 11 files changed, 182 insertions(+), 5 deletions(-) create mode 100644 sections/tools/managers/privilege_audit.php create mode 100644 templates/admin/privilege-audit.twig diff --git a/app/Manager/Privilege.php b/app/Manager/Privilege.php index 6f5ecb963..1f56f315c 100644 --- a/app/Manager/Privilege.php +++ b/app/Manager/Privilege.php @@ -118,6 +118,40 @@ class Privilege extends \Gazelle\BaseManager { return self::$db->to_array(false, MYSQLI_ASSOC); } + /** + * Given a list of userclasses [A, B, C, D], return a list of pairs + * [ ['lower' => 0, 'higher' => A], ['lower' => A, 'higher' => B], ..., ['lower' => C, 'higher' => D] ] + * These will be used by compareUserclass() + */ + public function pairUserclass(array $userclassList): array { + $result = []; + array_unshift($userclassList, 0); + for ($n = 0, $end = count($userclassList) - 1; $n < $end; ++$n) { + $result[] = [ + 'lower' => $userclassList[$n], + 'higher' => $userclassList[$n + 1], + ]; + } + return $result; + } + + /** + * Given two userclasses (permissions.ID) return an array of + * all privileges that have been added to the second class over the first, + * and the privileges which are in the first class but not in the second. + */ + public function compareUserclass(int $lowerPermissionID, int $higherPermissionID): array { + $list = $this->privilege(); + $lower = array_keys(array_filter($list, fn ($i) => in_array($lowerPermissionID, $i['can']))); + $higher = array_keys(array_filter($list, fn ($i) => in_array($higherPermissionID, $i['can']))); + sort($lower); + sort($higher); + return [ + 'add' => array_values(array_diff($higher, $lower)), + 'remove' => array_values(array_diff($lower, $higher)), + ]; + } + /** * The list of defined privileges. The `can` field * in the returned array acts as a sparse matrix. diff --git a/lib/config.php b/lib/config.php index c7a471773..c751e14fc 100644 --- a/lib/config.php +++ b/lib/config.php @@ -829,6 +829,22 @@ defined('FORUM_MOD') or define('FORUM_MOD', 21); defined('MOD') or define('MOD', 11); defined('SYSOP') or define('SYSOP', 15); +// When adding a new privilege userclasses, care must be taken to +// ensure all classes beyond the initial class receive it. The +// Privileges Audit page is configured entirely by this definition. +defined('USERCLASS_AUDIT') or define('USERCLASS_AUDIT', + [ + [ + 'heading' => 'User classes', + 'compare' => [USER, MEMBER, POWER, ELITE, TORRENT_MASTER, POWER_TM, ELITE_TM], + ], + [ + 'heading' => 'Staff classes', + 'compare' => [FORUM_MOD, MOD, SYSOP], + ], + ] +); + // Permission ID of secondary class. defined('DONOR') or define('DONOR', 42); defined('FLS_TEAM') or define('FLS_TEAM', 23); diff --git a/sass/global.scss b/sass/global.scss index ace1dccf5..00db4d5d2 100644 --- a/sass/global.scss +++ b/sass/global.scss @@ -1132,6 +1132,29 @@ form.analysis-case { justify-self: end; } +.compare-grid { + column-gap: 10px; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); +} +.pair-grid { + border: 1px solid #808080; + column-gap: 5px; + display: grid; + grid-template-columns: 50% 50%; + margin-bottom: 12px; +} +.pair-grid div.added ul { + list-style-type: '+ '; +} +.pair-grid div.removed ul { + list-style-type: '– '; +} +.pair-grid .no-change { + padding: 5px; + text-align: left; +} + #more-news-loader { cursor: pointer; } diff --git a/sections/tools/index.php b/sections/tools/index.php index a5aa112a1..e5fa57e75 100644 --- a/sections/tools/index.php +++ b/sections/tools/index.php @@ -152,6 +152,9 @@ switch ($_REQUEST['action'] ?? '') { case 'privilege-alter': include_once 'managers/userclass_alter.php'; break; + case 'privilege-audit': + include_once 'managers/privilege_audit.php'; + break; case 'privilege_matrix': include_once 'managers/privilege_matrix.php'; break; diff --git a/sections/tools/managers/privilege_audit.php b/sections/tools/managers/privilege_audit.php new file mode 100644 index 000000000..c4bc853e6 --- /dev/null +++ b/sections/tools/managers/privilege_audit.php @@ -0,0 +1,17 @@ +permitted('admin_manage_permissions')) { + Error403::error(); +} + +echo $Twig->render('admin/privilege-audit.twig', [ + 'privilege_manager' => new Manager\Privilege(), + 'user_manager' => new Manager\User(), + 'config' => USERCLASS_AUDIT, +]); diff --git a/templates/admin/privilege-audit.twig b/templates/admin/privilege-audit.twig new file mode 100644 index 000000000..b3fd5c923 --- /dev/null +++ b/templates/admin/privilege-audit.twig @@ -0,0 +1,60 @@ +{{ header('Audit Userclass Privileges') }} +
+ +
+
+{% for section in config %} +

{{ section.heading }}

+{% for comparison in privilege_manager.pairUserclass(section.compare) %} +{% if loop.first %} +
+{% endif %} +
+

+{%- if comparison.lower -%} +{{ user_manager.userclassName(comparison.lower) }} 🠞 {{ user_manager.userclassName(comparison.higher) -}} +{% else -%} +Initial 🠞 {{ user_manager.userclassName(comparison.higher) -}} +{% endif -%} +

+{% set result = privilege_manager.compareUserclass(comparison.lower, comparison.higher) %} +
+
+
    +{% for r in result.add %} +
  • {{ r }}
  • +{% else %} +{% if result.remove %} +
  • None
  • +{% endif %} +{% endfor %} +
+
+
+
    +{% for r in result.remove %} +
  • {{ r }}
  • +{% else %} +{% if result.add %} +
  • None
  • +{% endif %} +{% endfor %} +
+
+{% if not (result.add or result.remove) %} + No change +{% endif %} +
+
+{% if loop.last %} +
+{% endif %} +{% endfor %} +{% endfor %} +{{ footer() }} diff --git a/templates/admin/privilege-edit.twig b/templates/admin/privilege-edit.twig index ec7cf5d02..ce9febdf8 100644 --- a/templates/admin/privilege-edit.twig +++ b/templates/admin/privilege-edit.twig @@ -2,8 +2,9 @@ {{ header('Manage Permissions') }} diff --git a/templates/admin/privilege-matrix.twig b/templates/admin/privilege-matrix.twig index 0da5795a4..e70a1193a 100644 --- a/templates/admin/privilege-matrix.twig +++ b/templates/admin/privilege-matrix.twig @@ -1,14 +1,15 @@ -{{ header('Privilege Matrix') }} +{{ header('Privileges Matrix') }}
-

Privilege Matrix

+

Privileges Matrix

diff --git a/templates/admin/privilege-usage.twig b/templates/admin/privilege-usage.twig index 206870a1e..ab5b4c542 100644 --- a/templates/admin/privilege-usage.twig +++ b/templates/admin/privilege-usage.twig @@ -2,7 +2,8 @@
Primary Class ★

Privilege