Solved Custom Permissions Plugin Giving * Permission To Group/Player

Discussion in 'Spigot Plugin Development' started by MarcusNerloe, Dec 30, 2018.

  1. Hi guys,
    i am making my own Permissions plugin and it is working great, but i wan't to add the ability to add the * permission like you can in PermissionsEx, i have tried looking through the open source, for PermissionsEx, but i couldn't seem to figure out how they made it work.

    https://github.com/PEXPlugins/PermissionsEx/

    I wan't it so, when you give the * permission you get all permissions.
    When you get the permission essentials.* you get all permissions in essentials. (Just like in PermissionsEx)

    I saw someone saying it had something to do with PermissionsEx going into Bukkit, and changing something, but i am not sure.

    I hope you can help me. :)
    Thanks in regards.
     
  2. The answer in this thread might be of some help to you.
    https://www.spigotmc.org/threads/wildcard-permission-using-permissionattachment.217139/#post-2232128

    tl;dr is: there's no easy or straightforward way to implement wildcards. The only sane way to do it is to use reflection to inject a custom PermissibleBase implementation into players when they first connect to the server. This allows you to change the behaviour of the hasPermission method and emulate the "wildcard" system you see in other permission plugins.

    That just covers setting permissions, it doesn't explain wildcards.
     
    #3 Luck, Jan 2, 2019
    Last edited: Jan 2, 2019
  3. Thanks both of you. :) I had already found other posts where they wrote that you had to inject the Permissible Base implementation into players. (I think i saw your post, actually. (Luck)) But i couldn't figure out, how it was done? Can you help in any way? :)
     
  4. Firstly, it should be fairly straightforward to create a class that extends PermissibleBase, and then overrides the hasPermission method to implement the wildcard behaviour. Luckily that class is exposed in the Bukkit API.

    The next bit is slightly trickier, you'll need to use the Java Reflection API to replace the players existing PermissibleBase with your own one when they login. It's stored under the "perm" field of CraftHumanEntity, which is one of the CraftBukkit implementation classes for the Player interface in Bukkit. As this is an org.bukkit.craftbukkit class, you also have to account for the package versioning. That sort of thing should be covered by a Bukkit related reflection tutorial though if you're unsure.

    You'll also want to copy over any existing state from the old PermissibleBase instance (again you can use reflection to do this), including attachments, permissions, etc.

    There's some (fairly ugly looking, but simple) code that does all of ^^^ here.
    https://github.com/weaondara/Bungee...ock/bungeeperms/platform/bukkit/Injector.java

    Orrr, some (slightly less ugly looking, but possibly more complicated) code that does the same here.
    https://github.com/lucko/LuckPerms/...t/inject/permissible/PermissibleInjector.java

    I advise against blindly copying any of the code linked above - try to understand what's going on an write your own version. You'll get more out of the process that way. ;)
     
    • Winner Winner x 1
  5. Thank you so much Luck! :) I will try reading through all of it, and writing it part for part, to learn and figure out how to do this. :) Again thank you so much, for taking your time! :D
     
    • Like Like x 1
  6. Why not just use op? It's meant for this kind of thing.
     
  7. Because if you wan't to give a player access to... lets say, all WorldEdit commands, then you don't wan't to give them access to all commans (with OP), but instead give them access to all WE Commands (with the permission: worldedit.*).
     
    • Agree Agree x 1
  8. Code (Java):
    public class PPermissible extends PermissibleBase {

        private Player p;
        private Map<String, PermissionAttachmentInfo> permissions;
        private Permissible oldPermissible = new PermissibleBase(null);

        public PPermissible(Player p) {
            super(p);
            this.p = p;
            permissions = new LinkedHashMap<String, PermissionAttachmentInfo>() {
                @Override
                public PermissionAttachmentInfo put(String k, PermissionAttachmentInfo v) {
                    PermissionAttachmentInfo existing = this.get(k);
                    if (existing != null) {
                        return existing;
                    }

                    return super.put(k, v);
                }
            };

            Permissions.getInstance().setField(PermissibleBase.class, this, permissions, "permissions");
        }

        public Permissible getOldPermissible() {
            return oldPermissible;
        }

        public void setOldPermissible(Permissible oldPermissible) {
            this.oldPermissible = oldPermissible;
        }

        public boolean hasSuperPerm(String perm) {
            if (oldPermissible == null) {
                return super.hasPermission(perm);
            }

            return oldPermissible.hasPermission(perm);
        }

        @Override
        public boolean hasPermission(String permission) {
            if (permission == null) {
                throw new NullPointerException("permission");
            }

            boolean res = Permissions.getInstance().getPermissionsManager().has(p, permission);

            return res;
        }

        @Override
        public boolean hasPermission(Permission permission) {
            return hasPermission(permission.getName());
        }

        @Override
        public void recalculatePermissions() {
            if (oldPermissible == null) {
                super.recalculatePermissions();
                return;
            }

            oldPermissible.recalculatePermissions();
        }

        @Override
        public Set<PermissionAttachmentInfo> getEffectivePermissions() {
            if (oldPermissible == null) {
                return super.getEffectivePermissions();
            }

            return new LinkedHashSet<>(permissions.values());
        }

        @Override
        public boolean isOp() {
            if (oldPermissible == null) {
                return super.isOp();
            }

            return oldPermissible.isOp();
        }

        @Override
        public void setOp(boolean value) {
            if (oldPermissible == null) {
                super.setOp(value);
                return;
            }

            oldPermissible.setOp(value);
        }

        @Override
        public boolean isPermissionSet(String permission) {
            return true;
        }

        @Override
        public boolean isPermissionSet(Permission perm) {
            return true;
        }

        @Override
        public PermissionAttachment addAttachment(Plugin plugin) {
            if (oldPermissible == null) {
                return super.addAttachment(plugin);
            }

            return oldPermissible.addAttachment(plugin);
        }

        @Override
        public PermissionAttachment addAttachment(Plugin plugin, int ticks) {
            if (oldPermissible == null) {
                return super.addAttachment(plugin, ticks);
            }

            return oldPermissible.addAttachment(plugin, ticks);
        }

        @Override
        public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) {
            if (oldPermissible == null) {
                return super.addAttachment(plugin, name, value);
            }

            return oldPermissible.addAttachment(plugin, name, value);
        }

        @Override
        public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) {
            if (oldPermissible == null) {
                return super.addAttachment(plugin, name, value, ticks);
            }

            return oldPermissible.addAttachment(plugin, name, value, ticks);
        }

        @Override
        public void removeAttachment(PermissionAttachment attachment) {
            if (oldPermissible == null) {
                super.removeAttachment(attachment);
                return;
            }

            oldPermissible.removeAttachment(attachment);
        }

        @Override
        public synchronized void clearPermissions() {
            if (oldPermissible == null) {
                super.clearPermissions();
                return;
            }

            if (oldPermissible instanceof PermissibleBase) {
                PermissibleBase base = (PermissibleBase) oldPermissible;
                base.clearPermissions();
            }
        }


    }
    Code (Java):
    public final class PermissibleInjector {

        private static final Field HUMAN_ENTITY_PERMISSIBLE_FIELD;
        private static final Field PERMISSIBLE_BASE_ATTATCHMENTS_FIELD;

        static {
            try {
                Field humanEntityPermissibleField;
                try {
                    humanEntityPermissibleField = CraftBukkitUtil.obcClass("entity.CraftHumanEntity").getDeclaredField("perm");
                    humanEntityPermissibleField.setAccessible(true);
                } catch (Exception e) {
                    humanEntityPermissibleField = CraftBukkitUtil.obcClass("net.glowstone.entity.GlowHumanEntity").getDeclaredField("permissions");
                    humanEntityPermissibleField.setAccessible(true);
                }

                HUMAN_ENTITY_PERMISSIBLE_FIELD = humanEntityPermissibleField;

                PERMISSIBLE_BASE_ATTATCHMENTS_FIELD = PermissibleBase.class.getDeclaredField("attachments");
                PERMISSIBLE_BASE_ATTATCHMENTS_FIELD.setAccessible(true);
            } catch (ClassNotFoundException | NoSuchFieldException e) {
                throw new ExceptionInInitializerError(e);
            }
        }

        public static Permissible inject(Player p, Permissible newPermissible) {
            try {
                PermissibleBase oldPermissible = (PermissibleBase) HUMAN_ENTITY_PERMISSIBLE_FIELD.get(p);

                if (newPermissible instanceof PermissibleBase) {
                    ((List) PERMISSIBLE_BASE_ATTATCHMENTS_FIELD.get(newPermissible)).addAll((List) PERMISSIBLE_BASE_ATTATCHMENTS_FIELD.get(oldPermissible));
                }

                HUMAN_ENTITY_PERMISSIBLE_FIELD.set(p, newPermissible);
                return oldPermissible;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        public static Permissible uninject(Player p) {
            try {
                Permissible permissible = (Permissible) HUMAN_ENTITY_PERMISSIBLE_FIELD.get(p);
                if (permissible instanceof PPermissible) {
                    HUMAN_ENTITY_PERMISSIBLE_FIELD.set(p, ((PPermissible) permissible).getOldPermissible());
                    return (PPermissible) permissible;
                }

                return null;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        public static PermissibleBase getPermissible(Player p) {
            try {
                PermissibleBase permissible = (PermissibleBase) HUMAN_ENTITY_PERMISSIBLE_FIELD.get(p);
                return permissible;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

    }
    Code (Java):
        @EventHandler
        public void onJoin(PlayerJoinEvent e) {
            Player p = e.getPlayer();

            Permissible permissible = new PPermissible(p);
            Permissible oldPermissible = PermissibleInjector.inject(p, permissible);
            ((PPermissible) permissible).setOldPermissible(oldPermissible);
    }
    I have now made my own Permissible class called "PPermissible", and i have made an injector, that gets fired whenever a player joins.. But the * permision still doesn't seem to do anything? I am still using PermissionAttachments to set the permissions, is that wrong or? ;)
     
  9. md_5

    Administrator Developer

    * doesn't exist in the bukkit permissions system by design.

    Leads to sadness and/or hilarity when plugins define permissions like foo.vanishonjoin and foo.lightningonjoin and then people wonder why on earth their admins are invisible and striking lightning whenever they log in (yes this has happened)
     
  10. I don't understand.. how shall i do it then?
     
  11. md_5

    Administrator Developer

    Op the player and let defaults handle it
     
  12. Thanks, but what if i only want to give the player permissions, to a specific plugin? :) fx. basics.* or worldedit.*
     
  13. md_5

    Administrator Developer

    The plugin can easily define those wildcards
     
  14. Do you mean the individual plugin, or my plugin? :)
     
  15. md_5

    Administrator Developer

    Individual
     
  16. You could list all permissions up for each plugin in a hashmap with the plugin name as key. Then, give the player the permission (e.g. worldedit.*). Now, in the method where you check whether said player has the permission, return true if the player has the permission OR if the player has the wildcard and the permission required for that plugin is in the hashmap. Putting the permissions in a hashmap can be done during startup I guess, but I have no idea what methods to use to get the permissions for each plugin. (as defined in plugin.yml). That's one way I can think of, but it's not efficient. If that doesn't work out, tell me and drop some of your code :)
     
  17. If you want anything.* to be wildcard and anything.** to be deep wildcard, check for those permissions when traveling down path for the xxx permission, so if I ask for xxx.yyy check for anything xxx or ** being deep, then next node is the last, so check for * or yyy, or ** since it's still deep. That way it would work (would be a bit slower I'd think for checking perms but nothing too too heavy). I wouldn't do it like that since it slows it down a bit but it's how I'd do it if I were to.
     
  18. Not every plugin, has added their permissions to the plugin.yml file. :) Thats why i can't do it that way. :/ But i will try, thanks.

    I just want anything.* to be a wildcard, but as i said before, i can't use the plugin.yml permissions, because not every plugin, has set them up. :)

    Thanks for the replies. :D
     

Share This Page