ACL rewrite

This commit is contained in:
Roman 2024-04-23 12:14:28 +02:00
parent d6c6572989
commit aea20b7a10
23 changed files with 435 additions and 180 deletions

@ -33,7 +33,6 @@ namespace Core\API\ApiKey {
use Core\API\Traits\Pagination; use Core\API\Traits\Pagination;
use Core\Driver\SQL\Condition\Compare; use Core\Driver\SQL\Condition\Compare;
use Core\Driver\SQL\Condition\CondAnd; use Core\Driver\SQL\Condition\CondAnd;
use Core\Driver\SQL\Query\Insert;
use Core\Objects\Context; use Core\Objects\Context;
use Core\Objects\DatabaseEntity\ApiKey; use Core\Objects\DatabaseEntity\ApiKey;
@ -61,8 +60,8 @@ namespace Core\API\ApiKey {
return $this->success; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [], "Allows users to create new API-Keys", true); return "Allows users to create new API-Keys";
} }
} }
@ -106,8 +105,8 @@ namespace Core\API\ApiKey {
return $this->success; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [], "Allows users to fetch new API-Keys", true); return "Allows users to fetch their API-Keys";
} }
} }
@ -134,8 +133,8 @@ namespace Core\API\ApiKey {
return $this->success; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [], "Allows users to refresh API-Keys", true); return "Allows users to refresh their API-Keys";
} }
} }
@ -158,8 +157,8 @@ namespace Core\API\ApiKey {
return $this->success; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [], "Allows users to revoke API-Keys", true); return "Allows users to revoke their API-Keys";
} }
} }
} }

@ -2,8 +2,14 @@
namespace Core\API { namespace Core\API {
use Core\Objects\Context;
abstract class DatabaseAPI extends Request { 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\DatabaseAPI;
use Core\API\Parameter\RegexType; use Core\API\Parameter\RegexType;
use Core\API\Parameter\StringType;
use Core\Driver\SQL\Query\Insert;
use Core\Objects\Context; use Core\Objects\Context;
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity; use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
use Core\Objects\DatabaseEntity\Group; use Core\Objects\DatabaseEntity\Group;
@ -27,14 +31,16 @@ namespace Core\API\Database {
protected function _execute(): bool { protected function _execute(): bool {
$sql = $this->context->getSQL(); $sql = $this->context->getSQL();
$status = $sql->getStatus(); $status = $sql->getStatus();
$this->result["status"] = $status; $this->result["status"] = $status;
return true; return true;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to view the database status", true); 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; return true;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to migrate the database structure", true); return "Allows users to migrate the database structure";
}
public static function getDefaultPermittedGroups(): array {
return [Group::ADMIN];
} }
} }
} }

@ -21,7 +21,6 @@ namespace Core\API\GpgKey {
use Core\API\Parameter\StringType; use Core\API\Parameter\StringType;
use Core\API\Template\Render; use Core\API\Template\Render;
use Core\Driver\SQL\Condition\Compare; use Core\Driver\SQL\Condition\Compare;
use Core\Driver\SQL\Query\Insert;
use Core\Objects\Context; use Core\Objects\Context;
use Core\Objects\DatabaseEntity\GpgKey; use Core\Objects\DatabaseEntity\GpgKey;
use Core\Objects\DatabaseEntity\User; use Core\Objects\DatabaseEntity\User;
@ -137,8 +136,8 @@ namespace Core\API\GpgKey {
return $this->success; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [], "Allows users to import gpg keys for a secure e-mail communication", true); return "Allows users to import gpg keys for a secure e-mail communication";
} }
} }
@ -170,8 +169,8 @@ namespace Core\API\GpgKey {
return $this->success; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [], "Allows users to unlink gpg keys from their profile", true); return "Allows users to unlink gpg keys from their profile";
} }
} }
@ -219,6 +218,10 @@ namespace Core\API\GpgKey {
return $this->success; return $this->success;
} }
public static function getDescription(): string {
return "Allows users to confirm their imported gpg key";
}
} }
class Download extends GpgKeyAPI { class Download extends GpgKeyAPI {
@ -289,7 +292,9 @@ namespace Core\API\GpgKey {
header("Content-Disposition: attachment; filename=\"$fileName\""); header("Content-Disposition: attachment; filename=\"$fileName\"");
die($key); die($key);
} }
}
public static function getDescription(): string {
return "Allows users to download any gpg public key";
}
}
} }

@ -56,18 +56,14 @@ namespace Core\API\Groups {
use Core\API\GroupsAPI; use Core\API\GroupsAPI;
use Core\API\Parameter\Parameter; use Core\API\Parameter\Parameter;
use Core\API\Parameter\RegexType; use Core\API\Parameter\RegexType;
use Core\API\Parameter\StringType;
use Core\API\Traits\Pagination; use Core\API\Traits\Pagination;
use Core\Driver\SQL\Column\Column; use Core\Driver\SQL\Column\Column;
use Core\Driver\SQL\Condition\Compare; use Core\Driver\SQL\Condition\Compare;
use Core\Driver\SQL\Condition\CondAnd;
use Core\Driver\SQL\Expression\Alias; use Core\Driver\SQL\Expression\Alias;
use Core\Driver\SQL\Expression\Count; use Core\Driver\SQL\Expression\Count;
use Core\Driver\SQL\Join\InnerJoin; use Core\Driver\SQL\Join\InnerJoin;
use Core\Driver\SQL\Query\Insert;
use Core\Objects\Context; use Core\Objects\Context;
use Core\Objects\DatabaseEntity\Group; use Core\Objects\DatabaseEntity\Group;
use Core\Objects\DatabaseEntity\Route;
use Core\Objects\DatabaseEntity\User; use Core\Objects\DatabaseEntity\User;
class Fetch extends GroupsAPI { class Fetch extends GroupsAPI {
@ -113,8 +109,12 @@ namespace Core\API\Groups {
return $this->success; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN, Group::SUPPORT], "Allows users to fetch available groups", true); 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; return true;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN, Group::SUPPORT], "Allows users to get details about a group", true); 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(); $nmTable = User::getHandler($sql)->getNMRelation("groups")->getTableName();
$condition = new Compare("group_id", $this->getParam("id")); $condition = new Compare("group_id", $this->getParam("id"));
$nmJoin = new InnerJoin($nmTable, "$nmTable.user_id", "User.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; return false;
} }
@ -174,8 +178,12 @@ namespace Core\API\Groups {
return true; return true;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN, Group::SUPPORT], "Allows users to fetch members of a group", true); 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; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to create a new group", true); 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; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to update existing groups", true); 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; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to delete a group", true); 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 { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to add members to a group", true); 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 { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to remove members from a group", true); return "Allows users to remove members from a group";
}
public static function getDefaultPermittedGroups(): array {
return [Group::ADMIN];
} }
} }
} }

@ -23,4 +23,8 @@ class Info extends Request {
return true; return true;
} }
public static function getDescription(): string {
return "Returns general information about the Site";
}
} }

@ -48,6 +48,10 @@ namespace Core\API\Language {
return $this->success; return $this->success;
} }
public static function getDescription(): string {
return "Allows users to retrieve a list of built-in languages";
}
} }
class Set extends LanguageAPI { class Set extends LanguageAPI {
@ -109,6 +113,10 @@ namespace Core\API\Language {
$this->result["language"] = $this->language->jsonSerialize(); $this->result["language"] = $this->language->jsonSerialize();
return $this->success; return $this->success;
} }
public static function getDescription(): string {
return "Allows users to set their preferred language";
}
} }
class GetEntries extends LanguageAPI { class GetEntries extends LanguageAPI {
@ -172,5 +180,9 @@ namespace Core\API\Language {
} }
return true; return true;
} }
public static function getDescription(): string {
return "Returns a set of translations for the given language and module";
}
} }
} }

@ -24,7 +24,6 @@ namespace Core\API\Logs {
use Core\Driver\SQL\Condition\CondIn; use Core\Driver\SQL\Condition\CondIn;
use Core\Driver\SQL\Condition\CondLike; use Core\Driver\SQL\Condition\CondLike;
use Core\Driver\SQL\Condition\CondOr; use Core\Driver\SQL\Condition\CondOr;
use Core\Driver\SQL\Query\Insert;
use Core\Objects\Context; use Core\Objects\Context;
use Core\Objects\DatabaseEntity\Group; use Core\Objects\DatabaseEntity\Group;
use Core\Objects\DatabaseEntity\SystemLog; use Core\Objects\DatabaseEntity\SystemLog;
@ -148,8 +147,12 @@ namespace Core\API\Logs {
return true; return true;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to fetch system logs", true); return "Allows users to fetch system logs";
}
public static function getDefaultPermittedGroups(): array {
return [Group::ADMIN];
} }
} }
} }

@ -46,7 +46,6 @@ namespace Core\API\Mail {
use Core\API\MailAPI; use Core\API\MailAPI;
use Core\API\Parameter\Parameter; use Core\API\Parameter\Parameter;
use Core\API\Parameter\StringType; use Core\API\Parameter\StringType;
use Core\Driver\SQL\Query\Insert;
use Core\Objects\DatabaseEntity\Group; use Core\Objects\DatabaseEntity\Group;
use Core\Objects\DatabaseEntity\MailQueueItem; use Core\Objects\DatabaseEntity\MailQueueItem;
use DateTimeInterface; use DateTimeInterface;
@ -83,8 +82,12 @@ namespace Core\API\Mail {
return $this->success; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to send a test email to verify configuration", true); 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; 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 { class SendQueue extends MailAPI {
@ -282,5 +293,13 @@ namespace Core\API\Mail {
return $this->success; 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;
}
} }
} }

@ -23,6 +23,7 @@ namespace Core\API {
protected function isRestricted(string $method): bool { protected function isRestricted(string $method): bool {
return in_array(strtolower($method), ["permission/update", "permission/delete"]); 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\Column\Column;
use Core\Driver\SQL\Condition\CondIn; use Core\Driver\SQL\Condition\CondIn;
use Core\Driver\SQL\Condition\CondLike; use Core\Driver\SQL\Condition\CondLike;
use Core\Driver\SQL\Query\Insert;
use Core\Driver\SQL\Strategy\UpdateStrategy; use Core\Driver\SQL\Strategy\UpdateStrategy;
use Core\Objects\Context; use Core\Objects\Context;
use Core\Objects\DatabaseEntity\Group; use Core\Objects\DatabaseEntity\Group;
@ -93,6 +93,14 @@ namespace Core\API\Permission {
return $this->success; 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 { class Fetch extends PermissionAPI {
@ -146,8 +154,12 @@ namespace Core\API\Permission {
return $this->success; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to fetch API permissions", true); 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; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow( return "Allows users to modify API permissions. This is restricted to the administrator and cannot be changed";
self::getEndpoint(), [Group::ADMIN], }
"Allows users to modify API permissions. This is restricted to the administrator and cannot be changed",
true 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; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow( return "Allows users to delete API permissions. This is restricted to the administrator and cannot be changed";
self::getEndpoint(), [Group::ADMIN], }
"Allows users to delete API permissions. This is restricted to the administrator and cannot be changed",
true public static function getDefaultPermittedGroups(): array {
); return [Group::ADMIN];
}
public static function hasConfigurablePermissions(): bool {
return false;
} }
} }
} }

@ -3,7 +3,6 @@
namespace Core\API; namespace Core\API;
use Core\Driver\Logger\Logger; use Core\Driver\Logger\Logger;
use Core\Driver\SQL\Query\Insert;
use Core\Objects\Context; use Core\Objects\Context;
use Core\Objects\DatabaseEntity\TwoFactorToken; use Core\Objects\DatabaseEntity\TwoFactorToken;
use Core\Objects\TwoFactor\KeyBasedTwoFactorToken; use Core\Objects\TwoFactor\KeyBasedTwoFactorToken;
@ -131,8 +130,14 @@ abstract class Request {
protected abstract function _execute(): bool; protected abstract function _execute(): bool;
// TODO: replace this function with two abstract methods: getDefaultPermittedGroups and getDescription public static abstract function getDescription(): string;
public static function getDefaultACL(Insert $insert): void { } public static function getDefaultPermittedGroups(): array {
return [];
}
public static function hasConfigurablePermissions(): bool {
return true;
}
protected function check2FA(?TwoFactorToken $tfaToken = null): bool { protected function check2FA(?TwoFactorToken $tfaToken = null): bool {

@ -70,7 +70,6 @@ namespace Core\API\Routes {
use Core\API\Parameter\Parameter; use Core\API\Parameter\Parameter;
use Core\API\Parameter\StringType; use Core\API\Parameter\StringType;
use Core\API\RoutesAPI; use Core\API\RoutesAPI;
use Core\Driver\SQL\Query\Insert;
use Core\Objects\Context; use Core\Objects\Context;
use Core\Objects\DatabaseEntity\Group; use Core\Objects\DatabaseEntity\Group;
use Core\Objects\DatabaseEntity\Route; use Core\Objects\DatabaseEntity\Route;
@ -98,8 +97,12 @@ namespace Core\API\Routes {
return $this->success; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to fetch site routing", true); 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; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow( return "Allows users to fetch a single route";
self::getEndpoint(), }
[Group::ADMIN, Group::MODERATOR],
"Allows users to fetch a single route", public static function getDefaultPermittedGroups(): array {
true return [Group::ADMIN, Group::MODERATOR];
);
} }
} }
@ -177,8 +179,12 @@ namespace Core\API\Routes {
return $this->success && $this->regenerateCache(); return $this->success && $this->regenerateCache();
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to add new routes", true); 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(); return $this->success && $this->regenerateCache();
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to update existing routes", true); 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(); return $this->success && $this->regenerateCache();
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to remove routes", true); 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); return $this->toggleRoute($id, true);
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to enable a route", true); 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); return $this->toggleRoute($id, false);
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to disable a route", true); 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; return $this->router;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), return "Allows users to regenerate the routing cache";
[Group::ADMIN, Group::SUPPORT], }
"Allows users to regenerate the routing cache", true
); public static function getDefaultPermittedGroups(): array {
return [Group::ADMIN, Group::SUPPORT];
} }
} }
@ -368,12 +391,12 @@ namespace Core\API\Routes {
return $this->success; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow("routes/check", return "This endpoint grants the ability to check, if a route pattern is matched with the given path for debugging purposes";
[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 getDefaultPermittedGroups(): array {
); return [Group::ADMIN, Group::MODERATOR];
} }
} }
} }

@ -7,6 +7,7 @@ use Core\Objects\Context;
use Core\Objects\Search\Searchable; use Core\Objects\Search\Searchable;
use Core\Objects\Search\SearchQuery; use Core\Objects\Search\SearchQuery;
// TODO: rework this entirely
class Search extends Request { class Search extends Request {
public function __construct(Context $context, bool $externalCall = false) { public function __construct(Context $context, bool $externalCall = false) {
@ -38,4 +39,8 @@ class Search extends Request {
return true; return true;
} }
public static function getDescription(): string {
return "Searches the site documents and returns a list of matching routes";
}
} }

@ -39,7 +39,6 @@ namespace Core\API\Settings {
use Core\Driver\SQL\Column\Column; use Core\Driver\SQL\Column\Column;
use Core\Driver\SQL\Condition\CondBool; use Core\Driver\SQL\Condition\CondBool;
use Core\Driver\SQL\Condition\CondIn; use Core\Driver\SQL\Condition\CondIn;
use Core\Driver\SQL\Query\Insert;
use Core\Driver\SQL\Strategy\UpdateStrategy; use Core\Driver\SQL\Strategy\UpdateStrategy;
use Core\Objects\Context; use Core\Objects\Context;
use Core\Objects\DatabaseEntity\Group; use Core\Objects\DatabaseEntity\Group;
@ -66,8 +65,12 @@ namespace Core\API\Settings {
return $this->success; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to fetch site settings", true); 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; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to modify site settings", true); return "Allows users to modify site settings";
}
public static function getDefaultPermittedGroups(): array {
return [Group::ADMIN];
} }
} }
} }

@ -2,14 +2,9 @@
namespace Core\API; 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\Group;
use Core\Objects\DatabaseEntity\Route; use Core\Objects\DatabaseEntity\Route;
use Core\Objects\DatabaseEntity\User; use Core\Objects\DatabaseEntity\User;
use DateTime;
use Core\Driver\SQL\Condition\Compare;
use Core\Driver\SQL\Condition\CondBool; use Core\Driver\SQL\Condition\CondBool;
use Core\Objects\Context; use Core\Objects\Context;
@ -82,7 +77,11 @@ class Stats extends Request {
return $this->success; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN, Group::SUPPORT], "Allows users to view site statistics", true); return "Allows users to view site statistics";
}
public static function getDefaultPermittedGroups(): array {
return [Group::ADMIN, Group::SUPPORT];
} }
} }

@ -2,9 +2,10 @@
namespace Core\API; namespace Core\API;
use Core\API\Parameter\IntegerType;
use Core\API\Parameter\RegexType;
use Core\API\Parameter\StringType; use Core\API\Parameter\StringType;
use Core\Objects\Context; use Core\Objects\Context;
use Core\Objects\DatabaseEntity\Group;
class Swagger extends Request { class Swagger extends Request {
@ -52,6 +53,16 @@ class Swagger extends Request {
return true; 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 { private function getDocumentation(): string {
$settings = $this->context->getSettings(); $settings = $this->context->getSettings();
@ -86,6 +97,7 @@ class Swagger extends Request {
} }
} }
$bodyName = $this->getBodyName($apiClass);
$parameters = $apiObject->getDefaultParams(); $parameters = $apiObject->getDefaultParams();
if (!empty($parameters)) { if (!empty($parameters)) {
$body = []; $body = [];
@ -97,6 +109,17 @@ class Swagger extends Request {
if ($param instanceof StringType && $param->maxLength > 0) { if ($param instanceof StringType && $param->maxLength > 0) {
$body[$param->name]["maxLength"] = $param->maxLength; $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())) { if ($body[$param->name]["type"] === "string" && ($format = $param->getSwaggerFormat())) {
@ -108,7 +131,6 @@ class Swagger extends Request {
} }
} }
$bodyName = $apiClass->getShortName() . "Body";
$definitions[$bodyName] = [ $definitions[$bodyName] = [
"description" => "Body for $endpoint", "description" => "Body for $endpoint",
"properties" => $body "properties" => $body
@ -122,6 +144,7 @@ class Swagger extends Request {
$endPointDefinition = [ $endPointDefinition = [
"post" => [ "post" => [
"tags" => [$tag ?? "Global"], "tags" => [$tag ?? "Global"],
"summary" => $apiObject->getDescription(),
"produces" => ["application/json"], "produces" => ["application/json"],
"responses" => [ "responses" => [
"200" => ["description" => "OK!"], "200" => ["description" => "OK!"],
@ -143,7 +166,7 @@ class Swagger extends Request {
"in" => "body", "in" => "body",
"name" => "body", "name" => "body",
"required" => !empty($requiredProperties), "required" => !empty($requiredProperties),
"schema" => ["\$ref" => "#/definitions/" . $apiClass->getShortName() . "Body"] "schema" => ["\$ref" => "#/definitions/$bodyName"]
]]; ]];
} else if ($apiObject->isMethodAllowed("GET")) { } else if ($apiObject->isMethodAllowed("GET")) {
$endPointDefinition["get"] = $endPointDefinition["post"]; $endPointDefinition["get"] = $endPointDefinition["post"];
@ -170,6 +193,13 @@ class Swagger extends Request {
]; ];
return \yaml_emit($yamlData); 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 [];
} }
} }

@ -79,6 +79,12 @@ namespace Core\API\Template {
return true; 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;
}
}
} }

@ -57,7 +57,6 @@ namespace Core\API\TFA {
use Core\API\Parameter\StringType; use Core\API\Parameter\StringType;
use Core\API\TfaAPI; use Core\API\TfaAPI;
use Core\Driver\SQL\Query\Insert;
use Core\Objects\Context; use Core\Objects\Context;
use Core\Objects\TwoFactor\AttestationObject; use Core\Objects\TwoFactor\AttestationObject;
use Core\Objects\TwoFactor\AuthenticationData; use Core\Objects\TwoFactor\AuthenticationData;
@ -128,8 +127,8 @@ namespace Core\API\TFA {
return $this->success; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [], "Allows users to remove their 2FA-Tokens", true); return "Allows users to remove their 2FA-Tokens";
} }
} }
@ -168,8 +167,8 @@ namespace Core\API\TFA {
die($twoFactorToken->generateQRCode($this->context)); die($twoFactorToken->generateQRCode($this->context));
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [], "Allows users generate a QR-code to add a time-based 2FA-Token", true); 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; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [], "Allows users to confirm their time-based 2FA-Token", true); return "Allows users to confirm their time-based 2FA-Token";
} }
} }
@ -236,8 +235,8 @@ namespace Core\API\TFA {
return $this->success; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [], "Allows users to verify time-based 2FA-Tokens", true); return "Allows users to verify time-based 2FA-Tokens";
} }
} }
@ -333,8 +332,8 @@ namespace Core\API\TFA {
return $this->success; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [], "Allows users to register a 2FA hardware-key", true); return "Allows users to register a 2FA hardware-key";
} }
} }
@ -395,8 +394,8 @@ namespace Core\API\TFA {
return $this->success; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [], "Allows users to verify a 2FA hardware-key", true); return "Allows users to verify a 2FA hardware-key";
} }
} }
} }

@ -2,7 +2,7 @@
namespace Core\API\Traits; namespace Core\API\Traits;
use Core\API\Parameter\Parameter; use Core\API\Parameter\IntegerType;
use Core\API\Parameter\StringType; use Core\API\Parameter\StringType;
use Core\Driver\SQL\Condition\Condition; use Core\Driver\SQL\Condition\Condition;
use Core\Driver\SQL\SQL; use Core\Driver\SQL\SQL;
@ -11,26 +11,24 @@ use Core\Objects\DatabaseEntity\Controller\DatabaseEntityQuery;
trait Pagination { 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; $this->paginationOrderColumns = $orderColumns;
$defaultOrderBy = $defaultOrderBy ?? current($orderColumns); $defaultOrderBy = $defaultOrderBy ?? current($orderColumns);
return [ return [
'page' => new Parameter('page', Parameter::TYPE_INT, true, 1), 'page' => new IntegerType('page', 1,PHP_INT_MAX, true, 1),
'count' => new Parameter('count', Parameter::TYPE_INT, true, 25), 'count' => new IntegerType('count', 1, $maxPageSize, true, 25),
'orderBy' => new StringType('orderBy', -1, true, $defaultOrderBy, array_values($orderColumns)), 'orderBy' => new StringType('orderBy', -1, true, $defaultOrderBy, array_values($orderColumns)),
'sortOrder' => new StringType('sortOrder', -1, true, $defaultSortOrder, ['asc', 'desc']), '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->paginationClass = $class;
$this->paginationCondition = $condition; $this->paginationCondition = $condition;
if (!$this->validateParameters($maxPageSize)) {
return false;
}
$this->entityCount = call_user_func("$this->paginationClass::count", $sql, $condition, $joins); $this->entityCount = call_user_func("$this->paginationClass::count", $sql, $condition, $joins);
$this->pageSize = $this->getParam("count");
if ($this->entityCount === false) { if ($this->entityCount === false) {
return $this->createError("Error fetching $this->paginationClass::count: " . $sql->getLastError()); return $this->createError("Error fetching $this->paginationClass::count: " . $sql->getLastError());
} }
@ -48,20 +46,6 @@ trait Pagination {
return true; 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 { function createPaginationQuery(SQL $sql, ?array $additionalValues = null, ?array $joins = null): DatabaseEntityQuery {
$page = $this->getParam("page"); $page = $this->getParam("page");
$count = $this->getParam("count"); $count = $this->getParam("count");

@ -131,7 +131,6 @@ namespace Core\API\User {
use Core\Driver\SQL\Condition\CondLike; use Core\Driver\SQL\Condition\CondLike;
use Core\Driver\SQL\Condition\CondOr; use Core\Driver\SQL\Condition\CondOr;
use Core\Driver\SQL\Expression\Alias; use Core\Driver\SQL\Expression\Alias;
use Core\Driver\SQL\Query\Insert;
use Core\Objects\DatabaseEntity\Group; use Core\Objects\DatabaseEntity\Group;
use Core\Objects\DatabaseEntity\UserToken; use Core\Objects\DatabaseEntity\UserToken;
use Core\Driver\SQL\Column\Column; use Core\Driver\SQL\Column\Column;
@ -141,7 +140,6 @@ namespace Core\API\User {
use Core\Objects\TwoFactor\KeyBasedTwoFactorToken; use Core\Objects\TwoFactor\KeyBasedTwoFactorToken;
use ImagickException; use ImagickException;
use Core\Objects\Context; use Core\Objects\Context;
use Core\Objects\DatabaseEntity\GpgKey;
use Core\Objects\DatabaseEntity\User; use Core\Objects\DatabaseEntity\User;
class Create extends UserAPI { class Create extends UserAPI {
@ -208,8 +206,12 @@ namespace Core\API\User {
return $this->user; return $this->user;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to create new users", true); 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; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN, Group::SUPPORT], "Allows users to fetch all users", true); 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; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN, Group::SUPPORT], "Allows users to get details about a user", true); 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; return true;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN, Group::SUPPORT], "Allows users to search other users", true); 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; 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 { class Invite extends UserAPI {
@ -476,8 +498,12 @@ namespace Core\API\User {
return $this->success; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN, Group::SUPPORT], "Allows users to invite new users", true); 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 { 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 { class Login extends UserAPI {
@ -642,6 +676,14 @@ namespace Core\API\User {
return $this->success; 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 { class Logout extends UserAPI {
@ -664,6 +706,14 @@ namespace Core\API\User {
$this->lastError = $this->context->getSQL()->getLastError(); $this->lastError = $this->context->getSQL()->getLastError();
return $this->success; 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 { class Register extends UserAPI {
@ -776,6 +826,10 @@ namespace Core\API\User {
$this->logger->info("Registered new user with id=" . $user->getId()); $this->logger->info("Registered new user with id=" . $user->getId());
return $this->success; return $this->success;
} }
public static function getDescription(): string {
return "Allows users to register a new account";
}
} }
class Edit extends UserAPI { class Edit extends UserAPI {
@ -892,8 +946,12 @@ namespace Core\API\User {
return $this->success; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to modify other user's details", true); 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; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [Group::ADMIN], "Allows users to delete other users", true); 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; return $this->success;
} }
public static function getDescription(): string {
return "Allows users to request a password reset link";
}
} }
class ResendConfirmEmail extends UserAPI { class ResendConfirmEmail extends UserAPI {
@ -1115,6 +1181,10 @@ namespace Core\API\User {
return $this->success; return $this->success;
} }
public static function getDescription(): string {
return "Allows users to request a new e-mail confirmation link";
}
} }
class ResetPassword extends UserAPI { class ResetPassword extends UserAPI {
@ -1127,6 +1197,7 @@ namespace Core\API\User {
)); ));
$this->csrfTokenRequired = false; $this->csrfTokenRequired = false;
$this->apiKeyAllowed = false;
} }
public function _execute(): bool { 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 { class UpdateProfile extends UserAPI {
@ -1175,6 +1250,7 @@ namespace Core\API\User {
)); ));
$this->loginRequired = true; $this->loginRequired = true;
$this->csrfTokenRequired = true; $this->csrfTokenRequired = true;
$this->apiKeyAllowed = false; // prevent account takeover when an API-key is stolen
$this->forbidMethod("GET"); $this->forbidMethod("GET");
} }
@ -1231,8 +1307,8 @@ namespace Core\API\User {
return $this->success; return $this->success;
} }
public static function getDefaultACL(Insert $insert): void { public static function getDescription(): string {
$insert->addRow(self::getEndpoint(), [], "Allows users to update their profiles.", true); return "Allows users to update their profiles.";
} }
} }
@ -1343,6 +1419,10 @@ namespace Core\API\User {
return $this->success; return $this->success;
} }
public static function getDescription(): string {
return "Allows users to upload and change their profile pictures.";
}
} }
class RemovePicture extends UserAPI { class RemovePicture extends UserAPI {
@ -1373,6 +1453,10 @@ namespace Core\API\User {
return $this->success; return $this->success;
} }
public static function getDescription(): string {
return "Allows users to remove their profile pictures.";
}
} }
class CheckToken extends UserAPI { class CheckToken extends UserAPI {
@ -1402,5 +1486,9 @@ namespace Core\API\User {
$this->result["token"] = $userToken->jsonSerialize(); $this->result["token"] = $userToken->jsonSerialize();
return $this->success; return $this->success;
} }
public static function getDescription(): string {
return "Allows users to validate a token received in an e-mail for various purposes";
}
} }
} }

@ -59,4 +59,8 @@ class VerifyCaptcha extends Request {
return $this->success; return $this->success;
} }
public static function getDescription(): string {
return "Verifies a captcha response. This API is for internal use only.";
}
} }

@ -5,10 +5,6 @@ namespace Core\Configuration;
use Core\API\Request; use Core\API\Request;
use Core\Driver\SQL\SQL; use Core\Driver\SQL\SQL;
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity; 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; use PHPUnit\Util\Exception;
class CreateDatabase extends DatabaseScript { class CreateDatabase extends DatabaseScript {
@ -20,7 +16,7 @@ class CreateDatabase extends DatabaseScript {
$queries[] = $sql->createTable("Settings") $queries[] = $sql->createTable("Settings")
->addString("name", 32) ->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("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 ->addBool("readonly", false) // these values are neither returned, nor can be changed from outside
->primaryKey("name"); ->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"]); $query = $sql->insert("ApiPermission", ["method", "groups", "description", "is_core"]);
foreach (Request::getApiEndpoints() as $reflectionClass) { foreach (Request::getApiEndpoints() as $reflectionClass) {
$method = $reflectionClass->getName() . "::getDefaultACL"; $className = $reflectionClass->getName();
$method($query); 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()) { if ($query->hasRows()) {
$queries[] = $query; $queries[] = $query;
} }

@ -136,7 +136,7 @@ export default function AccessControlList(props) {
}, [acl]); }, [acl]);
const isRestricted = (method) => { 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); !props.api.hasGroup(USER_GROUP_ADMIN);
} }

@ -38,6 +38,8 @@ import SettingsSelection from "./input-selection";
export default function SettingsView(props) { export default function SettingsView(props) {
// TODO: website-logo (?), mail_contact, mail_contact_gpg_key_id
// meta // meta
const api = props.api; const api = props.api;
const showDialog = props.showDialog; const showDialog = props.showDialog;
@ -205,7 +207,7 @@ export default function SettingsView(props) {
key_name: key_name, key_name: key_name,
value: settings[key_name], value: settings[key_name],
disabled: disabled, disabled: disabled,
onChangeValue: v => setSettings({...settings, [key_name]: v}), onChangeValue: v => { setChanged(true); setSettings({...settings, [key_name]: v}) },
...props ...props
}; };
} }