RFC Rewriting Permissions

Discussion in 'Contributing & RFCs' started by SOFe, May 4, 2018.

  1. SOFe

    SOFe Administrator Staff Member PMMP Team Poggit Admin

    I am looking into rewriting the permissions API. There are some goals to do that I want to know what everyone thinks:
    • Permission inheritance: The current parent-child relationship is ambiguous. Is there a clear way of evaluating permissions that we want to know?
    • Permission predicates: Do we want to have, in addition to permission attachments, "predicate" classes that check the permission dynamically every time? This means the predicate will be executed every time hasPermission() is called. Might have significant performance impact if abused.
    • Permission declaration: Do we want to check if every permission is declared in plugin.yml so that permission plugins can list them?
    • Permission types: In addition to boolean (true/false) permissions, do we want to allow other types, such as integers (i.e. limit the number of blocks placed per hour)? Or do you think this is irrelevant to permissions? And if it is irrelevant, is there a good way we can implement a generic abstraction for these settings such that they can be set through permissions plugin without explicitly depending on one?
    • Permission parameterization: In addition to more permission types, do we want to have permission parameters? For example, in SimpleWarp, instead of registering a permission for every new warp (e.g. simplewarp.warp.shop, simplewarp.warp.spawn, simplewarp.warp.jail, simplewarp.warp.toilet), what about having the simplewarp.warp permission for all of them and parameterize this permission?
    Note that many of the above suggestions have performance impacts. Permission parameterization, in particular, greatly increases the cost of calling hasPermission(), because the permissions plugin will have to recalculate the parameters every time.

    Feel free to add more suggestions.
    Last edited: May 4, 2018
  2. Michel5F30

    Michel5F30 Silverfish

    I guess it could be simplified. But the inheritance really makes sense, if the permissions will be automatically inherited by the dot.
    Example "test.a" & "test.b" will be automatically be childs of "test" when registered.
    It saves a lot performance, so you don't need to iterate over all permissions, instead always only those of the specific inheritance.
    Example a check for "test.c" does firstly check for the permission "test", then their childs instead of checking all registered permissions.

    I don't think this is worth the effort (altough i don't really understood the sense).

    After reconsidering this, i say yes. Throw an exception if it is not declared but checked for.
    But... it should be allowed if it's registered by a permission management plugin and not the plugin.yml of a plugin which checks for the permission (because of see next point). I mean, just check if the permission is registred, not if it is registred by the plugin.yml of the specific plugin which checks for it.

    Maybe as second step, because it's not really necessary.
    I just remember how Bukkit's plotme did it, it used the permission "plotme.limit.#" while "#" could be a freely defined number. By checking the child permissions of "plotme.limit" it will get the amount. This permission itself however is not registered by the plugin.yml of plotme, but by the used permission management plugin. So a check of registered permissions like mentioned at "permission declaration" should be fine.

    Server administrators and plugin developers should not loose the opportunity to declare (partly) freely definable permissions by a permission plugin e.g. "someplugin.somefeature.<dynamicpart>". So for server administrators it's a more flexible way of assinging permissions and for plugin developers it's easier to implement variable type permissions.
    Again to clarify, such permissions would not be declared by the plugin.yml of a plugin since the plugin developer doesn't know the configuration a server administrator will need, instead it will be registered by a permission management plugin, where a server administrator declares the permission.
    This makes Permission types like integers insignificant, because server administrators could define free text permissions (easiest case an integer as string like plotme did).

    Permission values should always be TRUE or FALSE to represent the status allowed or denied. I should always have the opportunity to define a specific permission to DENY it.

    I think that would exactly be the thing i described above by dynamic parts of permissions. And i think such dynamic parts would be the easiest thing (because they already work today with the old system) and the most common, since everyone already knows how to use them and it's the most easiest to understand for the end-users (server administrators).

    New suggestion:
    Automatically recognize wildcards and automatically set up the inheritance tree like described above.
    By rewriting the inheritance like i tried to explain above, you could easily check for a child permission called "*".
    Example strategy for a check of the permission "test.a"
    • Check if there is a permission registred called "test"
      If not, throw an exception (permission not declared)
      If yes:
      • Check if there is a child permission called "*" (full name "test.*")
        If yes, check if there is "a" (full name "test.a") and if it is denyed, if yes return false, if not return true (this makes it possible to deny permissions altough someone has a wildcard, which i think is important)
        If not:
      • Check if there is a child permission "a" (full name "test.a")
        If not, throw an exceprion (permission not declared)
        If yes, return its value (allowed or denied)
        • Same strategy for further inheritated permissions (i think this will be faster, because you're directly "navigating" to a specific permission instead of searching through all)
    I'm not sure if i clearly explained everything like i thought the system might work. But i really believe like this it will be a complete and consistent concept.
    Last edited: May 4, 2018
    SOFe likes this.
  3. Thunder33345

    Thunder33345 Moderator Staff Member

    i would suggest a way to retrieve child and parent nodes
    so say i want to glob(simplewarp.warp.*) from say a player i dont have to bruteforce it or do some trickery,
    i can just getpermissionchilds("parent.child"), which should return everything that is set that matches parent.child, with the warp example i will get an array of object/string or whatever that i can iterate through

    say the plotme example, someone used /showplotlimit, this could be help stress when plugin dont have to abuse the haspermission() with a forloop just so i can tell the user you have xyz plots, now i just have to get childs than sort by biggest

    regarding wildcards: i wish for wildcard that cant be cancelled/overwrite (think of root in most linux dristo) maybe like "**" which will take precedence over a direct set while the normal "*" still allow overwrites

    example: a.b.[c,d,e.[f,g,h]]
    in theory of following same behaviour for wildcard negations (-*) will unset all perm node not set which is kinda not helpful as it cant really do anything since a set will always take precedence so it might just be useless in the end because by default it
    so following example if i do -a.b.* the result is still a.b.[c,d,e.[f,g,h]] since it's already set thus overwrites it
    but overwriting wildcard negations (-**) it shall unset all set set perms
    and following example if i do -a.b.** the result is not set due to a.b is not directly set to true but indirectly set due to childs
  4. SOFe

    SOFe Administrator Staff Member PMMP Team Poggit Admin

    Why not check for test.c before checking for test?
    Also, if test is default true but test.c is default op, does a non-op get test.c?
    So what do you mean? Do you mean that if a plugin wants any of test.a/test.b, test should return true, or if the plugin wants all of test.a/test.b, test should return true, or if the plugin wants specifically test.a, it should check test first?
    The effort is not large. But for rarely queried permissions, predicates may be better, e.g. if the predicate checks if the player has an area. Or if it is something that cannot be toggled easily, e.g. the value is based on randomness (which doesn't really make sense), or based on the player's ping etc., predicates may be easier to use.
    In addition, it might be useful if permissions are declared dynamically (like the plotme example you mentioned).
    This would be complicated. If a plugin is unloaded, its permissions should be unloaded too. So if you register children of permissions from another plugin, they would get unloaded together. Things might get complicated.
    Side note: I usually write an internal file watcher on plugin.yml to generate a PHP constant class that contains plugin.yml permissions, e.g. plugin.yml:
    name: blah
          test.a: {}
    Then it generates a Permissions.php:
    namespace my\plugin\name\space;
    final class 
      public const 
    TEST "test";
      public const 
    TEST_A "test.a";
    This may be helpful for your workflow too.
    Sounds like a hack. After all, this isn't what the permissions system was supposed to do. Also, the performance isn't as efficient,
    Permissions are just "true" or "false". They do not have the lexical meaning of "allow" or "deny".
    Permissions are sometimes used for things that not even an op should have it. Basically any rank-related boolean value is possible, sometimes even normal players should have it but not ops.
    This is equivalent to looping through the permission children. Is this any different?
    And should grandchildren be counted?
    Also, asking the plugin to declare the need to check * may be useful for optimization.

    I maintain that a ternary permission system, i.e. true/false/not set, would be useful, especially when dealing with inheritance.
    Right now there is already PluginManager->getPermission($permissionName)->getChildren() to iterate on.
    You are talking about the config within the permission management plugin. This is the permission management plugin's own decision, and we don't have to think about it from the perspective of core API design.
  5. Thunder33345

    Thunder33345 Moderator Staff Member

    that's referring to the previous thread if say it was added
  6. Michel5F30

    Michel5F30 Silverfish

    To follow the inheritance top to bottom... and be able to evaluate the parent configuration like wildcards and negative permissions.
    Uhm... good question... maybe "yes"? this would be like a wildcard on "test.*".
    So "test" = "test.*", in general "<permission>" = "<permission>.*". I don't see a problem with that right now.

    Well there might be problems, because today it's different. So better not. If "test" is allowed, it has no influence on its childs, only "test.*" has. Non-wildcard permissions sould only have effect, when they're explicitely asked for.
    I mean, that if you're internally using array_key_exists, you're iterating over all permissions:
    I didn't consider permissions to be "unloaded"... now i understand why you want a plugin itself to register its permissions. However, you could provide a method for permission plugins to be able to register new permissions for another plugin. The permission plugin could pick out the corresponding plugin the permission is used for and pass it when registering the permission.
    It's like the difference of "array_key_exists", which iterates over all keys and "isset", which just checks the specific key (at least i think it does).

    In addition, here is a overview like i think it should work out for an example check as "test.a.b.c":
    • a negative permission as DENY (marked by leading -) always has priority
    • If the user has *, but -test.a.b.c -> false
    • If the user has *, but -test.a.b.* -> false
    • If the user has test.a.b.c, but -* -> false
    • If the user has test.a.b.c, but -test.a.b.* -> false
    • If the user has test.a.b.* and test.a.b.c -> true
    • If the user just has test.a.b.c -> true
    • It should not be possible to set the same permission twice (e.g. test.a.b.c and -test.a.b.c)
    I always thought, the old bukkit system (and like it is in PMMP now) by permissions can having the values FALSE, NOT_OP, OP, TRUE too complicated and superfluous.
    Couldn't they just have the values ALLOW and DENY? Well what to do with OPs then... OPs are always ALLOW?

    EDIT: Changed my mind abut one thing see above.
    Last edited: May 5, 2018
  7. SOFe

    SOFe Administrator Staff Member PMMP Team Poggit Admin

    So you think the parent permission should override the child one?
    I still don't think * is a good idea to be a permission in itself .It would make sense that a permission plugin wants to use that in the config file (user level), but it doesn't make sense to me to have the asterik in the permission name itself, being registered as an individual permission.
    Let's limit our discussion to the scope of the permission API. You are talking about the symbols used in a permission manager plugin.
  8. Michel5F30

    Michel5F30 Silverfish

    That's because i'm writing one currently and the changes you might implement might affect my plugin;).

    I think it's critical to implement wildcarts differently (e.g. when a user has "test.a", then he/she has all subpermissions automatically, that's not what end-users expect to be, because it never has been like this).
    So the only way i see is to make * wildcards as separated explicite permission.

    Wait.... i forgot, when you check if a permission is registred as soon as #hasPermission will be executed, so i (as a permission management plugin writer) could support wildcards by iterating over all childs (to allow permissions), without extending PermissibleBase and overwriting #hasPermission as i would need to do it now (because now there are unregistered permissions checked by plugins).

    Ok then... you could ignore wildcards and it could be part of a permission management plugin (will be part of mine).
    However you prefer... i just need to know before releasing my plugin. Either it will be part of PMMP or of my plugin.
  9. SOFe

    SOFe Administrator Staff Member PMMP Team Poggit Admin

    For permission using plugins (those that check hasPermission()):
    I don't understand how hard it is to loop through the children of a permission to check if any is true/false.

    function areAllChildren(Permissible $pstring $permissionbool $trueOrFalse){
    $permission Server::getInstance()->getPluginManager()->getPermission($permission);
    $permisison->getChildren() as $child){
    $p->hasPermission($child) !== $trueOrFalse){
    // to check if a player has any of the children of "test"
    For permission management plugins (those that affect the result of hasPermission() on other plugins):
    Wildcards are something they use in their own user interface. We don't need to consider it in the API, otherwise this gets overcomplicated. Permission managers are expected to convert user input into something compatible with the internals.
    Last edited: May 5, 2018

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.