package com.bergerkiller.bukkit.common.internal;
import java.util.Locale;
import net.milkbowl.vault.permission.Permission;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider;
import com.bergerkiller.bukkit.common.ToggledState;
import com.bergerkiller.bukkit.common.utils.StringUtil;
/**
* Takes care of Vault permission checks, and additional logic needed to handle permissions.
* It's main purpose is redirecting permission checks and providing *-wildcard support.
*/
public class PermissionHandler implements PermissionChecker {
private static final String PERMISSION_TEST_NODE_ROOT = "bkcommonlib.permission.testnode";
private static final String PERMISSION_TEST_NODE = PERMISSION_TEST_NODE_ROOT + ".test";
private static final String PERMISSION_TEST_NODE_ALL = PERMISSION_TEST_NODE_ROOT + ".*";
private boolean hasSuperWildcardSupport = false;
private final ToggledState needsWildcardCheck = new ToggledState(false);
private boolean vaultEnabled = false;
private Permission vaultPermission = null;
public void updateDependency(Plugin plugin, String pluginName, boolean enabled) {
if (pluginName.equals("Vault")) {
if (this.vaultEnabled == enabled) {
return;
}
if (enabled) {
// Enable the support for Vault
RegisteredServiceProvider<Permission> permissionProvider = Bukkit.getServicesManager().getRegistration(Permission.class);
if (permissionProvider != null) {
this.vaultPermission = permissionProvider.getProvider();
this.vaultEnabled = this.vaultPermission != null;
}
} else {
// Disable the support for Vault
this.vaultPermission = null;
this.vaultEnabled = false;
}
if (this.vaultEnabled) {
this.needsWildcardCheck.set();
}
}
}
private boolean hasSuperWildcardSupport() {
if (this.needsWildcardCheck.clear()) {
this.hasSuperWildcardSupport = false;
// Perform a little experiment with a random player name
// Give the player a test node, and see if the *-permission is handled right
// Also pass in an invalid value to check that there are no inconsistencies
// Set up the test permission
org.bukkit.permissions.Permission perm = getPermission(PERMISSION_TEST_NODE);
perm.setDefault(PermissionDefault.FALSE);
// Find a player that does not have the test permission (this is kinda pointless, but hey, we are secure!)
final int maxTries = 10000;
final String world = null;
final String testPlayerNameBase = "TestPlayer";
StringBuilder testPlayerNameBldr = new StringBuilder(testPlayerNameBase);
String testPlayerName = testPlayerNameBase;
int i;
for (i = 0; i < maxTries; i++) {
testPlayerNameBldr.setLength(testPlayerNameBase.length());
testPlayerNameBldr.append(i);
testPlayerName = testPlayerNameBldr.toString();
try {
if (!this.vaultPermission.playerHas(world, testPlayerName, PERMISSION_TEST_NODE)) {
break;
}
} catch (Throwable t) {
// Failure
i = maxTries;
}
}
// Check for permission failure (perhaps there is a consistent permission thing)
if (i < (maxTries - 1)) {
try {
// Grant permission with the *-node
this.vaultPermission.playerAdd(world, testPlayerName, PERMISSION_TEST_NODE_ALL);
// Adding was successful, ALWAYS do the removal phase
// See if it had the desired effect
try {
this.hasSuperWildcardSupport = this.vaultPermission.playerHas(world, testPlayerName, PERMISSION_TEST_NODE);
} catch (Throwable t) {
}
// Undo permission change
try {
this.vaultPermission.playerRemove(world, testPlayerName, PERMISSION_TEST_NODE_ALL);
} catch (Throwable t) {
}
} catch (Throwable t) {
}
}
// Undo the previously added Bukkit Permission
Bukkit.getPluginManager().removePermission(perm);
}
return this.hasSuperWildcardSupport;
}
public org.bukkit.permissions.Permission getPermission(String node) {
org.bukkit.permissions.Permission perm = Bukkit.getPluginManager().getPermission(node);
if (perm == null) {
// Figure out what permission default to use
// This is done by checking all *-names, and if they exist, using that default
// ===================================
// TRUE found or anything else: TRUE
// NOT_OP AND OP found: TRUE
// NOT_OP found: NOT_OP
// OP found: OP
// otherwise: FALSE
// ===================================
PermissionDefaultFinder finder = new PermissionDefaultFinder();
permCheckWildcard(finder, null, new StringBuilder(node.length()), node.split("\\."), 0);
// Use permission default FALSE to avoid OP-players having automatic permissions for *-nodes
perm = new org.bukkit.permissions.Permission(node, finder.getDefault());
Bukkit.getPluginManager().addPermission(perm);
}
return perm;
}
@Override
public boolean handlePermission(CommandSender sender, String permission) {
// Initialize the permission (and it's default) prior to check
org.bukkit.permissions.Permission perm = getPermission(permission);
// Resort back to the default logic
if (this.vaultEnabled) {
// On a new line to avoid possible preliminary field access
if (!this.vaultPermission.hasSuperPermsCompat()) {
// Use a call here
// First, make sure to handle our permission defaults
if (perm.getDefault().getValue(sender.isOp())) {
return true;
}
// Handle the remainder using Vault
if (sender instanceof Player) {
Player p = (Player) sender;
return this.vaultPermission.playerHas(p.getWorld(), p.getName(), permission);
} else {
return this.vaultPermission.has(sender, permission);
}
}
}
// Resort to the simpler Bukkit Super Permissions
return sender.hasPermission(permission);
}
public boolean hasPermission(CommandSender sender, String[] permissionNode) {
if (hasSuperWildcardSupport()) {
return handlePermission(sender, StringUtil.join(".", permissionNode).toLowerCase(Locale.ENGLISH));
}
return permCheckWildcard(this, sender, permissionNode);
}
public boolean hasPermission(CommandSender sender, String permissionNode) {
String lowerNode = permissionNode.toLowerCase(Locale.ENGLISH);
if (handlePermission(sender, lowerNode)) {
return true;
}
// Only if no *-wildcard support is available internally do we check that as well
return !hasSuperWildcardSupport() && permCheckWildcard(this, sender, lowerNode);
}
private static boolean permCheckWildcard(PermissionChecker checker, CommandSender sender, String node) {
return permCheckWildcard(checker, sender, node.split("\\."));
}
private static boolean permCheckWildcard(PermissionChecker checker, CommandSender sender, String[] args) {
// Compute the expected length for the StringBuilder buffer
int expectedLength = args.length;
for (String node : args) {
expectedLength += node.length();
}
// Now call the other internal method
return permCheckWildcard(checker, sender, new StringBuilder(expectedLength), args, 0);
}
/**
* Performs a recursive permission check while taking *-permissions in account
*
* @param sender - pass in the sender to check for
* @param root - use a new buffer
* @param args - pass in all parts of the permission node
* @param argIndex - pass in 0
* @return True if permission is granted, False if not
*/
private static boolean permCheckWildcard(PermissionChecker checker, CommandSender sender, StringBuilder root, String[] args, int argIndex) {
// Check the permission
String rootText = root.toString();
if (!rootText.isEmpty() && checker.handlePermission(sender, rootText)) {
return true;
}
// End of the sequence?
if (argIndex >= args.length) {
return false;
}
int rootLength = root.length();
if (rootLength != 0) {
root.append('.');
rootLength++;
}
final int newArgIndex = argIndex + 1;
// Check permission with original name
root.append(args[argIndex].toLowerCase(Locale.ENGLISH));
if (permCheckWildcard(checker, sender, root, args, newArgIndex)) {
return true;
}
// Try with *-signs
root.setLength(rootLength);
root.append('*');
return permCheckWildcard(checker, sender, root, args, newArgIndex);
}
private static final class PermissionDefaultFinder implements PermissionChecker {
private boolean hasTRUE, hasOP, hasNOTOP;
public PermissionDefault getDefault() {
if (hasTRUE) {
return PermissionDefault.TRUE;
} else if (hasOP) {
return PermissionDefault.OP;
} else if (hasNOTOP) {
return PermissionDefault.NOT_OP;
} else {
return PermissionDefault.FALSE;
}
}
@Override
public boolean handlePermission(CommandSender sender, String permission) {
org.bukkit.permissions.Permission perm = Bukkit.getPluginManager().getPermission(permission);
if (perm == null) {
return false;
}
switch (perm.getDefault()) {
case TRUE : this.hasTRUE = true; break;
case OP : this.hasOP = true; break;
case NOT_OP : this.hasNOTOP = true; break;
default: break;
}
if (hasOP && hasNOTOP) {
hasTRUE = true;
}
// Quit checking if we found out it's TRUE
return hasTRUE;
}
}
}