Flan (Fabric)

Flan (Fabric)

1M Downloads

Flan ignores global permissions

LukeOnuke opened this issue ยท 0 comments

commented

Modloader

Fabric

Minecraft version

1.21.8

Modloader version

fabric-api 0.129.0+1.21.8

Mod version

flan-1.21.8-1.12.0-fabric / 1.21.8 branch

Description

What happens?

Flan disregards global permissions with all players, regardless of their permissions with the permission api or with their vanilla permission level (op status).

Why is this important?

This causes problems in servers such as my own and with others #413 where players can give themselves gamebreaking potion effects or edit the entrance/exit text (if for example the server doesn't want to allow it, like my own).

How does this happen?

In this section of the bug report ill give a in depth view about my findings in the source code as to why this happens, I'll shortly be committing a pull request that addresses this.

Preamble, config.

The default config has this in global permissions.

"globalDefaultPerms": {
    "*": {
      "flan:edit_potions": "ALLFALSE",
      "flan:flight": "ALLTRUE",
      "flan:lock_items": "ALLTRUE",
      "flan:may_flight": "ALLFALSE",
      "flan:mob_spawn": "ALLFALSE",
      "flan:no_hunger": "ALLFALSE",
      "flan:teleport": "ALLFALSE"
    }
  }

Permission checking

I did some digging around as to how permissions are checked.
In the handler for the claim menu screen, this is the display that utilizes the same method:

case 4 -> {
ItemStack stack = ServerScreenHelper.createStack(Items.POTION,
ServerScreenHelper.coloredGuiText("flan.screenMenuPotion", ChatFormatting.GOLD));
if (!this.hasPerm(this.data, this.player, BuiltinPermission.EDITPOTIONS))
ServerScreenHelper.addLore(stack, ServerScreenHelper.coloredGuiText("flan.screenNoPerm", ChatFormatting.DARK_RED));
this.slots.get(i).set(stack);
}

This is the line that does it in the click handler, same class:

if (this.hasPerm(this.data, player, BuiltinPermission.EDITPOTIONS)) {

If we go into that method and inject logging code for debug.

private void logIfEditPoutions(ResourceLocation perm, String message){
        if(perm.toString().equals("flan:edit_potions")) Flan.LOGGER.info("canInterract({}, ...): {}",  perm.toString(), message);
    }

    @Override
    public boolean canInteract(ServerPlayer player, ResourceLocation perm, BlockPos pos, boolean message) {
        logIfEditPoutions(perm, "BEGIN");
        boolean realPlayer = player != null && player.getClass().equals(ServerPlayer.class);
        message = message && realPlayer && player.connection != null; //dont send messages to fake players
        //Delegate interaction to FAKEPLAYER perm if a fake player
        if (player != null && !realPlayer) {
            //Some mods use the actual user/placer/owner whatever of the fakeplayer. E.g. ComputerCraft
            //For those mods we dont pass them as fake players
            if (this.fakePlayers.contains(player.getUUID()))
                return true;
            if (!player.getUUID().equals(this.owner) && !this.playersGroups.containsKey(player.getUUID())) {
                perm = BuiltinPermission.FAKEPLAYER;
            }
        }
        InteractionResult res = ClaimEvents.INSTANCE.claimCheck(player, perm, pos);
        logIfEditPoutions(perm, "InteractionResult= " + res); //debug
        if (res != InteractionResult.PASS)
            return res != InteractionResult.FAIL;
        if (!this.isAdminClaim()) {
            Config.GlobalType global = ConfigHandler.CONFIG.getGlobal(this.level, perm);
            logIfEditPoutions(perm, "global.getValue= " + global.getValue()); //debug
            if (!global.canModify()) {

                if (global.getValue() || (player != null && this.playerBypassesPermission(player, perm))){
                    logIfEditPoutions(perm, "return true ");
                    return true;
                }
                if (message)
                    player.displayClientMessage(ClaimUtils.translatedText("flan.noPermissionSimple", ChatFormatting.DARK_RED), true);
                if (perm.equals(BuiltinPermission.FAKEPLAYER))
                    this.getOwnerPlayer().ifPresent(p -> PlayerClaimData.get(p).notifyFakePlayerInteraction(player, pos, this));
                logIfEditPoutions(perm, "return false "); //debug
                return false;
            }
            logIfEditPoutions(perm, "PASSED " + global.getValue()); //debug
            if (ConfigHandler.CONFIG.offlineProtectActivation != -1 && (LogoutTracker.getInstance(this.level.getServer()).justLoggedOut(this.getOwner()) || this.getOwnerPlayer().isPresent())) {
                return global == Config.GlobalType.NONE || global.getValue();
            }
        }
        if (PermissionManager.getInstance().isGlobalPermission(perm)) {
            for (Claim claim : this.subClaims) {
                if (claim.insideClaim(pos)) {
                    return claim.canInteract(player, perm, pos, message);
                }
            }
            if (this.hasPerm(perm))
                return true;
            if (message)
                player.displayClientMessage(ClaimUtils.translatedText("flan.noPermissionSimple", ChatFormatting.DARK_RED), true);
            if (perm.equals(BuiltinPermission.FAKEPLAYER))
                this.getOwnerPlayer().ifPresent(p -> PlayerClaimData.get(p).notifyFakePlayerInteraction(player, pos, this));
            return false;
        }
        if (this.playerBypassesPermission(player, perm))
            return true;
        if (!perm.equals(BuiltinPermission.EDITCLAIM) && !perm.equals(BuiltinPermission.EDITPERMS))
            for (Claim claim : this.subClaims) {
                if (claim.insideClaim(pos)) {
                    return claim.canInteract(player, perm, pos, message);
                }
            }
        if (this.playersGroups.containsKey(player.getUUID())) {
            Map<ResourceLocation, Boolean> map = this.permissions.get(this.playersGroups.get(player.getUUID()));
            if (map != null && map.containsKey(perm)) {
                if (map.get(perm))
                    return true;
                if (message)
                    player.displayClientMessage(ClaimUtils.translatedText("flan.noPermissionSimple", ChatFormatting.DARK_RED), true);
                if (perm.equals(BuiltinPermission.FAKEPLAYER))
                    this.getOwnerPlayer().ifPresent(p -> PlayerClaimData.get(p).notifyFakePlayerInteraction(player, pos, this));
                return false;
            }
        }
        if (this.hasPerm(perm))
            return true;
        if (message)
            player.displayClientMessage(ClaimUtils.translatedText("flan.noPermissionSimple", ChatFormatting.DARK_RED), true);
        if (perm.equals(BuiltinPermission.FAKEPLAYER))
            this.getOwnerPlayer().ifPresent(p -> PlayerClaimData.get(p).notifyFakePlayerInteraction(player, pos, this));
        return false;
    }

You will notice that at the end it returns true at:

if (global.getValue() || (player != null && this.playerBypassesPermission(player, perm)))
return true;

This is according to the log you get in console thanks to the added loggers:

[16:21:43] [Server thread/INFO] (flan) canInterract(flan:edit_potions, ...): BEGIN
[16:21:43] [Server thread/INFO] (flan) canInterract(flan:edit_potions, ...): InteractionResult= Pass[]
[16:21:43] [Server thread/INFO] (flan) canInterract(flan:edit_potions, ...): global.getValue= false
[16:21:43] [Server thread/INFO] (flan) canInterract(flan:edit_potions, ...): return true 

According to that if statement and the situation where these checks are called it can be presumed that:

  • the permission will be false (actually checked this with the inserted logger, refer to code block above)
  • player will not be null
  • this.playerBypassesPermission(player, perm)) has to return true in order for the return true to be called

The aforementioned method doesnt check for global permissions, it will just return true if its the owner of the claim.

if (player.getUUID().equals(this.owner))
return true;

Steps to reproduce

  1. Load up a blank server with flan, with default configs.
  2. Join the game as non op.
  3. Click on edit potion effects.
  4. Watch as flan disregards global permissions.

Mods that might affect the issue

N/A

Logs

https://gist.github.com/LukeOnuke/fe87328c0827309c35188d5b9aa1d9bf