ninja.leaping.permissionsex.bukkit.PEXPermissible.java Source code

Java tutorial

Introduction

Here is the source code for ninja.leaping.permissionsex.bukkit.PEXPermissible.java

Source

/**
 * PermissionsEx
 * Copyright (C) zml and PermissionsEx contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ninja.leaping.permissionsex.bukkit;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.Maps;
import ninja.leaping.permissionsex.PermissionsEx;
import ninja.leaping.permissionsex.exception.PermissionsLoadingException;
import ninja.leaping.permissionsex.subject.CalculatedSubject;
import ninja.leaping.permissionsex.util.NodeTree;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.PermissibleBase;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.permissions.PermissionRemovedExecutor;
import org.bukkit.plugin.Plugin;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.StreamSupport;

import static ninja.leaping.permissionsex.PermissionsEx.SUBJECTS_GROUP;
import static ninja.leaping.permissionsex.PermissionsEx.SUBJECTS_USER;
import static ninja.leaping.permissionsex.bukkit.BukkitTranslations.t;

/**
 * Implementation of Permissible using PEX for data
 */
public class PEXPermissible extends PermissibleBase {

    private static Metapermission[] METAPERMISSIONS = new Metapermission[] {
            /**
             * | Permission                 | Usage
             |----------------------------|------
             | `group.<group>`            | Added for each group a user is in
             | `groups.<group>`           | same as above
             | `options.<option>.<value>` | Each option the user has
             | `prefix.<prefix>`          | User's prefix
             | `suffix.<suffix>`          | User's suffix
             */
            new Metapermission(Pattern.compile("groups?\\.(?<name>.+)")) {
                @Override
                public boolean isMatch(Matcher result, CalculatedSubject subj,
                        Set<Map.Entry<String, String>> contexts) {
                    return subj.getParents(contexts)
                            .contains(Maps.immutableEntry(SUBJECTS_GROUP, result.group("name")));
                }

                @Override
                public Iterator<String> getValues(CalculatedSubject subj, Set<Map.Entry<String, String>> contexts) {
                    return subj
                            .getParents(contexts).stream().filter(
                                    ent -> ent.getKey().equals(SUBJECTS_GROUP))
                            .flatMap(ent -> StreamSupport.<String>stream(Spliterators.spliterator(
                                    new String[] { "group." + ent.getValue(), "groups." + ent.getValue() },
                                    Spliterator.IMMUTABLE | Spliterator.DISTINCT), false))
                            .iterator();
                }
            }, new Metapermission(Pattern.compile("options\\.(?<key>.*)\\.(?<value>.*)")) {
                @Override
                public boolean isMatch(Matcher result, CalculatedSubject subj,
                        Set<Map.Entry<String, String>> contexts) {
                    return subj.getOption(contexts, result.group("key"))
                            .map(val -> val.equals(result.group("value"))).orElse(false);
                }

                @Override
                public Iterator<String> getValues(CalculatedSubject subj, Set<Map.Entry<String, String>> contexts) {
                    return Iterables.transform(subj.getOptions(contexts).entrySet(),
                            ent -> "options." + ent.getKey() + "." + ent.getValue()).iterator();
                }
            }, new SpecificOptionMetapermission("prefix"), new SpecificOptionMetapermission("suffix") };

    private abstract static class Metapermission {
        /**
         * Pattern to match against
         */
        private final Pattern matchAgainst;

        protected Metapermission(Pattern matchAgainst) {
            this.matchAgainst = matchAgainst;
        }

        public abstract boolean isMatch(Matcher result, CalculatedSubject subj,
                Set<Map.Entry<String, String>> contexts);

        public abstract Iterator<String> getValues(CalculatedSubject subj, Set<Map.Entry<String, String>> contexts);
    }

    private static class SpecificOptionMetapermission extends Metapermission {
        private final String option;

        public SpecificOptionMetapermission(String option) {
            super(Pattern.compile(Pattern.quote(option) + "\\.(?<value>.+)"));
            this.option = option;
        }

        @Override
        public boolean isMatch(Matcher result, CalculatedSubject subj, Set<Map.Entry<String, String>> contexts) {
            return subj.getOption(contexts, option).map(val -> val.equals(result.group("value"))).orElse(false);
        }

        @Override
        public Iterator<String> getValues(CalculatedSubject subj, Set<Map.Entry<String, String>> contexts) {
            String ret = subj.getOptions(contexts).get(option);
            return ret == null ? Iterators.emptyIterator() : Iterators.singletonIterator(this.option + "." + ret);
        }
    }

    private final Player player;
    private final PermissionsExPlugin plugin;
    private PermissionsEx pex;
    private CalculatedSubject subj;
    private Permissible previousPermissible;
    private final Set<PEXPermissionAttachment> attachments = new HashSet<>();

    public PEXPermissible(Player player, PermissionsExPlugin plugin) {
        super(player);
        this.player = player;
        this.plugin = plugin;
        try {
            this.pex = plugin.getManager();
            this.subj = pex.getCalculatedSubject(SUBJECTS_USER, player.getUniqueId().toString());
        } catch (PermissionsLoadingException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    public PermissionsEx getManager() {
        return this.pex;
    }

    @Override
    public boolean isOp() {
        return super.isOp(); // TODO: Implement op handling
    }

    @Override
    public void setOp(boolean value) {
        super.setOp(value);
    }

    @Override
    public boolean isPermissionSet(String name) {
        Preconditions.checkNotNull(name, "name");
        name = name.toLowerCase();
        return getPermissionValue(getActiveContexts(), name) != 0;
    }

    private int getPermissionValue(Set<Map.Entry<String, String>> contexts, String permission) {
        int ret = getPermissionValue0(subj.getPermissions(contexts), permission);

        if (ret == 0) {
            for (Metapermission mPerm : METAPERMISSIONS) {
                Matcher match = mPerm.matchAgainst.matcher(permission);
                if (match.matches() && mPerm.isMatch(match, subj, contexts)) {
                    ret = 1;
                }
            }
        }

        if (pex.hasDebugMode()) {
            pex.getLogger().info(t("Checked permission %s for player %s in contexts %s: %s", permission,
                    player.getName(), contexts, ret));
        }
        return ret;
    }

    private int getPermissionValue0(NodeTree nodeTree, String name) {
        int val = nodeTree.get(name);
        if (val != 0) {
            return val;
        }

        for (Map.Entry<String, Boolean> ent : plugin.getPermissionList().getParents(name)) {
            val = getPermissionValue0(nodeTree, ent.getKey());
            if (!ent.getValue()) {
                val = -val;
            }
            if (val != 0) {
                return val;
            }
        }

        return 0;
    }

    @Override
    public boolean isPermissionSet(Permission perm) {
        Preconditions.checkNotNull(perm, "perm");
        return isPermissionSet(perm.getName());
    }

    @Override
    public boolean hasPermission(String inName) {
        Preconditions.checkNotNull(inName, "inName");
        inName = inName.toLowerCase();
        return getPermissionValue(getActiveContexts(), inName) > 0;
    }

    @Override
    public boolean hasPermission(Permission perm) {
        Preconditions.checkNotNull(perm, "perm");
        return hasPermission(perm.getName());
    }

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

    @Override
    public PermissionAttachment addAttachment(Plugin plugin) {
        final PEXPermissionAttachment attach = new PEXPermissionAttachment(plugin, player, this);
        this.subj.transientData()
                .update(input -> input.addParent(PermissionsEx.GLOBAL_CONTEXT,
                        PEXPermissionAttachment.ATTACHMENT_TYPE, attach.getIdentifier()))
                .thenRun(() -> this.attachments.add(attach));
        return attach;
    }

    public boolean removeAttachmentInternal(final PEXPermissionAttachment attach) {
        this.subj.transientData().update(input -> input.removeParent(PermissionsEx.GLOBAL_CONTEXT,
                PEXPermissionAttachment.ATTACHMENT_TYPE, attach.getIdentifier())).thenRun(() -> {
                    PermissionRemovedExecutor exec = attach.getRemovalCallback();
                    if (exec != null) {
                        exec.attachmentRemoved(attach);
                    }
                });
        return true;
    }

    @Override
    public void removeAttachment(PermissionAttachment attachment) {
        if (!(attachment instanceof PEXPermissionAttachment)) {
            throw new IllegalArgumentException("Provided attachment was not a PEX attachment!");
        }
        removeAttachmentInternal(((PEXPermissionAttachment) attachment));
        this.attachments.remove(attachment);
    }

    void removeAllAttachments() {
        for (PEXPermissionAttachment attach : this.attachments) {
            removeAttachmentInternal(attach);
        }
        this.attachments.clear();
    }

    @Override
    public void recalculatePermissions() { // We don't need this currently? Guess could clear cache somehow, but automated should get it right. well, except for people adding children to permissions -- that is just weird
    }

    @Override
    public synchronized void clearPermissions() {
        // todo
    }

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

    @Override
    public PermissionAttachment addAttachment(Plugin plugin, int ticks) {
        return addAttachment(plugin); // TODO: Implement timed permissions
    }

    @Override
    public Set<PermissionAttachmentInfo> getEffectivePermissions() {
        ImmutableSet.Builder<PermissionAttachmentInfo> ret = ImmutableSet.builder();
        final Set<Map.Entry<String, String>> activeContexts = getActiveContexts();
        ret.addAll(Iterables.transform(subj.getPermissions(activeContexts).asMap().entrySet(),
                input -> new PermissionAttachmentInfo(player, input.getKey(), null, input.getValue() > 0)));
        for (Metapermission mPerm : METAPERMISSIONS) {
            ret.addAll(Iterators.transform(mPerm.getValues(this.subj, activeContexts),
                    input -> new PermissionAttachmentInfo(player, input, null, true)));
        }
        return ret.build();
    }

    public Set<Map.Entry<String, String>> getActiveContexts() {
        ImmutableSet.Builder<Map.Entry<String, String>> builder = ImmutableSet.builder();
        builder.add(Maps.immutableEntry("world", player.getWorld().getName()));
        builder.add(Maps.immutableEntry("dimension", player.getWorld().getEnvironment().name().toLowerCase()));
        for (String serverTag : plugin.getManager().getConfig().getServerTags()) {
            builder.add(Maps.immutableEntry(PermissionsExPlugin.SERVER_TAG_CONTEXT, serverTag));
        }
        return builder.build();
    }

    public void setPreviousPermissible(Permissible previousPermissible) {
        this.previousPermissible = previousPermissible;
    }

    public Permissible getPreviousPermissible() {
        return previousPermissible;
    }
}