Apply PermissionAttachment permissions at a higher priority than normal nodes using the transient system (#515)

This commit is contained in:
Luck 2017-10-22 10:24:04 +01:00
parent 1baefaade4
commit f109cb684a
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
10 changed files with 369 additions and 302 deletions

View File

@ -173,7 +173,7 @@ public class BukkitListener implements Listener {
t.printStackTrace(); t.printStackTrace();
} }
plugin.refreshAutoOp(player); plugin.refreshAutoOp(user, player);
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR)
@ -201,7 +201,7 @@ public class BukkitListener implements Listener {
} }
// everything is going well. login was processed ok, this is just to refresh auto-op status. // everything is going well. login was processed ok, this is just to refresh auto-op status.
plugin.refreshAutoOp(e.getPlayer()); plugin.refreshAutoOp(plugin.getUserManager().getIfLoaded(e.getPlayer().getUniqueId()), e.getPlayer());
} }
// Wait until the last priority to unload, so plugins can still perform permission checks on this event // Wait until the last priority to unload, so plugins can still perform permission checks on this event
@ -252,6 +252,6 @@ public class BukkitListener implements Listener {
@EventHandler(priority = EventPriority.LOWEST) @EventHandler(priority = EventPriority.LOWEST)
public void onWorldChange(PlayerChangedWorldEvent e) { public void onWorldChange(PlayerChangedWorldEvent e) {
plugin.getContextManager().invalidateCache(e.getPlayer()); plugin.getContextManager().invalidateCache(e.getPlayer());
plugin.refreshAutoOp(e.getPlayer()); plugin.refreshAutoOp(plugin.getUserManager().getIfLoaded(e.getPlayer().getUniqueId()), e.getPlayer());
} }
} }

View File

@ -402,23 +402,15 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
} }
} }
public void refreshAutoOp(Player player) { public void refreshAutoOp(User user, Player player) {
if (getConfiguration().get(ConfigKeys.AUTO_OP)) {
try {
LPPermissible permissible = Injector.getPermissible(player.getUniqueId());
if (permissible == null || !permissible.getActive().get()) {
return;
}
User user = permissible.getUser();
if (user == null) { if (user == null) {
return; return;
} }
Map<String, Boolean> backing = user.getUserData().getPermissionData(permissible.calculateContexts()).getImmutableBacking(); if (getConfiguration().get(ConfigKeys.AUTO_OP)) {
Map<String, Boolean> backing = user.getUserData().getPermissionData(contextManager.getApplicableContexts(player)).getImmutableBacking();
boolean op = Optional.ofNullable(backing.get("luckperms.autoop")).orElse(false); boolean op = Optional.ofNullable(backing.get("luckperms.autoop")).orElse(false);
player.setOp(op); player.setOp(op);
} catch (Exception ignored) {}
} }
} }

View File

@ -31,9 +31,6 @@ import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.bukkit.LPBukkitPlugin; import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.bukkit.model.Injector;
import me.lucko.luckperms.bukkit.model.LPPermissible;
import me.lucko.luckperms.bukkit.processors.AttachmentProcessor;
import me.lucko.luckperms.bukkit.processors.ChildProcessor; import me.lucko.luckperms.bukkit.processors.ChildProcessor;
import me.lucko.luckperms.bukkit.processors.DefaultsProcessor; import me.lucko.luckperms.bukkit.processors.DefaultsProcessor;
import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory; import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory;
@ -47,7 +44,6 @@ import me.lucko.luckperms.common.processors.RegexProcessor;
import me.lucko.luckperms.common.processors.WildcardProcessor; import me.lucko.luckperms.common.processors.WildcardProcessor;
import java.util.List; import java.util.List;
import java.util.UUID;
@AllArgsConstructor @AllArgsConstructor
public class BukkitCalculatorFactory extends AbstractCalculatorFactory { public class BukkitCalculatorFactory extends AbstractCalculatorFactory {
@ -63,14 +59,6 @@ public class BukkitCalculatorFactory extends AbstractCalculatorFactory {
processors.add(new ChildProcessor(plugin.getChildPermissionProvider())); processors.add(new ChildProcessor(plugin.getChildPermissionProvider()));
} }
if (plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_ATTACHMENT_PERMISSIONS)) {
final UUID uuid = plugin.getUuidCache().getExternalUUID(user.getUuid());
processors.add(new AttachmentProcessor(() -> {
LPPermissible permissible = Injector.getPermissible(uuid);
return permissible == null ? null : permissible.getAttachmentPermissions();
}));
}
if (plugin.getConfiguration().get(ConfigKeys.APPLYING_REGEX)) { if (plugin.getConfiguration().get(ConfigKeys.APPLYING_REGEX)) {
processors.add(new RegexProcessor()); processors.add(new RegexProcessor());
} }

View File

@ -45,63 +45,17 @@ public class DummyPermissible implements Permissible {
onRefresh.run(); onRefresh.run();
} }
@Override @Override public Set<PermissionAttachmentInfo> getEffectivePermissions() { return Collections.emptySet(); }
public Set<PermissionAttachmentInfo> getEffectivePermissions() { @Override public boolean isPermissionSet(String name) { return false; }
return Collections.emptySet(); @Override public boolean isPermissionSet(Permission perm) { return false; }
} @Override public boolean hasPermission(String name) { return false; }
@Override public boolean hasPermission(Permission perm) { return false; }
@Override @Override public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { return null; }
public boolean isPermissionSet(String name) { @Override public PermissionAttachment addAttachment(Plugin plugin) { return null; }
return false; @Override public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { return null; }
} @Override public PermissionAttachment addAttachment(Plugin plugin, int ticks) { return null; }
@Override public void removeAttachment(PermissionAttachment attachment) {}
@Override @Override public boolean isOp() { return false; }
public boolean isPermissionSet(Permission perm) { @Override public void setOp(boolean value) {}
return false;
}
@Override
public boolean hasPermission(String name) {
return false;
}
@Override
public boolean hasPermission(Permission perm) {
return false;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) {
return null;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin) {
return null;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) {
return null;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, int ticks) {
return null;
}
@Override
public void removeAttachment(PermissionAttachment attachment) {
} }
@Override
public boolean isOp() {
return false;
}
@Override
public void setOp(boolean value) {
}
}

View File

@ -35,77 +35,25 @@ import java.util.Collections;
import java.util.Set; import java.util.Set;
public class DummyPermissibleBase extends PermissibleBase { public class DummyPermissibleBase extends PermissibleBase {
public DummyPermissibleBase() { public static final DummyPermissibleBase INSTANCE = new DummyPermissibleBase();
private DummyPermissibleBase() {
super(null); super(null);
} }
@Override @Override public boolean isOp() { return false; }
public boolean isOp() { @Override public void setOp(boolean value) {}
return false; @Override public boolean isPermissionSet(String name) { return false; }
} @Override public boolean isPermissionSet(Permission perm) { return false; }
@Override public boolean hasPermission(String inName) { return false; }
@Override @Override public boolean hasPermission(Permission perm) { return false; }
public void setOp(boolean value) { @Override public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { return null; }
@Override public PermissionAttachment addAttachment(Plugin plugin) { return null; }
@Override public void removeAttachment(PermissionAttachment attachment) {}
@Override public void recalculatePermissions() {}
@Override public void clearPermissions() {}
@Override public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { return null; }
@Override public PermissionAttachment addAttachment(Plugin plugin, int ticks) { return null; }
@Override public Set<PermissionAttachmentInfo> getEffectivePermissions() { return Collections.emptySet(); }
} }
@Override
public boolean isPermissionSet(String name) {
return false;
}
@Override
public boolean isPermissionSet(Permission perm) {
return false;
}
@Override
public boolean hasPermission(String inName) {
return false;
}
@Override
public boolean hasPermission(Permission perm) {
return false;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) {
return null;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin) {
return null;
}
@Override
public void removeAttachment(PermissionAttachment attachment) {
}
@Override
public void recalculatePermissions() {
}
@Override
public synchronized void clearPermissions() {
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) {
return null;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, int ticks) {
return null;
}
@Override
public Set<PermissionAttachmentInfo> getEffectivePermissions() {
return Collections.emptySet();
}
}

View File

@ -0,0 +1,78 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.model;
import org.bukkit.Server;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginLoader;
import java.io.File;
import java.io.InputStream;
import java.util.List;
import java.util.logging.Logger;
/**
* Dummy plugin instance
*/
public class DummyPlugin implements Plugin {
public static final DummyPlugin INSTANCE = new DummyPlugin();
private DummyPlugin() {
}
@Override
public boolean isEnabled() {
return true;
}
@Override public File getDataFolder() { return null; }
@Override public PluginDescriptionFile getDescription() { return null; }
@Override public FileConfiguration getConfig() { return null; }
@Override public InputStream getResource(String s) { return null; }
@Override public void saveConfig() {}
@Override public void saveDefaultConfig() {}
@Override public void saveResource(String s, boolean b) {}
@Override public void reloadConfig() {}
@Override public PluginLoader getPluginLoader() { return null; }
@Override public Server getServer() { return null; }
@Override public void onDisable() {}
@Override public void onLoad() {}
@Override public void onEnable() {}
@Override public boolean isNaggable() { return false; }
@Override public void setNaggable(boolean b) {}
@Override public ChunkGenerator getDefaultWorldGenerator(String s, String s1) { return null; }
@Override public Logger getLogger() { return null; }
@Override public String getName() { return null; }
@Override public boolean onCommand(CommandSender commandSender, Command command, String s, String[] strings) { return false; }
@Override public List<String> onTabComplete(CommandSender commandSender, Command command, String s, String[] strings) { return null; }
}

View File

@ -116,13 +116,12 @@ public class Injector {
//noinspection unchecked //noinspection unchecked
List<PermissionAttachment> attachments = (List<PermissionAttachment>) PERMISSIBLE_BASE_ATTACHMENTS_FIELD.get(oldPermissible); List<PermissionAttachment> attachments = (List<PermissionAttachment>) PERMISSIBLE_BASE_ATTACHMENTS_FIELD.get(oldPermissible);
newPermissible.addAttachments(attachments); newPermissible.convertAndAddAttachments(attachments);
attachments.clear(); attachments.clear();
oldPermissible.clearPermissions(); oldPermissible.clearPermissions();
// Setup the new permissible // Setup the new permissible
newPermissible.getActive().set(true); newPermissible.getActive().set(true);
newPermissible.recalculatePermissions(false);
newPermissible.setOldPermissible(oldPermissible); newPermissible.setOldPermissible(oldPermissible);
newPermissible.updateSubscriptionsAsync(); newPermissible.updateSubscriptionsAsync();
@ -164,23 +163,14 @@ public class Injector {
// handle the replacement permissible. // handle the replacement permissible.
if (dummy) { if (dummy) {
// just inject a dummy class. this is used when we know the player is about to quit the server. // just inject a dummy class. this is used when we know the player is about to quit the server.
HUMAN_ENTITY_PERMISSIBLE_FIELD.set(player, new DummyPermissibleBase()); HUMAN_ENTITY_PERMISSIBLE_FIELD.set(player, DummyPermissibleBase.INSTANCE);
} else { } else {
// otherwise, inject the permissible they had when we first injected.
List<PermissionAttachment> lpAttachments = lpPermissible.getAttachments();
PermissibleBase newPb = lpPermissible.getOldPermissible(); PermissibleBase newPb = lpPermissible.getOldPermissible();
if (newPb == null) { if (newPb == null) {
newPb = new PermissibleBase(player); newPb = new PermissibleBase(player);
} }
//noinspection unchecked
List<PermissionAttachment> newPbAttachments = (List<PermissionAttachment>) PERMISSIBLE_BASE_ATTACHMENTS_FIELD.get(newPb);
newPbAttachments.addAll(lpAttachments);
lpAttachments.clear();
HUMAN_ENTITY_PERMISSIBLE_FIELD.set(player, newPb); HUMAN_ENTITY_PERMISSIBLE_FIELD.set(player, newPb);
} }
} }

View File

@ -37,25 +37,19 @@ import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.verbose.CheckOrigin; import me.lucko.luckperms.common.verbose.CheckOrigin;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.permissions.PermissibleBase; import org.bukkit.permissions.PermissibleBase;
import org.bukkit.permissions.Permission; import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment; import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo; import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.permissions.PermissionRemovedExecutor;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -94,12 +88,9 @@ public class LPPermissible extends PermissibleBase {
// if the permissible is currently active. // if the permissible is currently active.
private final AtomicBoolean active = new AtomicBoolean(false); private final AtomicBoolean active = new AtomicBoolean(false);
// the permissions registered by PermissionAttachments.
// stored in this format, as that's what is used by #getEffectivePermissions
private final Map<String, PermissionAttachmentInfo> attachmentPermissions = new ConcurrentHashMap<>();
// the attachments hooked onto the permissible. // the attachments hooked onto the permissible.
private final List<PermissionAttachment> attachments = Collections.synchronizedList(new ArrayList<>()); // this collection is only modified by the attachments themselves
final Set<LPPermissionAttachment> attachments = ConcurrentHashMap.newKeySet();
public LPPermissible(@NonNull Player parent, @NonNull User user, @NonNull LPBukkitPlugin plugin) { public LPPermissible(@NonNull Player parent, @NonNull User user, @NonNull LPBukkitPlugin plugin) {
super(parent); super(parent);
@ -204,8 +195,10 @@ public class LPPermissible extends PermissibleBase {
* *
* @param attachments the attachments to add * @param attachments the attachments to add
*/ */
public void addAttachments(Collection<PermissionAttachment> attachments) { public void convertAndAddAttachments(Collection<PermissionAttachment> attachments) {
this.attachments.addAll(attachments); for (PermissionAttachment attachment : attachments) {
new LPPermissionAttachment(this, attachment).hook();
}
} }
/** /**
@ -226,135 +219,70 @@ public class LPPermissible extends PermissibleBase {
@Override @Override
public Set<PermissionAttachmentInfo> getEffectivePermissions() { public Set<PermissionAttachmentInfo> getEffectivePermissions() {
Set<PermissionAttachmentInfo> perms = new HashSet<>(); Set<PermissionAttachmentInfo> perms = new HashSet<>();
perms.addAll(attachmentPermissions.values());
perms.addAll( perms.addAll(
user.getUserData().getPermissionData(calculateContexts()).getImmutableBacking().entrySet().stream() user.getUserData().getPermissionData(calculateContexts()).getImmutableBacking().entrySet().stream()
.map(e -> new PermissionAttachmentInfo(parent, e.getKey(), null, e.getValue())) .map(e -> new PermissionAttachmentInfo(parent, e.getKey(), null, e.getValue()))
.collect(Collectors.toList()) .collect(Collectors.toList())
); );
return perms; return perms;
} }
@Override @Override
public PermissionAttachment addAttachment(@NonNull Plugin plugin, @NonNull String name, boolean value) { public LPPermissionAttachment addAttachment(Plugin plugin) {
if (!plugin.isEnabled()) { LPPermissionAttachment ret = new LPPermissionAttachment(this, plugin);
throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled"); ret.hook();
} return ret;
PermissionAttachment result = addAttachment(plugin);
result.setPermission(name, value);
recalculatePermissions();
return result;
} }
@Override @Override
public PermissionAttachment addAttachment(@NonNull Plugin plugin) { public PermissionAttachment addAttachment(Plugin plugin, @NonNull String name, boolean value) {
if (!plugin.isEnabled()) { PermissionAttachment ret = addAttachment(plugin);
throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled"); ret.setPermission(name, value);
} return ret;
PermissionAttachment result = new PermissionAttachment(plugin, parent);
attachments.add(result);
recalculatePermissions();
return result;
} }
@Override @Override
public PermissionAttachment addAttachment(@NonNull Plugin plugin, @NonNull String name, boolean value, int ticks) { public LPPermissionAttachment addAttachment(@NonNull Plugin plugin, int ticks) {
if (!plugin.isEnabled()) { if (!plugin.isEnabled()) {
throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled"); throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled");
} }
PermissionAttachment result = addAttachment(plugin, ticks); LPPermissionAttachment ret = addAttachment(plugin);
if (result != null) { if (getPlugin().getServer().getScheduler().scheduleSyncDelayedTask(plugin, ret::remove, ticks) == -1) {
result.setPermission(name, value); ret.remove();
throw new RuntimeException("Could not add PermissionAttachment to " + parent + " for plugin " + plugin.getDescription().getFullName() + ": Scheduler returned -1");
} }
return ret;
return result;
} }
@Override @Override
public PermissionAttachment addAttachment(@NonNull Plugin plugin, int ticks) { public LPPermissionAttachment addAttachment(Plugin plugin, @NonNull String name, boolean value, int ticks) {
if (!plugin.isEnabled()) { LPPermissionAttachment ret = addAttachment(plugin, ticks);
throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled"); ret.setPermission(name, value);
} return ret;
PermissionAttachment result = addAttachment(plugin);
if (Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, result::remove, ticks) == -1) {
Bukkit.getServer().getLogger().log(Level.WARNING, "Could not add PermissionAttachment to " + parent + " for plugin " + plugin.getDescription().getFullName() + ": Scheduler returned -1");
result.remove();
return null;
} else {
return result;
}
} }
@Override @Override
public void removeAttachment(@NonNull PermissionAttachment attachment) { public void removeAttachment(@NonNull PermissionAttachment attachment) {
if (attachments.contains(attachment)) { if (!(attachment instanceof LPPermissionAttachment)) {
attachments.remove(attachment); throw new IllegalArgumentException("Given attachment is not a LPPermissionAttachment.");
PermissionRemovedExecutor ex = attachment.getRemovalCallback();
if (ex != null) {
ex.attachmentRemoved(attachment);
} }
recalculatePermissions(); LPPermissionAttachment a = ((LPPermissionAttachment) attachment);
} else { if (a.getPermissible() != this) {
throw new IllegalArgumentException("Given attachment is not part of Permissible object " + parent); throw new IllegalArgumentException("Attachment does not belong to this permissible.");
} }
a.remove();
} }
@Override @Override
public void recalculatePermissions() { public void recalculatePermissions() {
recalculatePermissions(true); // do nothing
}
public void recalculatePermissions(boolean invalidate) {
if (attachmentPermissions == null) {
return;
}
attachmentPermissions.clear();
for (PermissionAttachment attachment : attachments) {
calculateChildPermissions(attachment.getPermissions(), false, attachment);
}
if (invalidate) {
user.getUserData().invalidatePermissionCalculators();
}
} }
@Override @Override
public synchronized void clearPermissions() { public void clearPermissions() {
Set<String> perms = attachmentPermissions.keySet(); attachments.forEach(LPPermissionAttachment::remove);
for (String name : perms) {
Bukkit.getServer().getPluginManager().unsubscribeFromPermission(name, parent);
}
attachmentPermissions.clear();
}
private void calculateChildPermissions(Map<String, Boolean> children, boolean invert, PermissionAttachment attachment) {
for (Map.Entry<String, Boolean> e : children.entrySet()) {
Permission perm = Bukkit.getServer().getPluginManager().getPermission(e.getKey());
boolean value = e.getValue() ^ invert;
String name = e.getKey().toLowerCase();
attachmentPermissions.put(name, new PermissionAttachmentInfo(parent, name, attachment, value));
Bukkit.getServer().getPluginManager().subscribeToPermission(name, parent);
if (perm != null) {
calculateChildPermissions(perm.getChildren(), !value, attachment);
}
}
} }
} }

View File

@ -0,0 +1,192 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.model;
import lombok.Getter;
import lombok.Setter;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.node.ImmutableTransientNode;
import me.lucko.luckperms.common.node.NodeFactory;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionRemovedExecutor;
import org.bukkit.plugin.Plugin;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* PermissionAttachment for LuckPerms.
*
* Applies all permissions directly to the backing user instance via transient nodes.
*/
public class LPPermissionAttachment extends PermissionAttachment {
/**
* The parent LPPermissible
*/
@Getter
private final LPPermissible permissible;
/**
* The plugin which "owns" this attachment, may be null
*/
private final Plugin owner;
/**
* The permissions being applied by this attachment
*/
private final Map<String, Boolean> perms = Collections.synchronizedMap(new HashMap<>());
/**
* If the attachment has been applied to the user
*/
private boolean hooked = false;
/**
* Callback to run when the attachment is removed
*/
@Getter
@Setter
private PermissionRemovedExecutor removalCallback = null;
public LPPermissionAttachment(LPPermissible permissible, Plugin owner) {
super(DummyPlugin.INSTANCE, null);
this.permissible = permissible;
this.owner = owner;
}
public LPPermissionAttachment(LPPermissible permissible, PermissionAttachment bukkit) {
super(DummyPlugin.INSTANCE, null);
this.permissible = permissible;
this.owner = null;
// copy
perms.putAll(bukkit.getPermissions());
}
public void hook() {
hooked = true;
permissible.attachments.add(this);
for (Map.Entry<String, Boolean> entry : perms.entrySet()) {
setPermissionInternal(entry.getKey(), entry.getValue());
}
}
private void setPermissionInternal(String name, boolean value) {
if (!permissible.getPlugin().getConfiguration().get(ConfigKeys.APPLY_BUKKIT_ATTACHMENT_PERMISSIONS)) {
return;
}
ImmutableTransientNode node = ImmutableTransientNode.of(NodeFactory.make(name, value), this);
if (permissible.getUser().setTransientPermission(node).asBoolean()) {
permissible.getUser().getRefreshBuffer().request();
}
}
private void unsetPermissionInternal(String name) {
if (!permissible.getPlugin().getConfiguration().get(ConfigKeys.APPLY_BUKKIT_ATTACHMENT_PERMISSIONS)) {
return;
}
if (permissible.getUser().removeIfTransient(n -> n instanceof ImmutableTransientNode && ((ImmutableTransientNode) n).getOwner() == this && n.getPermission().equals(name))) {
permissible.getUser().getRefreshBuffer().request();
}
}
@Override
public boolean remove() {
if (!hooked) {
return false;
}
if (permissible.getUser().removeIfTransient(n -> n instanceof ImmutableTransientNode && ((ImmutableTransientNode) n).getOwner() == this)) {
permissible.getUser().getRefreshBuffer().request();
}
if (removalCallback != null) {
removalCallback.attachmentRemoved(this);
}
hooked = false;
permissible.attachments.remove(this);
return true;
}
@Override
public void setPermission(String name, boolean value) {
Boolean previous = perms.put(name, value);
if (previous != null && previous == value) {
return;
}
if (!hooked) {
return;
}
if (previous != null) {
unsetPermissionInternal(name);
}
setPermissionInternal(name, value);
}
@Override
public void unsetPermission(String name) {
Boolean previous = perms.remove(name);
if (previous == null) {
return;
}
if (!hooked) {
return;
}
unsetPermissionInternal(name);
}
@Override
public Map<String, Boolean> getPermissions() {
return perms;
}
@Override
public Plugin getPlugin() {
return owner != null ? owner : permissible.getPlugin();
}
@Override
public boolean equals(Object obj) {
return this == obj;
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
}

View File

@ -23,42 +23,39 @@
* SOFTWARE. * SOFTWARE.
*/ */
package me.lucko.luckperms.bukkit.processors; package me.lucko.luckperms.common.node;
import lombok.AccessLevel;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull;
import lombok.ToString;
import lombok.experimental.Delegate;
import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.common.processors.PermissionProcessor;
import org.bukkit.permissions.PermissionAttachmentInfo;
import java.util.Map;
import java.util.function.Supplier;
/** /**
* Permission Processor for permissions set to a player via permission attachments. * Holds a Node and plus an owning object. All calls are passed onto the contained Node instance.
*/ */
@AllArgsConstructor
public class AttachmentProcessor implements PermissionProcessor {
@Getter @Getter
private final Supplier<Map<String, PermissionAttachmentInfo>> map; @ToString
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Override public final class ImmutableTransientNode implements Node {
public Tristate hasPermission(String permission) { public static ImmutableTransientNode of(@NonNull Node node, @NonNull Object owner) {
Map<String, PermissionAttachmentInfo> m = map.get(); return new ImmutableTransientNode(node, owner);
if (m == null) {
return Tristate.UNDEFINED;
} }
PermissionAttachmentInfo pai = m.get(permission); @Delegate
return pai == null ? Tristate.UNDEFINED : Tristate.fromBoolean(pai.getValue()); private final Node node;
private final Object owner;
@Override
public int hashCode() {
return node.hashCode();
} }
@Override @Override
public void updateBacking(Map<String, Boolean> map) { public boolean equals(Object obj) {
// Do nothing, this doesn't use the backing return this == obj || node.equals(obj);
} }
} }