Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[permissions] move permissions to database, add GUI permissions editor #1149

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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_ex((int)$image_id), true));
}
// Reload users from the database in case they were modified
UserClass::loadClasses();
$page = new Page();

$_tracer->end(); # setUp
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;
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
Loading