Skip to content

Commit

Permalink
[permissions] move permissions to database, add GUI permissions editor
Browse files Browse the repository at this point in the history
  • Loading branch information
discomrade committed May 8, 2024
1 parent 124f3eb commit 2eeb314
Show file tree
Hide file tree
Showing 10 changed files with 585 additions and 79 deletions.
1 change: 1 addition & 0 deletions core/permissions.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ abstract class Permissions

/** enable or disable extensions */
public const MANAGE_EXTENSION_LIST = "manage_extension_list";
public const MANAGE_PERMISSION_LIST = "manage_permission_list";
public const MANAGE_ALIAS_LIST = "manage_alias_list";
public const MANAGE_AUTO_TAG = "manage_auto_tag";
public const MASS_TAG_EDIT = "mass_tag_edit";
Expand Down
2 changes: 2 additions & 0 deletions core/testcase.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public function setUp(): void
foreach ($database->get_col("SELECT id FROM images") as $image_id) {
send_event(new ImageDeletionEvent(Image::by_id((int)$image_id), true));
}
// Reload users from the database in case they were modified
UserClass::loadClasses();

$_tracer->end(); # setUp
$_tracer->begin("test");
Expand Down
11 changes: 0 additions & 11 deletions core/tests/UserClassTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,6 @@

class UserClassTest extends ShimmiePHPUnitTestCase
{
public function test_new_class(): void
{
$cls = new UserClass("user2", "user", [
Permissions::CREATE_COMMENT => true,
Permissions::BIG_SEARCH => false,
]);
$this->assertEquals("user2", $cls->name);
$this->assertTrue($cls->can(Permissions::CREATE_COMMENT));
$this->assertFalse($cls->can(Permissions::BIG_SEARCH));
}

public function test_not_found(): void
{
$cls = UserClass::$known_classes['user'];
Expand Down
2 changes: 1 addition & 1 deletion core/user.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public function __construct(array $row)
$this->email = $row['email'];
$this->join_date = $row['joindate'];
$this->passhash = $row['pass'];

//var_dump(array_keys(UserClass::$known_classes));
if (array_key_exists($row["class"], UserClass::$known_classes)) {
$this->class = UserClass::$known_classes[$row["class"]];
} else {
Expand Down
107 changes: 40 additions & 67 deletions core/userclass.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace Shimmie2;

use FFSPHP\PDO;

use GQLA\Type;
use GQLA\Field;

Expand All @@ -19,21 +21,29 @@ class UserClass
#[Field]
public ?string $name = null;
public ?UserClass $parent = null;
public bool $core = false;

/** @var array<string, bool> */
public array $abilities = [];

/**
* @param array<string, bool> $abilities
*/
public function __construct(string $name, string $parent = null, array $abilities = [])
public function __construct(string $name)
{
global $database;

$this->name = $name;
$this->abilities = $abilities;
$class = $database->execute("SELECT * FROM permissions WHERE class = :class", ["class" => $name])->fetch(PDO::FETCH_ASSOC);

if (!is_null($parent)) {
$this->parent = static::$known_classes[$parent];
if (!is_null($class["parent"])) {
$this->parent = static::$known_classes[$class["parent"]];
}
$this->core = (bool)$class["core"];

unset($class["id"]);
unset($class["class"]);
unset($class["parent"]);
unset($class["core"]);

$this->abilities = $class;

static::$known_classes[$name] = $this;
}
Expand All @@ -58,10 +68,16 @@ public function permissions(): array
*/
public function can(string $ability): bool
{
if (array_key_exists($ability, $this->abilities)) {
return $this->abilities[$ability];
// hellbanned is a snowflake, it isn't really a "permission" so much as
// "a special behaviour which applies to one particular user class"
if ($this->name == "admin" && $ability != "hellbanned") {
return true;
} elseif (array_key_exists($ability, $this->abilities) && $this->abilities[$ability]) {
return true;
} elseif (!is_null($this->parent)) {
return $this->parent->can($ability);
} elseif (array_key_exists($ability, $this->abilities)) {
return false;
} else {
$min_dist = 9999;
$min_ability = null;
Expand All @@ -75,63 +91,20 @@ public function can(string $ability): bool
throw new ServerError("Unknown ability '$ability'. Did the developer mean '$min_ability'?");
}
}
}

$_all_false = [];
$_all_true = [];
foreach ((new \ReflectionClass(Permissions::class))->getConstants() as $k => $v) {
assert(is_string($v));
$_all_false[$v] = false;
$_all_true[$v] = true;
// clear and load classes from the database.
public static function loadClasses(): void
{
global $database;

// clear any existing classes to avoid complications with parent classes
foreach(static::$known_classes as $k => $v) {
unset(static::$known_classes[$k]);
}

$classes = $database->get_col("SELECT class FROM permissions WHERE 1=1 ORDER BY id");
foreach($classes as $class) {
new UserClass($class);
}
}
}
// hellbanned is a snowflake, it isn't really a "permission" so much as
// "a special behaviour which applies to one particular user class"
$_all_true[Permissions::HELLBANNED] = false;
new UserClass("base", null, $_all_false);
new UserClass("admin", null, $_all_true);
unset($_all_true);
unset($_all_false);

// Ghost users can't do anything
new UserClass("ghost", "base", [
Permissions::READ_PM => true,
]);

// Anonymous users can't do anything by default, but
// the admin might grant them some permissions
new UserClass("anonymous", "base", [
Permissions::CREATE_USER => true,
]);

new UserClass("user", "base", [
Permissions::BIG_SEARCH => true,
Permissions::CREATE_IMAGE => true,
Permissions::CREATE_COMMENT => true,
Permissions::EDIT_IMAGE_TAG => true,
Permissions::EDIT_IMAGE_SOURCE => true,
Permissions::EDIT_IMAGE_TITLE => true,
Permissions::EDIT_IMAGE_RELATIONSHIPS => true,
Permissions::EDIT_IMAGE_ARTIST => true,
Permissions::CREATE_IMAGE_REPORT => true,
Permissions::EDIT_IMAGE_RATING => true,
Permissions::EDIT_FAVOURITES => true,
Permissions::CREATE_VOTE => true,
Permissions::SEND_PM => true,
Permissions::READ_PM => true,
Permissions::SET_PRIVATE_IMAGE => true,
Permissions::PERFORM_BULK_ACTIONS => true,
Permissions::BULK_DOWNLOAD => true,
Permissions::CHANGE_USER_SETTING => true,
Permissions::FORUM_CREATE => true,
Permissions::NOTES_CREATE => true,
Permissions::NOTES_EDIT => true,
Permissions::NOTES_REQUEST => true,
Permissions::POOLS_CREATE => true,
Permissions::POOLS_UPDATE => true,
]);

new UserClass("hellbanned", "user", [
Permissions::HELLBANNED => true,
]);

@include_once "data/config/user-classes.conf.php";
20 changes: 20 additions & 0 deletions ext/perm_manager/info.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Shimmie2;

class PermManagerInfo extends ExtensionInfo
{
public const KEY = "perm_manager";

public string $key = self::KEY;
public string $name = "Permission Manager";
public string $url = self::SHIMMIE_URL;
public array $authors = self::SHISH_AUTHOR;
public string $license = self::LICENSE_GPLV2;
public ExtensionVisibility $visibility = ExtensionVisibility::ADMIN;
public ExtensionCategory $category = ExtensionCategory::ADMIN;
public string $description = "Allows the admin to modify user class permissions.";
public bool $core = true;
}
Loading

0 comments on commit 2eeb314

Please sign in to comment.