diff --git a/src/Server.php b/src/Server.php index 59cacb5f0..e62d8865a 100644 --- a/src/Server.php +++ b/src/Server.php @@ -682,7 +682,7 @@ class Server{ $this->operators->set(strtolower($name), true); if(($player = $this->getPlayerExact($name)) !== null){ - $player->onOpStatusChange(true); + $player->setBasePermission(DefaultPermissions::ROOT_OPERATOR, true); } $this->operators->save(); } @@ -691,7 +691,7 @@ class Server{ $this->operators->remove(strtolower($name)); if(($player = $this->getPlayerExact($name)) !== null){ - $player->onOpStatusChange(false); + $player->unsetBasePermission(DefaultPermissions::ROOT_OPERATOR); } $this->operators->save(); } diff --git a/src/command/ConsoleCommandSender.php b/src/command/ConsoleCommandSender.php index 49b25efa5..70c9fb409 100644 --- a/src/command/ConsoleCommandSender.php +++ b/src/command/ConsoleCommandSender.php @@ -76,10 +76,6 @@ class ConsoleCommandSender implements CommandSender{ return "CONSOLE"; } - public function onOpStatusChange(bool $value) : void{ - - } - public function getScreenLineHeight() : int{ return $this->lineHeight ?? PHP_INT_MAX; } diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 2bd9799e7..86d3d49a1 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -95,6 +95,7 @@ use pocketmine\network\mcpe\protocol\types\PlayerListEntry; use pocketmine\network\mcpe\protocol\types\PlayerPermissions; use pocketmine\network\mcpe\protocol\UpdateAttributesPacket; use pocketmine\network\NetworkSessionManager; +use pocketmine\permission\DefaultPermissions; use pocketmine\player\GameMode; use pocketmine\player\Player; use pocketmine\player\PlayerInfo; @@ -722,8 +723,9 @@ class NetworkSession{ //TODO: permission flags - $pk->commandPermission = ($for->isOp() ? AdventureSettingsPacket::PERMISSION_OPERATOR : AdventureSettingsPacket::PERMISSION_NORMAL); - $pk->playerPermission = ($for->isOp() ? PlayerPermissions::OPERATOR : PlayerPermissions::MEMBER); + $isOp = $for->hasPermission(DefaultPermissions::ROOT_OPERATOR); + $pk->commandPermission = ($isOp ? AdventureSettingsPacket::PERMISSION_OPERATOR : AdventureSettingsPacket::PERMISSION_NORMAL); + $pk->playerPermission = ($isOp ? PlayerPermissions::OPERATOR : PlayerPermissions::MEMBER); $pk->entityUniqueId = $for->getId(); $this->sendDataPacket($pk); diff --git a/src/permission/DefaultPermissions.php b/src/permission/DefaultPermissions.php index f6de4ec07..b9f995692 100644 --- a/src/permission/DefaultPermissions.php +++ b/src/permission/DefaultPermissions.php @@ -26,98 +26,103 @@ namespace pocketmine\permission; abstract class DefaultPermissions{ public const ROOT = "pocketmine"; - public static function registerPermission(Permission $perm, ?Permission $parent = null) : Permission{ - if($parent instanceof Permission){ - $parent->addChild($perm->getName(), true); - } - PermissionManager::getInstance()->addPermission($perm); + public const ROOT_OPERATOR = "pocketmine.group.operator"; + public const ROOT_USER = "pocketmine.group.user"; - return PermissionManager::getInstance()->getPermission($perm->getName()); + /** + * @param Permission[] $grantedBy + * @param Permission[] $deniedBy + */ + public static function registerPermission(Permission $candidate, array $grantedBy = [], array $deniedBy = []) : Permission{ + foreach($grantedBy as $permission){ + $permission->addChild($candidate->getName(), true); + } + foreach($deniedBy as $permission){ + $permission->addChild($candidate->getName(), false); + } + PermissionManager::getInstance()->addPermission($candidate); + + return PermissionManager::getInstance()->getPermission($candidate->getName()); } public static function registerCorePermissions() : void{ $parent = self::registerPermission(new Permission(self::ROOT, "Allows using all PocketMine commands and utilities")); - $broadcasts = self::registerPermission(new Permission(self::ROOT . ".broadcast", "Allows the user to receive all broadcast messages"), $parent); - self::registerPermission(new Permission(self::ROOT . ".broadcast.admin", "Allows the user to receive administrative broadcasts", Permission::DEFAULT_OP), $broadcasts); - self::registerPermission(new Permission(self::ROOT . ".broadcast.user", "Allows the user to receive user broadcasts", Permission::DEFAULT_TRUE), $broadcasts); - $broadcasts->recalculatePermissibles(); + $operatorRoot = self::registerPermission(new Permission(self::ROOT_OPERATOR, "Grants all operator permissions"), [$parent]); + $everyoneRoot = self::registerPermission(new Permission(self::ROOT_USER, "Grants all non-sensitive permissions that everyone gets by default"), [$operatorRoot]); - $commands = self::registerPermission(new Permission(self::ROOT . ".command", "Allows using all PocketMine commands"), $parent); + $broadcastRoot = self::registerPermission(new Permission(self::ROOT . ".broadcast", "Allows the user to receive all broadcast messages"), [$parent]); - $whitelist = self::registerPermission(new Permission(self::ROOT . ".command.whitelist", "Allows the user to modify the server whitelist", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.whitelist.add", "Allows the user to add a player to the server whitelist"), $whitelist); - self::registerPermission(new Permission(self::ROOT . ".command.whitelist.remove", "Allows the user to remove a player from the server whitelist"), $whitelist); - self::registerPermission(new Permission(self::ROOT . ".command.whitelist.reload", "Allows the user to reload the server whitelist"), $whitelist); - self::registerPermission(new Permission(self::ROOT . ".command.whitelist.enable", "Allows the user to enable the server whitelist"), $whitelist); - self::registerPermission(new Permission(self::ROOT . ".command.whitelist.disable", "Allows the user to disable the server whitelist"), $whitelist); - self::registerPermission(new Permission(self::ROOT . ".command.whitelist.list", "Allows the user to list all players on the server whitelist"), $whitelist); - $whitelist->recalculatePermissibles(); + self::registerPermission(new Permission(self::ROOT . ".broadcast.admin", "Allows the user to receive administrative broadcasts"), [$operatorRoot, $broadcastRoot]); + self::registerPermission(new Permission(self::ROOT . ".broadcast.user", "Allows the user to receive user broadcasts"), [$everyoneRoot, $broadcastRoot]); - $ban = self::registerPermission(new Permission(self::ROOT . ".command.ban", "Allows the user to ban people", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.ban.player", "Allows the user to ban players"), $ban); - self::registerPermission(new Permission(self::ROOT . ".command.ban.ip", "Allows the user to ban IP addresses"), $ban); - self::registerPermission(new Permission(self::ROOT . ".command.ban.list", "Allows the user to list banned players"), $ban); - $ban->recalculatePermissibles(); + //this allows using ALL commands if assigned, irrespective of what group the player is in + $commandRoot = self::registerPermission(new Permission(self::ROOT . ".command", "Allows using all PocketMine commands"), [$parent]); + $operatorCommand = [$commandRoot, $operatorRoot]; + $everyoneCommand = [$commandRoot, $everyoneRoot]; - $unban = self::registerPermission(new Permission(self::ROOT . ".command.unban", "Allows the user to unban people", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.unban.player", "Allows the user to unban players"), $unban); - self::registerPermission(new Permission(self::ROOT . ".command.unban.ip", "Allows the user to unban IP addresses"), $unban); - $unban->recalculatePermissibles(); + $whitelist = self::registerPermission(new Permission(self::ROOT . ".command.whitelist", "Allows the user to modify the server whitelist"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.whitelist.add", "Allows the user to add a player to the server whitelist"), [$whitelist]); + self::registerPermission(new Permission(self::ROOT . ".command.whitelist.remove", "Allows the user to remove a player from the server whitelist"), [$whitelist]); + self::registerPermission(new Permission(self::ROOT . ".command.whitelist.reload", "Allows the user to reload the server whitelist"), [$whitelist]); + self::registerPermission(new Permission(self::ROOT . ".command.whitelist.enable", "Allows the user to enable the server whitelist"), [$whitelist]); + self::registerPermission(new Permission(self::ROOT . ".command.whitelist.disable", "Allows the user to disable the server whitelist"), [$whitelist]); + self::registerPermission(new Permission(self::ROOT . ".command.whitelist.list", "Allows the user to list all players on the server whitelist"), [$whitelist]); - $op = self::registerPermission(new Permission(self::ROOT . ".command.op", "Allows the user to change operators", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.op.give", "Allows the user to give a player operator status"), $op); - self::registerPermission(new Permission(self::ROOT . ".command.op.take", "Allows the user to take a player's operator status"), $op); - $op->recalculatePermissibles(); + $ban = self::registerPermission(new Permission(self::ROOT . ".command.ban", "Allows the user to ban people"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.ban.player", "Allows the user to ban players"), [$ban]); + self::registerPermission(new Permission(self::ROOT . ".command.ban.ip", "Allows the user to ban IP addresses"), [$ban]); + self::registerPermission(new Permission(self::ROOT . ".command.ban.list", "Allows the user to list banned players"), [$ban]); - $save = self::registerPermission(new Permission(self::ROOT . ".command.save", "Allows the user to save the worlds", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.save.enable", "Allows the user to enable automatic saving"), $save); - self::registerPermission(new Permission(self::ROOT . ".command.save.disable", "Allows the user to disable automatic saving"), $save); - self::registerPermission(new Permission(self::ROOT . ".command.save.perform", "Allows the user to perform a manual save"), $save); - $save->recalculatePermissibles(); + $unban = self::registerPermission(new Permission(self::ROOT . ".command.unban", "Allows the user to unban people"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.unban.player", "Allows the user to unban players"), [$unban]); + self::registerPermission(new Permission(self::ROOT . ".command.unban.ip", "Allows the user to unban IP addresses"), [$unban]); - $time = self::registerPermission(new Permission(self::ROOT . ".command.time", "Allows the user to alter the time", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.time.add", "Allows the user to fast-forward time"), $time); - self::registerPermission(new Permission(self::ROOT . ".command.time.set", "Allows the user to change the time"), $time); - self::registerPermission(new Permission(self::ROOT . ".command.time.start", "Allows the user to restart the time"), $time); - self::registerPermission(new Permission(self::ROOT . ".command.time.stop", "Allows the user to stop the time"), $time); - self::registerPermission(new Permission(self::ROOT . ".command.time.query", "Allows the user query the time"), $time); - $time->recalculatePermissibles(); + $op = self::registerPermission(new Permission(self::ROOT . ".command.op", "Allows the user to change operators"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.op.give", "Allows the user to give a player operator status"), [$op]); + self::registerPermission(new Permission(self::ROOT . ".command.op.take", "Allows the user to take a player's operator status"), [$op]); - $kill = self::registerPermission(new Permission(self::ROOT . ".command.kill", "Allows the user to kill players", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.kill.self", "Allows the user to commit suicide", Permission::DEFAULT_TRUE), $kill); - self::registerPermission(new Permission(self::ROOT . ".command.kill.other", "Allows the user to kill other players"), $kill); - $kill->recalculatePermissibles(); + $save = self::registerPermission(new Permission(self::ROOT . ".command.save", "Allows the user to save the worlds"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.save.enable", "Allows the user to enable automatic saving"), [$save]); + self::registerPermission(new Permission(self::ROOT . ".command.save.disable", "Allows the user to disable automatic saving"), [$save]); + self::registerPermission(new Permission(self::ROOT . ".command.save.perform", "Allows the user to perform a manual save"), [$save]); - self::registerPermission(new Permission(self::ROOT . ".command.me", "Allows the user to perform a chat action", Permission::DEFAULT_TRUE), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.tell", "Allows the user to privately message another player", Permission::DEFAULT_TRUE), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.say", "Allows the user to talk as the console", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.give", "Allows the user to give items to players", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.effect", "Allows the user to give/take potion effects", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.enchant", "Allows the user to enchant items", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.particle", "Allows the user to create particle effects", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.teleport", "Allows the user to teleport players", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.kick", "Allows the user to kick players", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.stop", "Allows the user to stop the server", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.list", "Allows the user to list all online players", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.help", "Allows the user to view the help menu", Permission::DEFAULT_TRUE), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.plugins", "Allows the user to view the list of plugins", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.version", "Allows the user to view the version of the server", Permission::DEFAULT_TRUE), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.gamemode", "Allows the user to change the gamemode of players", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.defaultgamemode", "Allows the user to change the default gamemode", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.seed", "Allows the user to view the seed of the world", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.status", "Allows the user to view the server performance", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.gc", "Allows the user to fire garbage collection tasks", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.dumpmemory", "Allows the user to dump memory contents", Permission::DEFAULT_FALSE), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.timings", "Allows the user to records timings for all plugin events", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.spawnpoint", "Allows the user to change player's spawnpoint", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.setworldspawn", "Allows the user to change the world spawn", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.transferserver", "Allows the user to transfer self to another server", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.title", "Allows the user to send a title to the specified player", Permission::DEFAULT_OP), $commands); - self::registerPermission(new Permission(self::ROOT . ".command.difficulty", "Allows the user to change the game difficulty", Permission::DEFAULT_OP), $commands); + $time = self::registerPermission(new Permission(self::ROOT . ".command.time", "Allows the user to alter the time"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.time.add", "Allows the user to fast-forward time"), [$time]); + self::registerPermission(new Permission(self::ROOT . ".command.time.set", "Allows the user to change the time"), [$time]); + self::registerPermission(new Permission(self::ROOT . ".command.time.start", "Allows the user to restart the time"), [$time]); + self::registerPermission(new Permission(self::ROOT . ".command.time.stop", "Allows the user to stop the time"), [$time]); + self::registerPermission(new Permission(self::ROOT . ".command.time.query", "Allows the user query the time"), [$time]); - $commands->recalculatePermissibles(); + $kill = self::registerPermission(new Permission(self::ROOT . ".command.kill", "Allows the user to kill players"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.kill.self", "Allows the user to commit suicide"), [$kill, $everyoneRoot]); + self::registerPermission(new Permission(self::ROOT . ".command.kill.other", "Allows the user to kill other players"), [$kill]); - $parent->recalculatePermissibles(); + self::registerPermission(new Permission(self::ROOT . ".command.me", "Allows the user to perform a chat action"), $everyoneCommand); + self::registerPermission(new Permission(self::ROOT . ".command.tell", "Allows the user to privately message another player"), $everyoneCommand); + self::registerPermission(new Permission(self::ROOT . ".command.say", "Allows the user to talk as the console"), [$commandRoot, $operatorRoot]); + self::registerPermission(new Permission(self::ROOT . ".command.give", "Allows the user to give items to players"), [$commandRoot, $operatorRoot]); + self::registerPermission(new Permission(self::ROOT . ".command.effect", "Allows the user to give/take potion effects"), [$commandRoot, $operatorRoot]); + self::registerPermission(new Permission(self::ROOT . ".command.enchant", "Allows the user to enchant items"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.particle", "Allows the user to create particle effects"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.teleport", "Allows the user to teleport players"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.kick", "Allows the user to kick players"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.stop", "Allows the user to stop the server"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.list", "Allows the user to list all online players"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.help", "Allows the user to view the help menu"), $everyoneCommand); + self::registerPermission(new Permission(self::ROOT . ".command.plugins", "Allows the user to view the list of plugins"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.version", "Allows the user to view the version of the server"), $everyoneCommand); + self::registerPermission(new Permission(self::ROOT . ".command.gamemode", "Allows the user to change the gamemode of players"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.defaultgamemode", "Allows the user to change the default gamemode"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.seed", "Allows the user to view the seed of the world"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.status", "Allows the user to view the server performance"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.gc", "Allows the user to fire garbage collection tasks"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.dumpmemory", "Allows the user to dump memory contents"), [$commandRoot]); //TODO: this should be exclusively granted to CONSOLE + self::registerPermission(new Permission(self::ROOT . ".command.timings", "Allows the user to records timings for all plugin events"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.spawnpoint", "Allows the user to change player's spawnpoint"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.setworldspawn", "Allows the user to change the world spawn"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.transferserver", "Allows the user to transfer self to another server"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.title", "Allows the user to send a title to the specified player"), $operatorCommand); + self::registerPermission(new Permission(self::ROOT . ".command.difficulty", "Allows the user to change the game difficulty"), $operatorCommand); } } diff --git a/src/permission/Permissible.php b/src/permission/Permissible.php index 0a2759025..26ba2b1ed 100644 --- a/src/permission/Permissible.php +++ b/src/permission/Permissible.php @@ -27,9 +27,25 @@ use pocketmine\plugin\Plugin; interface Permissible{ - public function isOp() : bool; + /** + * Assigns a baseline permission to the permissible. This is **always** calculated before anything else, which means + * that permissions set using addAttachment() will always override base permissions. + * You probably don't want to use this if you're not assigning (denying) operator permissions. + * + * @internal + * @see Permissible::addAttachment() for normal permission assignments + * @param Permission|string $name + */ + public function setBasePermission($name, bool $grant) : void; - public function onOpStatusChange(bool $value) : void; + /** + * Unsets a baseline permission previously set. If it wasn't already set, this will have no effect. + * Note that this might have different results than setting the permission to false. + * + * @internal + * @param Permission|string $name + */ + public function unsetBasePermission($name) : void; /** * Checks if this instance has a permission overridden diff --git a/src/permission/PermissibleBase.php b/src/permission/PermissibleBase.php index 8a83d1aba..b7613006f 100644 --- a/src/permission/PermissibleBase.php +++ b/src/permission/PermissibleBase.php @@ -29,12 +29,17 @@ use pocketmine\timings\Timings; use function spl_object_id; class PermissibleBase implements Permissible{ - /** @var bool */ - private $op; - /** @var Permissible|null */ private $parent; + /** + * @var bool[] + * @phpstan-var array + */ + private $rootPermissions = [ + DefaultPermissions::ROOT_USER => true + ]; + /** @var PermissionAttachment[] */ private $attachments = []; @@ -43,7 +48,12 @@ class PermissibleBase implements Permissible{ public function __construct(?Permissible $permissible, bool $isOp){ $this->parent = $permissible; - $this->op = $isOp; + + //TODO: we can't setBasePermission here directly due to bad architecture that causes recalculatePermissions to explode + //so, this hack has to be done here to prevent permission recalculations until it's fixed... + if($isOp){ + $this->rootPermissions[DefaultPermissions::ROOT_OPERATOR] = true; + } //TODO: permissions need to be recalculated here, or inherited permissions won't work } @@ -51,12 +61,16 @@ class PermissibleBase implements Permissible{ return $this->parent ?? $this; } - public function isOp() : bool{ - return $this->op; + public function setBasePermission($name, bool $grant) : void{ + if($name instanceof Permission){ + $name = $name->getName(); + } + $this->rootPermissions[$name] = $grant; + $this->getRootPermissible()->recalculatePermissions(); } - public function onOpStatusChange(bool $value) : void{ - $this->op = $value; + public function unsetBasePermission($name) : void{ + unset($this->rootPermissions[$name instanceof Permission ? $name->getName() : $name]); $this->getRootPermissible()->recalculatePermissions(); } @@ -117,14 +131,16 @@ class PermissibleBase implements Permissible{ public function recalculatePermissions() : void{ Timings::$permissibleCalculationTimer->startTiming(); - $this->clearPermissions(); $permManager = PermissionManager::getInstance(); - $defaults = $permManager->getDefaultPermissions($this->isOp()); - $permManager->subscribeToDefaultPerms($this->isOp(), $this->getRootPermissible()); + $permManager->unsubscribeFromAllPermissions($this->getRootPermissible()); + $this->permissions = []; - foreach($defaults as $perm){ - $name = $perm->getName(); - $this->permissions[$name] = new PermissionAttachmentInfo($this->getRootPermissible(), $name, null, true); + foreach($this->rootPermissions as $name => $isGranted){ + $perm = $permManager->getPermission($name); + if($perm === null){ + throw new \InvalidStateException("Unregistered root permission $name"); + } + $this->permissions[$name] = new PermissionAttachmentInfo($this->getRootPermissible(), $name, null, $isGranted); $permManager->subscribeToPermission($name, $this->getRootPermissible()); $this->calculateChildPermissions($perm->getChildren(), false, null); } @@ -137,11 +153,7 @@ class PermissibleBase implements Permissible{ } public function clearPermissions() : void{ - $permManager = PermissionManager::getInstance(); - $permManager->unsubscribeFromAllPermissions($this->getRootPermissible()); - - $permManager->unsubscribeFromDefaultPerms(false, $this->getRootPermissible()); - $permManager->unsubscribeFromDefaultPerms(true, $this->getRootPermissible()); + PermissionManager::getInstance()->unsubscribeFromAllPermissions($this->getRootPermissible()); $this->permissions = []; } diff --git a/src/permission/PermissibleDelegateTrait.php b/src/permission/PermissibleDelegateTrait.php index f488a6f39..9d1b57d74 100644 --- a/src/permission/PermissibleDelegateTrait.php +++ b/src/permission/PermissibleDelegateTrait.php @@ -30,12 +30,18 @@ trait PermissibleDelegateTrait{ /** @var PermissibleBase */ private $perm; - public function isOp() : bool{ - return $this->perm->isOp(); + /** + * @param Permission|string $name + */ + public function setBasePermission($name, bool $value) : void{ + $this->perm->setBasePermission($name, $value); } - public function onOpStatusChange(bool $value) : void{ - $this->perm->onOpStatusChange($value); + /** + * @param Permission|string $name + */ + public function unsetBasePermission($name) : void{ + $this->perm->unsetBasePermission($name); } /** diff --git a/src/permission/Permission.php b/src/permission/Permission.php index 21043fded..df42876d3 100644 --- a/src/permission/Permission.php +++ b/src/permission/Permission.php @@ -31,14 +31,6 @@ namespace pocketmine\permission; * Represents a permission */ class Permission{ - public const DEFAULT_OP = "op"; - public const DEFAULT_NOT_OP = "notop"; - public const DEFAULT_TRUE = "true"; - public const DEFAULT_FALSE = "false"; - - /** @var string */ - public static $DEFAULT_PERMISSION = self::DEFAULT_OP; - /** @var string */ private $name; @@ -51,19 +43,15 @@ class Permission{ */ private $children; - /** @var string */ - private $defaultValue; - /** * Creates a new Permission object to be attached to Permissible objects * * @param bool[] $children * @phpstan-param array $children */ - public function __construct(string $name, ?string $description = null, ?string $defaultValue = null, array $children = []){ + public function __construct(string $name, ?string $description = null, array $children = []){ $this->name = $name; $this->description = $description ?? ""; - $this->defaultValue = $defaultValue ?? self::$DEFAULT_PERMISSION; $this->children = $children; $this->recalculatePermissibles(); @@ -81,17 +69,6 @@ class Permission{ return $this->children; } - public function getDefault() : string{ - return $this->defaultValue; - } - - public function setDefault(string $value) : void{ - if($value !== $this->defaultValue){ - $this->defaultValue = $value; - $this->recalculatePermissibles(); - } - } - public function getDescription() : string{ return $this->description; } @@ -110,8 +87,6 @@ class Permission{ public function recalculatePermissibles() : void{ $perms = $this->getPermissibles(); - PermissionManager::getInstance()->recalculatePermissionDefaults($this); - foreach($perms as $p){ $p->recalculatePermissions(); } diff --git a/src/permission/PermissionManager.php b/src/permission/PermissionManager.php index 678128159..a50db07e2 100644 --- a/src/permission/PermissionManager.php +++ b/src/permission/PermissionManager.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\permission; -use pocketmine\timings\Timings; use function count; use function spl_object_id; @@ -41,16 +40,8 @@ class PermissionManager{ /** @var Permission[] */ protected $permissions = []; - /** @var Permission[] */ - protected $defaultPerms = []; - /** @var Permission[] */ - protected $defaultPermsOp = []; /** @var Permissible[][] */ protected $permSubs = []; - /** @var Permissible[] */ - protected $defSubs = []; - /** @var Permissible[] */ - protected $defSubsOp = []; public function getPermission(string $name) : ?Permission{ return $this->permissions[$name] ?? null; @@ -59,7 +50,6 @@ class PermissionManager{ public function addPermission(Permission $permission) : bool{ if(!isset($this->permissions[$permission->getName()])){ $this->permissions[$permission->getName()] = $permission; - $this->calculatePermissionDefault($permission); return true; } @@ -78,45 +68,6 @@ class PermissionManager{ } } - /** - * @return Permission[] - */ - public function getDefaultPermissions(bool $op) : array{ - if($op){ - return $this->defaultPermsOp; - }else{ - return $this->defaultPerms; - } - } - - public function recalculatePermissionDefaults(Permission $permission) : void{ - if(isset($this->permissions[$permission->getName()])){ - unset($this->defaultPermsOp[$permission->getName()]); - unset($this->defaultPerms[$permission->getName()]); - $this->calculatePermissionDefault($permission); - } - } - - private function calculatePermissionDefault(Permission $permission) : void{ - Timings::$permissionDefaultTimer->startTiming(); - if($permission->getDefault() === Permission::DEFAULT_OP or $permission->getDefault() === Permission::DEFAULT_TRUE){ - $this->defaultPermsOp[$permission->getName()] = $permission; - $this->dirtyPermissibles(true); - } - - if($permission->getDefault() === Permission::DEFAULT_NOT_OP or $permission->getDefault() === Permission::DEFAULT_TRUE){ - $this->defaultPerms[$permission->getName()] = $permission; - $this->dirtyPermissibles(false); - } - Timings::$permissionDefaultTimer->stopTiming(); - } - - private function dirtyPermissibles(bool $op) : void{ - foreach($this->getDefaultPermSubscriptions($op) as $p){ - $p->recalculatePermissions(); - } - } - public function subscribeToPermission(string $permission, Permissible $permissible) : void{ if(!isset($this->permSubs[$permission])){ $this->permSubs[$permission] = []; @@ -149,33 +100,6 @@ class PermissionManager{ return $this->permSubs[$permission] ?? []; } - public function subscribeToDefaultPerms(bool $op, Permissible $permissible) : void{ - if($op){ - $this->defSubsOp[spl_object_id($permissible)] = $permissible; - }else{ - $this->defSubs[spl_object_id($permissible)] = $permissible; - } - } - - public function unsubscribeFromDefaultPerms(bool $op, Permissible $permissible) : void{ - if($op){ - unset($this->defSubsOp[spl_object_id($permissible)]); - }else{ - unset($this->defSubs[spl_object_id($permissible)]); - } - } - - /** - * @return Permissible[] - */ - public function getDefaultPermSubscriptions(bool $op) : array{ - if($op){ - return $this->defSubsOp; - } - - return $this->defSubs; - } - /** * @return Permission[] */ @@ -185,7 +109,5 @@ class PermissionManager{ public function clearPermissions() : void{ $this->permissions = []; - $this->defaultPerms = []; - $this->defaultPermsOp = []; } } diff --git a/src/permission/PermissionParser.php b/src/permission/PermissionParser.php index 80e2c2631..b88244426 100644 --- a/src/permission/PermissionParser.php +++ b/src/permission/PermissionParser.php @@ -23,31 +23,34 @@ declare(strict_types=1); namespace pocketmine\permission; -use function count; use function is_array; use function is_bool; -use function ksort; use function strtolower; class PermissionParser{ + public const DEFAULT_OP = "op"; + public const DEFAULT_NOT_OP = "notop"; + public const DEFAULT_TRUE = "true"; + public const DEFAULT_FALSE = "false"; + public const DEFAULT_STRING_MAP = [ - "op" => Permission::DEFAULT_OP, - "isop" => Permission::DEFAULT_OP, - "operator" => Permission::DEFAULT_OP, - "isoperator" => Permission::DEFAULT_OP, - "admin" => Permission::DEFAULT_OP, - "isadmin" => Permission::DEFAULT_OP, + "op" => self::DEFAULT_OP, + "isop" => self::DEFAULT_OP, + "operator" => self::DEFAULT_OP, + "isoperator" => self::DEFAULT_OP, + "admin" => self::DEFAULT_OP, + "isadmin" => self::DEFAULT_OP, - "!op" => Permission::DEFAULT_NOT_OP, - "notop" => Permission::DEFAULT_NOT_OP, - "!operator" => Permission::DEFAULT_NOT_OP, - "notoperator" => Permission::DEFAULT_NOT_OP, - "!admin" => Permission::DEFAULT_NOT_OP, - "notadmin" => Permission::DEFAULT_NOT_OP, + "!op" => self::DEFAULT_NOT_OP, + "notop" => self::DEFAULT_NOT_OP, + "!operator" => self::DEFAULT_NOT_OP, + "notoperator" => self::DEFAULT_NOT_OP, + "!admin" => self::DEFAULT_NOT_OP, + "notadmin" => self::DEFAULT_NOT_OP, - "true" => Permission::DEFAULT_TRUE, - "false" => Permission::DEFAULT_FALSE, + "true" => self::DEFAULT_TRUE, + "false" => self::DEFAULT_FALSE, ]; /** @@ -75,25 +78,27 @@ class PermissionParser{ * @param mixed[][] $data * @phpstan-param array> $data * - * @return Permission[] + * @return Permission[][] + * @phpstan-return array> */ - public static function loadPermissions(array $data, string $default = Permission::DEFAULT_OP) : array{ + public static function loadPermissions(array $data, string $default = self::DEFAULT_OP) : array{ $result = []; foreach($data as $key => $entry){ - $result[] = self::loadPermission($key, $entry, $default, $result); + self::loadPermission($key, $entry, $default, $result); } return $result; } /** - * @param mixed[] $data - * @param Permission[] $output reference parameter + * @param mixed[] $data + * @param Permission[][] $output reference parameter * @phpstan-param array $data + * @phpstan-param array> $output * * @throws \Exception */ - public static function loadPermission(string $name, array $data, string $default = Permission::DEFAULT_OP, array &$output = []) : Permission{ + public static function loadPermission(string $name, array $data, string $default = self::DEFAULT_OP, array &$output = []) : void{ $desc = null; $children = []; if(isset($data["default"])){ @@ -104,7 +109,7 @@ class PermissionParser{ if(is_array($data["children"])){ foreach($data["children"] as $k => $v){ if(is_array($v)){ - $output[] = self::loadPermission($k, $v, $default, $output); + self::loadPermission($k, $v, $default, $output); } $children[$k] = true; } @@ -117,6 +122,6 @@ class PermissionParser{ $desc = $data["description"]; } - return new Permission($name, $desc, $default, $children); + $output[$default][] = new Permission($name, $desc, $children); } } diff --git a/src/plugin/PluginDescription.php b/src/plugin/PluginDescription.php index ad56adaaf..91f4c0381 100644 --- a/src/plugin/PluginDescription.php +++ b/src/plugin/PluginDescription.php @@ -83,7 +83,10 @@ class PluginDescription{ /** @var PluginEnableOrder */ private $order; - /** @var Permission[] */ + /** + * @var Permission[][] + * @phpstan-var array> + */ private $permissions = []; /** @@ -286,7 +289,8 @@ class PluginDescription{ } /** - * @return Permission[] + * @return Permission[][] + * @phpstan-return array> */ public function getPermissions() : array{ return $this->permissions; diff --git a/src/plugin/PluginManager.php b/src/plugin/PluginManager.php index d1a0325f7..c879c5dc9 100644 --- a/src/plugin/PluginManager.php +++ b/src/plugin/PluginManager.php @@ -31,7 +31,9 @@ use pocketmine\event\plugin\PluginDisableEvent; use pocketmine\event\plugin\PluginEnableEvent; use pocketmine\event\RegisteredListener; use pocketmine\network\mcpe\protocol\ProtocolInfo; +use pocketmine\permission\DefaultPermissions; use pocketmine\permission\PermissionManager; +use pocketmine\permission\PermissionParser; use pocketmine\Server; use pocketmine\timings\TimingsHandler; use pocketmine\utils\AssumptionFailedError; @@ -162,8 +164,32 @@ class PluginManager{ } $permManager = PermissionManager::getInstance(); - foreach($description->getPermissions() as $perm){ - $permManager->addPermission($perm); + $opRoot = $permManager->getPermission(DefaultPermissions::ROOT_OPERATOR); + $everyoneRoot = $permManager->getPermission(DefaultPermissions::ROOT_USER); + foreach($description->getPermissions() as $default => $perms){ + foreach($perms as $perm){ + $permManager->addPermission($perm); + switch($default){ + case PermissionParser::DEFAULT_TRUE: + $everyoneRoot->addChild($perm->getName(), true); + break; + case PermissionParser::DEFAULT_OP: + $opRoot->addChild($perm->getName(), true); + break; + case PermissionParser::DEFAULT_NOT_OP: + //TODO: I don't think anyone uses this, and it currently relies on some magic inside PermissibleBase + //to ensure that the operator override actually applies. + //Explore getting rid of this. + //The following grants this permission to anyone who has the "everyone" root permission. + //However, if the operator root node (which has higher priority) is present, the + //permission will be denied instead. + $everyoneRoot->addChild($perm->getName(), true); + $opRoot->addChild($perm->getName(), false); + break; + default: + break; + } + } } /** diff --git a/tests/phpstan/configs/l8-baseline.neon b/tests/phpstan/configs/l8-baseline.neon index 2ed212f93..00c0c4502 100644 --- a/tests/phpstan/configs/l8-baseline.neon +++ b/tests/phpstan/configs/l8-baseline.neon @@ -445,6 +445,11 @@ parameters: count: 1 path: ../../../src/plugin/PluginBase.php + - + message: "#^Cannot call method addChild\\(\\) on pocketmine\\\\permission\\\\Permission\\|null\\.$#" + count: 4 + path: ../../../src/plugin/PluginManager.php + - message: "#^Parameter \\#1 \\$closure of static method pocketmine\\\\utils\\\\Utils\\:\\:getNiceClosureName\\(\\) expects Closure, Closure\\|null given\\.$#" count: 2