From aea20b7a10b1869db9ca464c547f267134f97ea2 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 23 Apr 2024 12:14:28 +0200 Subject: [PATCH] ACL rewrite --- Core/API/ApiKeyAPI.class.php | 17 ++- Core/API/DatabaseAPI.class.php | 26 ++-- Core/API/GpgKeyAPI.class.php | 19 ++- Core/API/GroupsAPI.class.php | 70 +++++++--- Core/API/Info.class.php | 4 + Core/API/LanguageAPI.class.php | 12 ++ Core/API/LogsAPI.class.php | 9 +- Core/API/MailAPI.class.php | 25 +++- Core/API/PermissionAPI.class.php | 50 ++++--- Core/API/Request.class.php | 11 +- Core/API/RoutesAPI.class.php | 85 +++++++----- Core/API/Search.class.php | 5 + Core/API/SettingsAPI.class.php | 17 ++- Core/API/Stats.class.php | 13 +- Core/API/Swagger.class.php | 36 ++++- Core/API/TemplateAPI.class.php | 8 +- Core/API/TfaAPI.class.php | 25 ++-- Core/API/Traits/Pagination.trait.php | 30 +---- Core/API/UserAPI.class.php | 124 +++++++++++++++--- Core/API/VerifyCaptcha.class.php | 4 + Core/Configuration/CreateDatabase.class.php | 19 +-- .../src/views/access-control-list.js | 2 +- .../src/views/settings/settings.js | 4 +- 23 files changed, 435 insertions(+), 180 deletions(-) diff --git a/Core/API/ApiKeyAPI.class.php b/Core/API/ApiKeyAPI.class.php index 0dcde96..5510394 100644 --- a/Core/API/ApiKeyAPI.class.php +++ b/Core/API/ApiKeyAPI.class.php @@ -33,7 +33,6 @@ namespace Core\API\ApiKey { use Core\API\Traits\Pagination; use Core\Driver\SQL\Condition\Compare; use Core\Driver\SQL\Condition\CondAnd; - use Core\Driver\SQL\Query\Insert; use Core\Objects\Context; use Core\Objects\DatabaseEntity\ApiKey; @@ -61,8 +60,8 @@ namespace Core\API\ApiKey { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [], "Allows users to create new API-Keys", true); + public static function getDescription(): string { + return "Allows users to create new API-Keys"; } } @@ -106,8 +105,8 @@ namespace Core\API\ApiKey { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [], "Allows users to fetch new API-Keys", true); + public static function getDescription(): string { + return "Allows users to fetch their API-Keys"; } } @@ -134,8 +133,8 @@ namespace Core\API\ApiKey { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [], "Allows users to refresh API-Keys", true); + public static function getDescription(): string { + return "Allows users to refresh their API-Keys"; } } @@ -158,8 +157,8 @@ namespace Core\API\ApiKey { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [], "Allows users to revoke API-Keys", true); + public static function getDescription(): string { + return "Allows users to revoke their API-Keys"; } } } \ No newline at end of file diff --git a/Core/API/DatabaseAPI.class.php b/Core/API/DatabaseAPI.class.php index d98ba30..ee84757 100644 --- a/Core/API/DatabaseAPI.class.php +++ b/Core/API/DatabaseAPI.class.php @@ -2,8 +2,14 @@ namespace Core\API { + use Core\Objects\Context; + abstract class DatabaseAPI extends Request { + public function __construct(Context $context, bool $externalCall = false, array $params = array()) { + parent::__construct($context, $externalCall, $params); + } + } } @@ -12,8 +18,6 @@ namespace Core\API\Database { use Core\API\DatabaseAPI; use Core\API\Parameter\RegexType; - use Core\API\Parameter\StringType; - use Core\Driver\SQL\Query\Insert; use Core\Objects\Context; use Core\Objects\DatabaseEntity\Controller\DatabaseEntity; use Core\Objects\DatabaseEntity\Group; @@ -27,14 +31,16 @@ namespace Core\API\Database { protected function _execute(): bool { $sql = $this->context->getSQL(); $status = $sql->getStatus(); - $this->result["status"] = $status; - return true; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to view the database status", true); + public static function getDescription(): string { + return "Allows users to view the database status"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } @@ -102,8 +108,12 @@ namespace Core\API\Database { return true; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to migrate the database structure", true); + public static function getDescription(): string { + return "Allows users to migrate the database structure"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } } \ No newline at end of file diff --git a/Core/API/GpgKeyAPI.class.php b/Core/API/GpgKeyAPI.class.php index 80d6d90..e195f83 100644 --- a/Core/API/GpgKeyAPI.class.php +++ b/Core/API/GpgKeyAPI.class.php @@ -21,7 +21,6 @@ namespace Core\API\GpgKey { use Core\API\Parameter\StringType; use Core\API\Template\Render; use Core\Driver\SQL\Condition\Compare; - use Core\Driver\SQL\Query\Insert; use Core\Objects\Context; use Core\Objects\DatabaseEntity\GpgKey; use Core\Objects\DatabaseEntity\User; @@ -137,8 +136,8 @@ namespace Core\API\GpgKey { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [], "Allows users to import gpg keys for a secure e-mail communication", true); + public static function getDescription(): string { + return "Allows users to import gpg keys for a secure e-mail communication"; } } @@ -170,8 +169,8 @@ namespace Core\API\GpgKey { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [], "Allows users to unlink gpg keys from their profile", true); + public static function getDescription(): string { + return "Allows users to unlink gpg keys from their profile"; } } @@ -219,6 +218,10 @@ namespace Core\API\GpgKey { return $this->success; } + + public static function getDescription(): string { + return "Allows users to confirm their imported gpg key"; + } } class Download extends GpgKeyAPI { @@ -289,7 +292,9 @@ namespace Core\API\GpgKey { header("Content-Disposition: attachment; filename=\"$fileName\""); die($key); } + + public static function getDescription(): string { + return "Allows users to download any gpg public key"; + } } - - } \ No newline at end of file diff --git a/Core/API/GroupsAPI.class.php b/Core/API/GroupsAPI.class.php index d27dee1..3f8c7f4 100644 --- a/Core/API/GroupsAPI.class.php +++ b/Core/API/GroupsAPI.class.php @@ -56,18 +56,14 @@ namespace Core\API\Groups { use Core\API\GroupsAPI; use Core\API\Parameter\Parameter; use Core\API\Parameter\RegexType; - use Core\API\Parameter\StringType; use Core\API\Traits\Pagination; use Core\Driver\SQL\Column\Column; use Core\Driver\SQL\Condition\Compare; - use Core\Driver\SQL\Condition\CondAnd; use Core\Driver\SQL\Expression\Alias; use Core\Driver\SQL\Expression\Count; use Core\Driver\SQL\Join\InnerJoin; - use Core\Driver\SQL\Query\Insert; use Core\Objects\Context; use Core\Objects\DatabaseEntity\Group; - use Core\Objects\DatabaseEntity\Route; use Core\Objects\DatabaseEntity\User; class Fetch extends GroupsAPI { @@ -113,8 +109,12 @@ namespace Core\API\Groups { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN, Group::SUPPORT], "Allows users to fetch available groups", true); + public static function getDescription(): string { + return "Allows users to fetch available groups"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN, Group::SUPPORT]; } } @@ -135,8 +135,12 @@ namespace Core\API\Groups { return true; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN, Group::SUPPORT], "Allows users to get details about a group", true); + public static function getDescription(): string { + return "Allows users to get details about a group"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN, Group::SUPPORT]; } } @@ -155,7 +159,7 @@ namespace Core\API\Groups { $nmTable = User::getHandler($sql)->getNMRelation("groups")->getTableName(); $condition = new Compare("group_id", $this->getParam("id")); $nmJoin = new InnerJoin($nmTable, "$nmTable.user_id", "User.id"); - if (!$this->initPagination($sql, User::class, $condition, 100, [$nmJoin])) { + if (!$this->initPagination($sql, User::class, $condition, [$nmJoin])) { return false; } @@ -174,8 +178,12 @@ namespace Core\API\Groups { return true; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN, Group::SUPPORT], "Allows users to fetch members of a group", true); + public static function getDescription(): string { + return "Allows users to fetch members of a group"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN, Group::SUPPORT]; } } @@ -210,8 +218,12 @@ namespace Core\API\Groups { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to create a new group", true); + public static function getDescription(): string { + return "Allows users to create a new group"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } @@ -252,8 +264,12 @@ namespace Core\API\Groups { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to update existing groups", true); + public static function getDescription(): string { + return "Allows users to update existing groups"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } @@ -280,8 +296,12 @@ namespace Core\API\Groups { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to delete a group", true); + public static function getDescription(): string { + return "Allows users to delete a group"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } @@ -321,8 +341,12 @@ namespace Core\API\Groups { } } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to add members to a group", true); + public static function getDescription(): string { + return "Allows users to add members to a group"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } @@ -362,8 +386,12 @@ namespace Core\API\Groups { } } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to remove members from a group", true); + public static function getDescription(): string { + return "Allows users to remove members from a group"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } } \ No newline at end of file diff --git a/Core/API/Info.class.php b/Core/API/Info.class.php index 688ac50..f4a4483 100644 --- a/Core/API/Info.class.php +++ b/Core/API/Info.class.php @@ -23,4 +23,8 @@ class Info extends Request { return true; } + + public static function getDescription(): string { + return "Returns general information about the Site"; + } } \ No newline at end of file diff --git a/Core/API/LanguageAPI.class.php b/Core/API/LanguageAPI.class.php index 75451af..bdb3644 100644 --- a/Core/API/LanguageAPI.class.php +++ b/Core/API/LanguageAPI.class.php @@ -48,6 +48,10 @@ namespace Core\API\Language { return $this->success; } + + public static function getDescription(): string { + return "Allows users to retrieve a list of built-in languages"; + } } class Set extends LanguageAPI { @@ -109,6 +113,10 @@ namespace Core\API\Language { $this->result["language"] = $this->language->jsonSerialize(); return $this->success; } + + public static function getDescription(): string { + return "Allows users to set their preferred language"; + } } class GetEntries extends LanguageAPI { @@ -172,5 +180,9 @@ namespace Core\API\Language { } return true; } + + public static function getDescription(): string { + return "Returns a set of translations for the given language and module"; + } } } \ No newline at end of file diff --git a/Core/API/LogsAPI.class.php b/Core/API/LogsAPI.class.php index 7bb50c2..ff79b8c 100644 --- a/Core/API/LogsAPI.class.php +++ b/Core/API/LogsAPI.class.php @@ -24,7 +24,6 @@ namespace Core\API\Logs { use Core\Driver\SQL\Condition\CondIn; use Core\Driver\SQL\Condition\CondLike; use Core\Driver\SQL\Condition\CondOr; - use Core\Driver\SQL\Query\Insert; use Core\Objects\Context; use Core\Objects\DatabaseEntity\Group; use Core\Objects\DatabaseEntity\SystemLog; @@ -148,8 +147,12 @@ namespace Core\API\Logs { return true; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to fetch system logs", true); + public static function getDescription(): string { + return "Allows users to fetch system logs"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } } \ No newline at end of file diff --git a/Core/API/MailAPI.class.php b/Core/API/MailAPI.class.php index f2aea97..055bce9 100644 --- a/Core/API/MailAPI.class.php +++ b/Core/API/MailAPI.class.php @@ -46,7 +46,6 @@ namespace Core\API\Mail { use Core\API\MailAPI; use Core\API\Parameter\Parameter; use Core\API\Parameter\StringType; - use Core\Driver\SQL\Query\Insert; use Core\Objects\DatabaseEntity\Group; use Core\Objects\DatabaseEntity\MailQueueItem; use DateTimeInterface; @@ -83,8 +82,12 @@ namespace Core\API\Mail { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to send a test email to verify configuration", true); + public static function getDescription(): string { + return "Allows users to send a test email to verify configuration"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } @@ -223,6 +226,14 @@ namespace Core\API\Mail { return $this->success; } + + public static function getDescription(): string { + return "Sends an email or puts it into an asynchronous queue. This function is for internal use only."; + } + + public static function hasConfigurablePermissions(): bool { + return false; + } } class SendQueue extends MailAPI { @@ -282,5 +293,13 @@ namespace Core\API\Mail { return $this->success; } + + public static function getDescription(): string { + return "Processes the asynchronous mailing queue. This function is for internal use only."; + } + + public static function hasConfigurablePermissions(): bool { + return false; + } } } \ No newline at end of file diff --git a/Core/API/PermissionAPI.class.php b/Core/API/PermissionAPI.class.php index ef3d625..0d4310e 100644 --- a/Core/API/PermissionAPI.class.php +++ b/Core/API/PermissionAPI.class.php @@ -23,6 +23,7 @@ namespace Core\API { protected function isRestricted(string $method): bool { return in_array(strtolower($method), ["permission/update", "permission/delete"]); + // TODO: access the "hasConfigurablePermissions" here. } } } @@ -36,7 +37,6 @@ namespace Core\API\Permission { use Core\Driver\SQL\Column\Column; use Core\Driver\SQL\Condition\CondIn; use Core\Driver\SQL\Condition\CondLike; - use Core\Driver\SQL\Query\Insert; use Core\Driver\SQL\Strategy\UpdateStrategy; use Core\Objects\Context; use Core\Objects\DatabaseEntity\Group; @@ -93,6 +93,14 @@ namespace Core\API\Permission { return $this->success; } + + public static function getDescription(): string { + return "Checks whether a user is permitted to access a given API-method"; + } + + public static function hasConfigurablePermissions(): bool { + return false; + } } class Fetch extends PermissionAPI { @@ -146,8 +154,12 @@ namespace Core\API\Permission { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to fetch API permissions", true); + public static function getDescription(): string { + return "Allows users to fetch API permissions"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } @@ -199,12 +211,16 @@ namespace Core\API\Permission { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow( - self::getEndpoint(), [Group::ADMIN], - "Allows users to modify API permissions. This is restricted to the administrator and cannot be changed", - true - ); + public static function getDescription(): string { + return "Allows users to modify API permissions. This is restricted to the administrator and cannot be changed"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; + } + + public static function hasConfigurablePermissions(): bool { + return false; } } @@ -250,12 +266,16 @@ namespace Core\API\Permission { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow( - self::getEndpoint(), [Group::ADMIN], - "Allows users to delete API permissions. This is restricted to the administrator and cannot be changed", - true - ); + public static function getDescription(): string { + return "Allows users to delete API permissions. This is restricted to the administrator and cannot be changed"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; + } + + public static function hasConfigurablePermissions(): bool { + return false; } } } \ No newline at end of file diff --git a/Core/API/Request.class.php b/Core/API/Request.class.php index 1fc50f9..65edef6 100644 --- a/Core/API/Request.class.php +++ b/Core/API/Request.class.php @@ -3,7 +3,6 @@ namespace Core\API; use Core\Driver\Logger\Logger; -use Core\Driver\SQL\Query\Insert; use Core\Objects\Context; use Core\Objects\DatabaseEntity\TwoFactorToken; use Core\Objects\TwoFactor\KeyBasedTwoFactorToken; @@ -131,8 +130,14 @@ abstract class Request { protected abstract function _execute(): bool; - // TODO: replace this function with two abstract methods: getDefaultPermittedGroups and getDescription - public static function getDefaultACL(Insert $insert): void { } + public static abstract function getDescription(): string; + public static function getDefaultPermittedGroups(): array { + return []; + } + + public static function hasConfigurablePermissions(): bool { + return true; + } protected function check2FA(?TwoFactorToken $tfaToken = null): bool { diff --git a/Core/API/RoutesAPI.class.php b/Core/API/RoutesAPI.class.php index 0a0bc2d..109e6e1 100644 --- a/Core/API/RoutesAPI.class.php +++ b/Core/API/RoutesAPI.class.php @@ -70,7 +70,6 @@ namespace Core\API\Routes { use Core\API\Parameter\Parameter; use Core\API\Parameter\StringType; use Core\API\RoutesAPI; - use Core\Driver\SQL\Query\Insert; use Core\Objects\Context; use Core\Objects\DatabaseEntity\Group; use Core\Objects\DatabaseEntity\Route; @@ -98,8 +97,12 @@ namespace Core\API\Routes { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to fetch site routing", true); + public static function getDescription(): string { + return "Allows users to fetch site routing."; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } @@ -130,13 +133,12 @@ namespace Core\API\Routes { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow( - self::getEndpoint(), - [Group::ADMIN, Group::MODERATOR], - "Allows users to fetch a single route", - true - ); + public static function getDescription(): string { + return "Allows users to fetch a single route"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN, Group::MODERATOR]; } } @@ -177,8 +179,12 @@ namespace Core\API\Routes { return $this->success && $this->regenerateCache(); } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to add new routes", true); + public static function getDescription(): string { + return "Allows users to add new routes"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } @@ -234,8 +240,12 @@ namespace Core\API\Routes { return $this->success && $this->regenerateCache(); } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to update existing routes", true); + public static function getDescription(): string { + return "Allows users to update existing routes"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } @@ -264,8 +274,12 @@ namespace Core\API\Routes { return $this->success && $this->regenerateCache(); } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to remove routes", true); + public static function getDescription(): string { + return "Allows users to remove routes"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } @@ -281,8 +295,12 @@ namespace Core\API\Routes { return $this->toggleRoute($id, true); } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to enable a route", true); + public static function getDescription(): string { + return "Allows users to enable a route"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } @@ -298,8 +316,12 @@ namespace Core\API\Routes { return $this->toggleRoute($id, false); } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to disable a route", true); + public static function getDescription(): string { + return "Allows users to disable a route"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } @@ -342,11 +364,12 @@ namespace Core\API\Routes { return $this->router; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), - [Group::ADMIN, Group::SUPPORT], - "Allows users to regenerate the routing cache", true - ); + public static function getDescription(): string { + return "Allows users to regenerate the routing cache"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN, Group::SUPPORT]; } } @@ -368,12 +391,12 @@ namespace Core\API\Routes { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow("routes/check", - [Group::ADMIN, Group::MODERATOR], - "Users with this permission can see, if a route pattern is matched with the given path for debugging purposes", - true - ); + public static function getDescription(): string { + return "This endpoint grants the ability to check, if a route pattern is matched with the given path for debugging purposes"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN, Group::MODERATOR]; } } } diff --git a/Core/API/Search.class.php b/Core/API/Search.class.php index e49d3d7..a133a4b 100644 --- a/Core/API/Search.class.php +++ b/Core/API/Search.class.php @@ -7,6 +7,7 @@ use Core\Objects\Context; use Core\Objects\Search\Searchable; use Core\Objects\Search\SearchQuery; +// TODO: rework this entirely class Search extends Request { public function __construct(Context $context, bool $externalCall = false) { @@ -38,4 +39,8 @@ class Search extends Request { return true; } + + public static function getDescription(): string { + return "Searches the site documents and returns a list of matching routes"; + } } \ No newline at end of file diff --git a/Core/API/SettingsAPI.class.php b/Core/API/SettingsAPI.class.php index cb1cb0f..2cd6dd2 100644 --- a/Core/API/SettingsAPI.class.php +++ b/Core/API/SettingsAPI.class.php @@ -39,7 +39,6 @@ namespace Core\API\Settings { use Core\Driver\SQL\Column\Column; use Core\Driver\SQL\Condition\CondBool; use Core\Driver\SQL\Condition\CondIn; - use Core\Driver\SQL\Query\Insert; use Core\Driver\SQL\Strategy\UpdateStrategy; use Core\Objects\Context; use Core\Objects\DatabaseEntity\Group; @@ -66,8 +65,12 @@ namespace Core\API\Settings { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to fetch site settings", true); + public static function getDescription(): string { + return "Allows users to fetch site settings"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } @@ -174,8 +177,12 @@ namespace Core\API\Settings { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to modify site settings", true); + public static function getDescription(): string { + return "Allows users to modify site settings"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } } \ No newline at end of file diff --git a/Core/API/Stats.class.php b/Core/API/Stats.class.php index 8e82376..1c2ce03 100644 --- a/Core/API/Stats.class.php +++ b/Core/API/Stats.class.php @@ -2,14 +2,9 @@ namespace Core\API; -use Core\Driver\SQL\Expression\Count; -use Core\Driver\SQL\Expression\Distinct; -use Core\Driver\SQL\Query\Insert; use Core\Objects\DatabaseEntity\Group; use Core\Objects\DatabaseEntity\Route; use Core\Objects\DatabaseEntity\User; -use DateTime; -use Core\Driver\SQL\Condition\Compare; use Core\Driver\SQL\Condition\CondBool; use Core\Objects\Context; @@ -82,7 +77,11 @@ class Stats extends Request { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN, Group::SUPPORT], "Allows users to view site statistics", true); + public static function getDescription(): string { + return "Allows users to view site statistics"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN, Group::SUPPORT]; } } \ No newline at end of file diff --git a/Core/API/Swagger.class.php b/Core/API/Swagger.class.php index ec615a5..6ba7796 100644 --- a/Core/API/Swagger.class.php +++ b/Core/API/Swagger.class.php @@ -2,9 +2,10 @@ namespace Core\API; +use Core\API\Parameter\IntegerType; +use Core\API\Parameter\RegexType; use Core\API\Parameter\StringType; use Core\Objects\Context; -use Core\Objects\DatabaseEntity\Group; class Swagger extends Request { @@ -52,6 +53,16 @@ class Swagger extends Request { return true; } + private function getBodyName(\ReflectionClass $class): string { + $bodyName = $class->getShortName() . "Body"; + $namespace = explode("\\", $class->getNamespaceName()); + if (count($namespace) > 2) { // Core\API\XYZ or Site\API\XYZ + $bodyName = $namespace[2] . $bodyName; + } + + return $bodyName; + } + private function getDocumentation(): string { $settings = $this->context->getSettings(); @@ -86,6 +97,7 @@ class Swagger extends Request { } } + $bodyName = $this->getBodyName($apiClass); $parameters = $apiObject->getDefaultParams(); if (!empty($parameters)) { $body = []; @@ -97,6 +109,17 @@ class Swagger extends Request { if ($param instanceof StringType && $param->maxLength > 0) { $body[$param->name]["maxLength"] = $param->maxLength; + } else if ($param instanceof IntegerType) { + if ($param->minValue > PHP_INT_MIN) { + $body[$param->name]["minimum"] = $param->minValue; + } + if ($param->maxValue < PHP_INT_MAX) { + $body[$param->name]["maximum"] = $param->maxValue; + } + } + + if ($param instanceof RegexType) { + $body[$param->name]["pattern"] = $param->pattern; } if ($body[$param->name]["type"] === "string" && ($format = $param->getSwaggerFormat())) { @@ -108,7 +131,6 @@ class Swagger extends Request { } } - $bodyName = $apiClass->getShortName() . "Body"; $definitions[$bodyName] = [ "description" => "Body for $endpoint", "properties" => $body @@ -122,6 +144,7 @@ class Swagger extends Request { $endPointDefinition = [ "post" => [ "tags" => [$tag ?? "Global"], + "summary" => $apiObject->getDescription(), "produces" => ["application/json"], "responses" => [ "200" => ["description" => "OK!"], @@ -143,7 +166,7 @@ class Swagger extends Request { "in" => "body", "name" => "body", "required" => !empty($requiredProperties), - "schema" => ["\$ref" => "#/definitions/" . $apiClass->getShortName() . "Body"] + "schema" => ["\$ref" => "#/definitions/$bodyName"] ]]; } else if ($apiObject->isMethodAllowed("GET")) { $endPointDefinition["get"] = $endPointDefinition["post"]; @@ -170,6 +193,13 @@ class Swagger extends Request { ]; return \yaml_emit($yamlData); + } + public static function getDescription(): string { + return "Returns the API-specification for this site. Endpoints, a user does not have access to, are hidden by default."; + } + + public static function getDefaultPermittedGroups(): array { + return []; } } \ No newline at end of file diff --git a/Core/API/TemplateAPI.class.php b/Core/API/TemplateAPI.class.php index 8557c38..46d1bbf 100644 --- a/Core/API/TemplateAPI.class.php +++ b/Core/API/TemplateAPI.class.php @@ -79,6 +79,12 @@ namespace Core\API\Template { return true; } - } + public static function getDescription(): string { + return "Renders a given template with a set of parameters. This API is for internal use only"; + } + public static function hasConfigurablePermissions(): bool { + return false; + } + } } \ No newline at end of file diff --git a/Core/API/TfaAPI.class.php b/Core/API/TfaAPI.class.php index d9cf156..31c8b88 100644 --- a/Core/API/TfaAPI.class.php +++ b/Core/API/TfaAPI.class.php @@ -57,7 +57,6 @@ namespace Core\API\TFA { use Core\API\Parameter\StringType; use Core\API\TfaAPI; - use Core\Driver\SQL\Query\Insert; use Core\Objects\Context; use Core\Objects\TwoFactor\AttestationObject; use Core\Objects\TwoFactor\AuthenticationData; @@ -128,8 +127,8 @@ namespace Core\API\TFA { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [], "Allows users to remove their 2FA-Tokens", true); + public static function getDescription(): string { + return "Allows users to remove their 2FA-Tokens"; } } @@ -168,8 +167,8 @@ namespace Core\API\TFA { die($twoFactorToken->generateQRCode($this->context)); } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [], "Allows users generate a QR-code to add a time-based 2FA-Token", true); + public static function getDescription(): string { + return "Allows users generate a QR-code to add a time-based 2FA-Token"; } } @@ -202,8 +201,8 @@ namespace Core\API\TFA { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [], "Allows users to confirm their time-based 2FA-Token", true); + public static function getDescription(): string { + return "Allows users to confirm their time-based 2FA-Token"; } } @@ -236,8 +235,8 @@ namespace Core\API\TFA { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [], "Allows users to verify time-based 2FA-Tokens", true); + public static function getDescription(): string { + return "Allows users to verify time-based 2FA-Tokens"; } } @@ -333,8 +332,8 @@ namespace Core\API\TFA { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [], "Allows users to register a 2FA hardware-key", true); + public static function getDescription(): string { + return "Allows users to register a 2FA hardware-key"; } } @@ -395,8 +394,8 @@ namespace Core\API\TFA { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [], "Allows users to verify a 2FA hardware-key", true); + public static function getDescription(): string { + return "Allows users to verify a 2FA hardware-key"; } } } \ No newline at end of file diff --git a/Core/API/Traits/Pagination.trait.php b/Core/API/Traits/Pagination.trait.php index 4aa7e48..961445a 100644 --- a/Core/API/Traits/Pagination.trait.php +++ b/Core/API/Traits/Pagination.trait.php @@ -2,7 +2,7 @@ namespace Core\API\Traits; -use Core\API\Parameter\Parameter; +use Core\API\Parameter\IntegerType; use Core\API\Parameter\StringType; use Core\Driver\SQL\Condition\Condition; use Core\Driver\SQL\SQL; @@ -11,26 +11,24 @@ use Core\Objects\DatabaseEntity\Controller\DatabaseEntityQuery; trait Pagination { - function getPaginationParameters(array $orderColumns, string $defaultOrderBy = null, string $defaultSortOrder = "asc"): array { + function getPaginationParameters(array $orderColumns, string $defaultOrderBy = null, + string $defaultSortOrder = "asc", int $maxPageSize = 100): array { $this->paginationOrderColumns = $orderColumns; $defaultOrderBy = $defaultOrderBy ?? current($orderColumns); return [ - 'page' => new Parameter('page', Parameter::TYPE_INT, true, 1), - 'count' => new Parameter('count', Parameter::TYPE_INT, true, 25), + 'page' => new IntegerType('page', 1,PHP_INT_MAX, true, 1), + 'count' => new IntegerType('count', 1, $maxPageSize, true, 25), 'orderBy' => new StringType('orderBy', -1, true, $defaultOrderBy, array_values($orderColumns)), 'sortOrder' => new StringType('sortOrder', -1, true, $defaultSortOrder, ['asc', 'desc']), ]; } - function initPagination(SQL $sql, string $class, ?Condition $condition = null, int $maxPageSize = 100, ?array $joins = null): bool { + function initPagination(SQL $sql, string $class, ?Condition $condition = null, ?array $joins = null): bool { $this->paginationClass = $class; $this->paginationCondition = $condition; - if (!$this->validateParameters($maxPageSize)) { - return false; - } - $this->entityCount = call_user_func("$this->paginationClass::count", $sql, $condition, $joins); + $this->pageSize = $this->getParam("count"); if ($this->entityCount === false) { return $this->createError("Error fetching $this->paginationClass::count: " . $sql->getLastError()); } @@ -48,20 +46,6 @@ trait Pagination { return true; } - function validateParameters(int $maxCount = 100): bool { - $this->page = $this->getParam("page"); - if ($this->page < 1) { - return $this->createError("Invalid page count"); - } - - $this->pageSize = $this->getParam("count"); - if ($this->pageSize < 1 || $this->pageSize > $maxCount) { - return $this->createError("Invalid fetch count"); - } - - return true; - } - function createPaginationQuery(SQL $sql, ?array $additionalValues = null, ?array $joins = null): DatabaseEntityQuery { $page = $this->getParam("page"); $count = $this->getParam("count"); diff --git a/Core/API/UserAPI.class.php b/Core/API/UserAPI.class.php index 29a95d0..f33553d 100644 --- a/Core/API/UserAPI.class.php +++ b/Core/API/UserAPI.class.php @@ -131,7 +131,6 @@ namespace Core\API\User { use Core\Driver\SQL\Condition\CondLike; use Core\Driver\SQL\Condition\CondOr; use Core\Driver\SQL\Expression\Alias; - use Core\Driver\SQL\Query\Insert; use Core\Objects\DatabaseEntity\Group; use Core\Objects\DatabaseEntity\UserToken; use Core\Driver\SQL\Column\Column; @@ -141,7 +140,6 @@ namespace Core\API\User { use Core\Objects\TwoFactor\KeyBasedTwoFactorToken; use ImagickException; use Core\Objects\Context; - use Core\Objects\DatabaseEntity\GpgKey; use Core\Objects\DatabaseEntity\User; class Create extends UserAPI { @@ -208,8 +206,12 @@ namespace Core\API\User { return $this->user; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to create new users", true); + public static function getDescription(): string { + return "Allows users to create new users"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } @@ -270,8 +272,12 @@ namespace Core\API\User { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN, Group::SUPPORT], "Allows users to fetch all users", true); + public static function getDescription(): string { + return "Allows users to fetch all users"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN, Group::SUPPORT]; } } @@ -313,8 +319,12 @@ namespace Core\API\User { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN, Group::SUPPORT], "Allows users to get details about a user", true); + public static function getDescription(): string { + return "Allows users to get details about a user"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN, Group::SUPPORT]; } } @@ -346,8 +356,12 @@ namespace Core\API\User { return true; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN, Group::SUPPORT], "Allows users to search other users", true); + public static function getDescription(): string { + return "Allows users to search other users"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN, Group::SUPPORT]; } } @@ -399,6 +413,14 @@ namespace Core\API\User { return $this->success; } + + public static function getDescription(): string { + return "Retrieves information about the current session"; + } + + public static function hasConfigurablePermissions(): bool { + return false; + } } class Invite extends UserAPI { @@ -476,8 +498,12 @@ namespace Core\API\User { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN, Group::SUPPORT], "Allows users to invite new users", true); + public static function getDescription(): string { + return "Allows users to invite new users"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN, Group::SUPPORT, Group::MODERATOR]; } } @@ -524,6 +550,10 @@ namespace Core\API\User { } } } + + public static function getDescription(): string { + return "Allows users to accept invitations and register an account"; + } } class ConfirmEmail extends UserAPI { @@ -563,6 +593,10 @@ namespace Core\API\User { } } } + + public static function getDescription(): string { + return "Allows users to confirm their email"; + } } class Login extends UserAPI { @@ -642,6 +676,14 @@ namespace Core\API\User { return $this->success; } + + public static function getDescription(): string { + return "Creates a new session identified by the session cookie"; + } + + public static function hasConfigurablePermissions(): bool { + return false; + } } class Logout extends UserAPI { @@ -664,6 +706,14 @@ namespace Core\API\User { $this->lastError = $this->context->getSQL()->getLastError(); return $this->success; } + + public static function getDescription(): string { + return "Destroys the current session and logs the user out"; + } + + public static function hasConfigurablePermissions(): bool { + return false; + } } class Register extends UserAPI { @@ -776,6 +826,10 @@ namespace Core\API\User { $this->logger->info("Registered new user with id=" . $user->getId()); return $this->success; } + + public static function getDescription(): string { + return "Allows users to register a new account"; + } } class Edit extends UserAPI { @@ -892,8 +946,12 @@ namespace Core\API\User { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to modify other user's details", true); + public static function getDescription(): string { + return "Allows users to modify other user's details"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } @@ -929,8 +987,12 @@ namespace Core\API\User { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to delete other users", true); + public static function getDescription(): string { + return "Allows users to delete other users"; + } + + public static function getDefaultPermittedGroups(): array { + return [Group::ADMIN]; } } @@ -1021,6 +1083,10 @@ namespace Core\API\User { return $this->success; } + + public static function getDescription(): string { + return "Allows users to request a password reset link"; + } } class ResendConfirmEmail extends UserAPI { @@ -1115,6 +1181,10 @@ namespace Core\API\User { return $this->success; } + + public static function getDescription(): string { + return "Allows users to request a new e-mail confirmation link"; + } } class ResetPassword extends UserAPI { @@ -1127,6 +1197,7 @@ namespace Core\API\User { )); $this->csrfTokenRequired = false; + $this->apiKeyAllowed = false; } public function _execute(): bool { @@ -1161,6 +1232,10 @@ namespace Core\API\User { } } } + + public static function getDescription(): string { + return "Allows users to reset their password with a token received by a password reset email"; + } } class UpdateProfile extends UserAPI { @@ -1175,6 +1250,7 @@ namespace Core\API\User { )); $this->loginRequired = true; $this->csrfTokenRequired = true; + $this->apiKeyAllowed = false; // prevent account takeover when an API-key is stolen $this->forbidMethod("GET"); } @@ -1231,8 +1307,8 @@ namespace Core\API\User { return $this->success; } - public static function getDefaultACL(Insert $insert): void { - $insert->addRow(self::getEndpoint(), [], "Allows users to update their profiles.", true); + public static function getDescription(): string { + return "Allows users to update their profiles."; } } @@ -1343,6 +1419,10 @@ namespace Core\API\User { return $this->success; } + + public static function getDescription(): string { + return "Allows users to upload and change their profile pictures."; + } } class RemovePicture extends UserAPI { @@ -1373,6 +1453,10 @@ namespace Core\API\User { return $this->success; } + + public static function getDescription(): string { + return "Allows users to remove their profile pictures."; + } } class CheckToken extends UserAPI { @@ -1402,5 +1486,9 @@ namespace Core\API\User { $this->result["token"] = $userToken->jsonSerialize(); return $this->success; } + + public static function getDescription(): string { + return "Allows users to validate a token received in an e-mail for various purposes"; + } } } \ No newline at end of file diff --git a/Core/API/VerifyCaptcha.class.php b/Core/API/VerifyCaptcha.class.php index 6db6bb2..705f370 100644 --- a/Core/API/VerifyCaptcha.class.php +++ b/Core/API/VerifyCaptcha.class.php @@ -59,4 +59,8 @@ class VerifyCaptcha extends Request { return $this->success; } + + public static function getDescription(): string { + return "Verifies a captcha response. This API is for internal use only."; + } } \ No newline at end of file diff --git a/Core/Configuration/CreateDatabase.class.php b/Core/Configuration/CreateDatabase.class.php index 910f696..fbf04bc 100644 --- a/Core/Configuration/CreateDatabase.class.php +++ b/Core/Configuration/CreateDatabase.class.php @@ -5,10 +5,6 @@ namespace Core\Configuration; use Core\API\Request; use Core\Driver\SQL\SQL; use Core\Objects\DatabaseEntity\Controller\DatabaseEntity; -use Core\Objects\DatabaseEntity\Group; -use Core\Objects\DatabaseEntity\Route; -use Core\Objects\Router\DocumentRoute; -use Core\Objects\Router\StaticFileRoute; use PHPUnit\Util\Exception; class CreateDatabase extends DatabaseScript { @@ -20,7 +16,7 @@ class CreateDatabase extends DatabaseScript { $queries[] = $sql->createTable("Settings") ->addString("name", 32) - ->addString("value", 1024, true) + ->addJson("value", true) ->addBool("private", false) // these values are not returned from '/api/settings/get', but can be changed ->addBool("readonly", false) // these values are neither returned, nor can be changed from outside ->primaryKey("name"); @@ -115,13 +111,20 @@ class CreateDatabase extends DatabaseScript { } } - public static function loadDefaultACL(array &$queries, SQL $sql) { + public static function loadDefaultACL(array &$queries, SQL $sql): void { $query = $sql->insert("ApiPermission", ["method", "groups", "description", "is_core"]); foreach (Request::getApiEndpoints() as $reflectionClass) { - $method = $reflectionClass->getName() . "::getDefaultACL"; - $method($query); + $className = $reflectionClass->getName(); + if (("$className::hasConfigurablePermissions")()) { + $method = ("$className::getEndpoint")(); + $groups = ("$className::getDefaultPermittedGroups")(); + $description = ("$className::getDescription")(); + $isCore = startsWith($className, "Core\\API\\"); + $query->addRow($method, $groups, $description, $isCore); + } } + if ($query->hasRows()) { $queries[] = $query; } diff --git a/react/admin-panel/src/views/access-control-list.js b/react/admin-panel/src/views/access-control-list.js index 9826c1c..7d15ee4 100644 --- a/react/admin-panel/src/views/access-control-list.js +++ b/react/admin-panel/src/views/access-control-list.js @@ -136,7 +136,7 @@ export default function AccessControlList(props) { }, [acl]); const isRestricted = (method) => { - return ["permissions/update", "permissions/delete"].includes(method.toLowerCase()) && + return ["permission/update", "permission/delete"].includes(method.toLowerCase()) || !props.api.hasGroup(USER_GROUP_ADMIN); } diff --git a/react/admin-panel/src/views/settings/settings.js b/react/admin-panel/src/views/settings/settings.js index 9cad784..94e4ee8 100644 --- a/react/admin-panel/src/views/settings/settings.js +++ b/react/admin-panel/src/views/settings/settings.js @@ -38,6 +38,8 @@ import SettingsSelection from "./input-selection"; export default function SettingsView(props) { + // TODO: website-logo (?), mail_contact, mail_contact_gpg_key_id + // meta const api = props.api; const showDialog = props.showDialog; @@ -205,7 +207,7 @@ export default function SettingsView(props) { key_name: key_name, value: settings[key_name], disabled: disabled, - onChangeValue: v => setSettings({...settings, [key_name]: v}), + onChangeValue: v => { setChanged(true); setSettings({...settings, [key_name]: v}) }, ...props }; }