3, 'n' => 'Roles', 'd' => '']); Database::exec('INSERT INTO role_x_user (userid, roleid) VALUES (:u, :r)', ['u' => 2, 'r' => 3]); Database::exec('INSERT INTO role_x_permission (roleid, permissionid) VALUES (:r, :p)', ['r' => 3, 'p' => 'permissionmanager.roles.*']); // Also add one row in role_x_location to satisfy INNER JOIN in location-specific queries; for ANY (null) use NULL Database::exec('INSERT INTO role_x_location (roleid, locationid) VALUES (:r, NULL)', ['r' => 3]); $this->assertTrue(PermissionUtil::userHasPermission(2, 'permissionmanager.roles.*', null)); // cached second call should also return true $this->assertTrue(PermissionUtil::userHasPermission(2, 'permissionmanager.roles.*', null)); } public function testUserHasPermissionLocationSpecificWithParentInheritance(): void { User::$id = 3; require_once __DIR__ . '/../../../modules-available/permissionmanager/inc/permissionutil.inc.php'; // Seed: create role 4 granting users.view at location 2 (parent of 4) Database::exec('INSERT INTO role (roleid, rolename, builtin, roledescription) VALUES (:id, :n, 0, :d)', ['id' => 4, 'n' => 'Users', 'd' => '']); Database::exec('INSERT INTO role_x_user (userid, roleid) VALUES (:u, :r)', ['u' => 3, 'r' => 4]); Database::exec('INSERT INTO role_x_permission (roleid, permissionid) VALUES (:r, :p)', ['r' => 4, 'p' => 'permissionmanager.users.view']); Database::exec('INSERT INTO role_x_location (roleid, locationid) VALUES (:r, :l)', ['r' => 4, 'l' => 2]); // userHasPermission should return true for location 4 by inheritance (2 is parent of 4 in seed) $this->assertTrue(PermissionUtil::userHasPermission(3, 'permissionmanager.users.view', 4)); // And false for unrelated location (3) $this->assertFalse(PermissionUtil::userHasPermission(3, 'permissionmanager.users.view', 3)); } public function testAdminBypassForPermissionManager(): void { // userid 1 always allowed for permissionmanager.* User::$id = 1; require_once __DIR__ . '/../../../modules-available/permissionmanager/inc/permissionutil.inc.php'; $this->assertTrue(PermissionUtil::userHasPermission(1, 'permissionmanager.*', null)); $this->assertTrue(PermissionUtil::userHasPermission(1, 'permissionmanager.roles.edit', 5)); } public function testGetAllowedLocationsGlobalAndSpecific(): void { User::$id = 5; require_once __DIR__ . '/../../../modules-available/permissionmanager/inc/permissionutil.inc.php'; // Case 1: global permission (locationid NULL -> treated as 0 and expands to all) Database::exec('INSERT INTO role (roleid, rolename, builtin, roledescription) VALUES (:id, :n, 0, :d)', ['id' => 5, 'n' => 'Global', 'd' => '']); Database::exec('INSERT INTO role_x_user (userid, roleid) VALUES (:u, :r)', ['u' => 5, 'r' => 5]); Database::exec('INSERT INTO role_x_permission (roleid, permissionid) VALUES (:r, :p)', ['r' => 5, 'p' => 'permissionmanager.users.view']); Database::exec('INSERT INTO role_x_location (roleid, locationid) VALUES (:r, NULL)', ['r' => 5]); $locs = PermissionUtil::getAllowedLocations(5, 'permissionmanager.users.view'); $this->assertContains(0, $locs); $this->assertContains(1, $locs); $this->assertContains(2, $locs); $this->assertContains(4, $locs); // Case 2: specific base location expands with children (location 2 -> includes 4) Database::exec('INSERT INTO role (roleid, rolename, builtin, roledescription) VALUES (:id, :n, 0, :d)', ['id' => 6, 'n' => 'Loc', 'd' => '']); Database::exec('INSERT INTO role_x_user (userid, roleid) VALUES (:u, :r)', ['u' => 5, 'r' => 6]); Database::exec('INSERT INTO role_x_permission (roleid, permissionid) VALUES (:r, :p)', ['r' => 6, 'p' => 'permissionmanager.roles.*']); Database::exec('INSERT INTO role_x_location (roleid, locationid) VALUES (:r, :l)', ['r' => 6, 'l' => 2]); $locs2 = PermissionUtil::getAllowedLocations(5, 'permissionmanager.roles.edit'); $this->assertNotContains(0, $locs2); $this->assertContains(2, $locs2); $this->assertContains(4, $locs2); } }