Effective removal of the old permissions system.

keep-around/6d4ac31d79b8d01b9303a187e765abb5ff83af9f
Pingex aka Raphaël 9 years ago
parent b505e79d2a
commit 6d4ac31d79

@ -5,7 +5,6 @@ import net.pingex.dcf.commands.InternalCommands;
import net.pingex.dcf.core.Configuration;
import net.pingex.dcf.core.GatewayConnectionManager;
import net.pingex.dcf.modularity.PluginLoader;
import net.pingex.dcf.permissions.PermissionsCommands;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import sx.blah.discord.api.ClientBuilder;
@ -45,7 +44,6 @@ public class DiscordCommandableFramework
// Register internal commands
new InternalCommands().getCommands().forEach(CommandRegistry::registerCommand);
new PermissionsCommands().getCommands().forEach(CommandRegistry::registerCommand);
// Run plugins
PluginLoader.getInstance().bulkRunPlugins();

@ -1,7 +1,6 @@
package net.pingex.dcf.commands;
import net.pingex.dcf.commands.options.ICommandOption;
import net.pingex.dcf.permissions.DefaultPermission;
import sx.blah.discord.handle.impl.events.MessageReceivedEvent;
import java.lang.reflect.Method;
import java.util.Arrays;
@ -39,12 +38,6 @@ public abstract class Command implements ICommandExecutor
*/
private String usage;
/**
* Default permission, ie. when the permissions provider doesn't return anything.
*/
@Deprecated
private DefaultPermission defaultPermission;
/**
* Contains all options for this command.
*/
@ -57,17 +50,15 @@ public abstract class Command implements ICommandExecutor
* @param description Description of the command
* @param isEnabled Is the command enabled ?
* @param usage Command usage help
* @param defaultPermission Default permission, ie. when the permissions provider doesn't return anything.
* @param options Command options.
*/
public Command(String name, List<String> aliases, String description, boolean isEnabled, String usage, @Deprecated DefaultPermission defaultPermission, Set<ICommandOption> options)
public Command(String name, List<String> aliases, String description, boolean isEnabled, String usage, Set<ICommandOption> options)
{
this.name = name;
this.aliases = aliases;
this.description = description;
this.isEnabled = isEnabled;
this.usage = usage;
this.defaultPermission = defaultPermission;
this.commandOptions = options;
}
@ -80,7 +71,6 @@ public abstract class Command implements ICommandExecutor
public static final String DESCRIPTION = "No command description provided.";
public static final boolean IS_ENABLED = true;
public static final String USAGE = "No command usage provided.";
public static final DefaultPermission DEFAULT_PERMISSION = DefaultPermission.EVERYONE;
public static final Set<ICommandOption> OPTIONS = Collections.emptySet();
}
@ -114,11 +104,6 @@ public abstract class Command implements ICommandExecutor
*/
private String usage = Defaults.USAGE;
/**
* Default permission, ie. when the permissions provider doesn't return anything.
*/
private DefaultPermission defaultPermission = Defaults.DEFAULT_PERMISSION;
/**
* Command options
*/
@ -163,13 +148,6 @@ public abstract class Command implements ICommandExecutor
return this;
}
@Deprecated
public Builder defaultPermission(DefaultPermission defaultPermission)
{
this.defaultPermission = defaultPermission;
return this;
}
public Builder options(Set<ICommandOption> options)
{
commandOptions = options;
@ -183,7 +161,7 @@ public abstract class Command implements ICommandExecutor
*/
public Command build(ICommandExecutor toExecute)
{
return new Command(name, aliases, description, isEnabled, usage, defaultPermission, commandOptions)
return new Command(name, aliases, description, isEnabled, usage, commandOptions)
{
@Override
public void execute(MessageReceivedEvent event, List<String> arguments) throws Throwable
@ -201,7 +179,7 @@ public abstract class Command implements ICommandExecutor
*/
public Command build(Method target, Object invokable)
{
return new Command(name, aliases, description, isEnabled, usage, defaultPermission, commandOptions)
return new Command(name, aliases, description, isEnabled, usage, commandOptions)
{
@Override
public void execute(MessageReceivedEvent event, List<String> arguments) throws Throwable
@ -247,12 +225,6 @@ public abstract class Command implements ICommandExecutor
return usage;
}
@Deprecated
public DefaultPermission getDefaultPermission()
{
return defaultPermission;
}
public Set<ICommandOption> getOptions()
{
return Collections.unmodifiableSet(commandOptions);

@ -7,13 +7,11 @@ import net.pingex.dcf.commands.parser.ICommandParser;
import net.pingex.dcf.commands.parser.ParserException;
import net.pingex.dcf.core.Configuration;
import net.pingex.dcf.modularity.PluginWrapper;
import net.pingex.dcf.permissions.PermissionsHandler;
import net.pingex.dcf.util.DiscordInteractionsUtil;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import sx.blah.discord.handle.impl.events.MessageReceivedEvent;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutorService;

@ -3,14 +3,11 @@ package net.pingex.dcf.commands;
import net.pingex.dcf.commands.options.ICommandOption;
import net.pingex.dcf.commands.options.ScopeOption;
import net.pingex.dcf.core.Configuration;
import net.pingex.dcf.permissions.DefaultPermission;
import net.pingex.dcf.permissions.PermissionsHandler;
import net.pingex.dcf.util.ArgumentParser;
import net.pingex.dcf.util.DiscordInteractionsUtil;
import org.apache.commons.lang3.StringUtils;
import sx.blah.discord.handle.impl.events.MessageReceivedEvent;
import java.util.*;
import java.util.stream.Collectors;
/**
* Internal commands of DCF.
@ -33,7 +30,6 @@ public class InternalCommands implements IWithCommands
private static final String DESCRIPTION = "List all available commands.";
private static final boolean IS_ENABLED = true;
private static final String USAGE = "Page";
private static final DefaultPermission DEFAULT_PERMISSION = DefaultPermission.EVERYONE;
private static final Set<ICommandOption> OPTIONS = Collections.singleton(new ScopeOption(ScopeOption.CommandScope.ANYWHERE));
/**
@ -45,15 +41,14 @@ public class InternalCommands implements IWithCommands
private ListCommand()
{
super(NAME, ALIASES, DESCRIPTION, IS_ENABLED, USAGE, DEFAULT_PERMISSION, OPTIONS);
super(NAME, ALIASES, DESCRIPTION, IS_ENABLED, USAGE, OPTIONS);
}
@Override
public void execute(MessageReceivedEvent event, List<String> arguments)
{
// Parameters
Set<Command> bank = CommandRegistry.getRegistry()
.stream().filter(command -> PermissionsHandler.canRunCommand(event.getMessage(), command)).collect(Collectors.toSet());
Set<Command> bank = CommandRegistry.getRegistry();
int amountPages = (int) Math.ceil(bank.size()/(double)COMMANDS_PER_PAGE);
int requestedPage;
@ -115,14 +110,13 @@ public class InternalCommands implements IWithCommands
private static final String DESCRIPTION = "Gives the usage of a command.";
private static final boolean IS_ENABLED = true;
private static final String USAGE = "Command";
private static final DefaultPermission DEFAULT_PERMISSION = DefaultPermission.EVERYONE;
private static final Set<ICommandOption> OPTIONS = Collections.singleton(new ScopeOption(ScopeOption.CommandScope.ANYWHERE));
static final UsageCommand INSTANCE = new UsageCommand();
private UsageCommand()
{
super(NAME, ALIASES, DESCRIPTION, IS_ENABLED, USAGE, DEFAULT_PERMISSION, OPTIONS);
super(NAME, ALIASES, DESCRIPTION, IS_ENABLED, USAGE, OPTIONS);
}
@Override

@ -1,63 +0,0 @@
package net.pingex.dcf.permissions;
import net.pingex.dcf.core.Configuration;
import sx.blah.discord.handle.obj.IGuild;
import sx.blah.discord.handle.obj.IUser;
import java.util.function.Predicate;
/**
* Default behavior when a permissions provider doesn't return `true` or `false`
*/
@Deprecated
public enum DefaultPermission implements Predicate<DefaultPermission.Tuple>
{
/**
* Everyone is allowed to run the command.
*/
EVERYONE(tuple -> true),
/**
* Only the guild owner is allowed to run the command.
*/
GUILD_OWNER(tuple -> tuple.guild != null && tuple.user.getID().equals(tuple.guild.getOwnerID())),
/**
* Only the bot owner is allowed to run the command.
*/
BOT_OWNER(tuple -> tuple.user.getID().equals(Configuration.BOT_OWNER)),
/**
* Guild Owner x Bot Owner
*/
ANY_OWNER(tuple -> GUILD_OWNER.test(tuple) || BOT_OWNER.test(tuple)),
/**
* Nobody is allowed to run the command
*/
NONE(tuple -> false);
DefaultPermission(Predicate<Tuple> predicate)
{
this.predicate = predicate;
}
private Predicate<Tuple> predicate;
@Override
public boolean test(Tuple o)
{
return predicate.test(o);
}
public static class Tuple
{
IUser user;
IGuild guild;
public Tuple(IUser user, IGuild guild)
{
this.user = user;
this.guild = guild;
}
}
}

@ -6,7 +6,7 @@ import sx.blah.discord.handle.obj.IRole;
import sx.blah.discord.handle.obj.IUser;
/**
* Provider of permissions, ie. the proxy between {@code PermissionsHandler} and a data storage.
* Provider of permissions, ie. a database which stores permissions data.
*/
public interface ICommandPermissionsProvider
{

@ -1,269 +0,0 @@
package net.pingex.dcf.permissions;
import net.pingex.dcf.commands.Command;
import net.pingex.dcf.commands.CommandRegistry;
import net.pingex.dcf.commands.IWithCommands;
import net.pingex.dcf.util.ArgumentParser;
import net.pingex.dcf.util.DiscordInteractionsUtil;
import org.apache.commons.lang3.StringUtils;
import sx.blah.discord.handle.impl.events.MessageReceivedEvent;
import sx.blah.discord.handle.obj.IGuild;
import sx.blah.discord.handle.obj.IRole;
import sx.blah.discord.handle.obj.IUser;
import java.util.*;
/**
* Commands for the permissions package.
*/
public class PermissionsCommands implements IWithCommands
{
@Override
public Set<Command> getCommands()
{
return new HashSet<>(Arrays.asList(
isAllowedCommand, setUser
));
}
private static final Command isAllowedCommand =
new Command.Builder("perm:canRun")
.description("Tells whether target user is allowed to run the command.")
.enabled(true)
.usage("Command <User#Disc|ID|me> <Guild|ID|this|*> [verbose]")
.defaultPermission(DefaultPermission.BOT_OWNER)
.build(PermissionsCommands::isAllowedImpl);
private static void isAllowedImpl(MessageReceivedEvent event, List<String> arguments)
{
// Parameters
Command target = null;
IUser userToLookup = null;
boolean verbose = false;
IGuild targetGuild = null;
ICommandPermissionsProvider provider = PermissionsHandler.getProvider();
// Argchk size
if(arguments.size() != 3 && arguments.size() != 4)
{
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "Invalid arguments.");
return;
}
// Argchk#1 - valid command
Optional<Command> uncheckedTarget = CommandRegistry.getCommandOrAliasByName(arguments.get(0));
if(!uncheckedTarget.isPresent()) // Command existence
{
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "Target command not found.");
return;
}
else target = uncheckedTarget.get();
// Argchk#2 - uid OR username#1234 OR "me"
if(arguments.get(1).equalsIgnoreCase("me")) userToLookup = event.getMessage().getAuthor(); // me
else // Anything else
try
{
Optional<IUser> uncheckedUser = ArgumentParser.checkParseUsernameOrID(arguments.get(1), event.getClient());
if(uncheckedUser.isPresent()) userToLookup = uncheckedUser.get();
else
{
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "User not found.");
return;
}
}
catch(ArgumentParser.ParserException e)
{
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "Invalid arguments.");
return;
}
// Argchk#3 - Guild
if(arguments.get(2).equalsIgnoreCase("this"))
try
{
targetGuild = event.getMessage().getGuild(); // this
}
catch(UnsupportedOperationException ignored) // DM
{
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "`this` is not supported outside a guild.");
return;
}
else if(arguments.get(2).equals("*")) targetGuild = null; // Global - ~NOP
else // Parse Name/ID
{
Optional<IGuild> uncheckedGuild = ArgumentParser.checkParseGuildOrID(arguments.get(2), event.getClient());
if(uncheckedGuild.isPresent()) targetGuild = uncheckedGuild.get();
else
{
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "Guild not found.");
return;
}
}
// Argchk#4 - verbosity
if(arguments.size() == 4) verbose = true;
// ========================================
if(verbose)
{
StringBuilder builder = new StringBuilder()
.append("**Permission walkthrough**\n")
.append("```\n");
builder.append("User: ").append(userToLookup.getName()).append("#").append(userToLookup.getDiscriminator()).append("\n");
builder.append("UID: ").append(userToLookup.getID()).append("\n");
if(targetGuild != null)
{
builder.append("Guild: ").append(targetGuild.getName()).append("\n");
builder.append("Guild ID: ").append(targetGuild.getID()).append("\n");
}
builder.append("Command: ").append(target.getName()).append("\n");
builder.append("\n");
if(targetGuild != null)
builder.append("UID Guild check: ").append(booleanToString(provider.validateUser(targetGuild, userToLookup, target))).append("\n");
builder.append("UID Global check: ").append(booleanToString(provider.validateUser(null, userToLookup, target))).append("\n");
if(targetGuild != null)
{
builder.append("Role check:\n");
for(IRole i : targetGuild.getRolesForUser(userToLookup))
builder.append("> ").append(StringUtils.rightPad(i.getName() + ":", 16))
.append(booleanToString(provider.validateGroup(i, target))).append("\n");
}
builder.append("Default behavior: ")
.append(booleanToString(target.getDefaultPermission().test(new DefaultPermission.Tuple(userToLookup, targetGuild))))
.append(" (").append(target.getDefaultPermission().toString()).append(")").append("\n");
builder.append("```");
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), builder.toString());
}
else
{
StringBuilder builder = new StringBuilder()
.append("**Permission summary**\n")
.append("```\n");
builder.append("User: ").append(userToLookup.getName()).append("#").append(userToLookup.getDiscriminator()).append("\n");
builder.append("UID: ").append(userToLookup.getID()).append("\n");
if(targetGuild != null)
{
builder.append("Guild: ").append(targetGuild.getName()).append("\n");
builder.append("Guild ID: ").append(targetGuild.getID()).append("\n");
}
builder.append("Command: ").append(target.getName()).append("\n");
builder.append("Has Access: ").append(PermissionsHandler.canRunCommand(userToLookup, targetGuild, target) ? "Yes" : "No").append("\n");
builder.append("```");
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), builder.toString());
}
}
/**
* Quick helper function to convert a bool to a String.
* @param input Input boolean, can be `null`
* @return "Grant" => `true`, "Deny" => `false`, "N/A" => `null`
*/
private static String booleanToString(Boolean input)
{
return input != null ? (input ? "Grant" : "Deny") : "N/A";
}
private static final Command setUser =
new Command.Builder("perm:setUser")
.description("Overrides an user's permission to run a command.")
.enabled(true)
.usage("Command <User#Disc|ID> <Guild|ID|this|*> <true|false|null>")
.defaultPermission(DefaultPermission.BOT_OWNER)
.build(PermissionsCommands::setUserImpl);
private static void setUserImpl(MessageReceivedEvent event, List<String> arguments)
{
// Parameters
IUser targetUser;
IGuild targetGuild = null;
Command targetCommand;
Boolean action;
ICommandPermissionsProvider provider = PermissionsHandler.getProvider();
// Argchk size
if(arguments.size() != 3 && arguments.size() != 4)
{
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "Invalid arguments.");
return;
}
// Argchk#1 Command
Optional<Command> uncheckedCommand = CommandRegistry.getCommandOrAliasByName(arguments.get(0));
if(!uncheckedCommand.isPresent()) // Command existence
{
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "Target command not found.");
return;
}
else targetCommand = uncheckedCommand.get();
// Argchk#2 User#Disc/UID
try
{
Optional<IUser> uncheckedUser = ArgumentParser.checkParseUsernameOrID(arguments.get(1), event.getClient());
if(uncheckedUser.isPresent()) targetUser = uncheckedUser.get();
else
{
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "User not found.");
return;
}
}
catch(ArgumentParser.ParserException e)
{
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "Invalid arguments.");
return;
}
// Argchk#3 Guild
if(arguments.get(2).equalsIgnoreCase("this"))
try
{
targetGuild = event.getMessage().getGuild(); // this
}
catch(UnsupportedOperationException ignored) // DM
{
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "`this` is not supported outside a guild.");
return;
}
else if(arguments.get(2).equals("*")) targetGuild = null; // Global - ~NOP
else // Parse Name/ID
{
Optional<IGuild> uncheckedGuild = ArgumentParser.checkParseGuildOrID(arguments.get(2), event.getClient());
if(uncheckedGuild.isPresent()) targetGuild = uncheckedGuild.get();
else
{
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "Guild not found.");
return;
}
}
// Argchk#4 true/false/null
action = (Boolean) ArgumentParser.parse(Boolean.class, arguments.size() > 3 ? arguments.get(3) : null);
// ========================================
StringBuilder builder = new StringBuilder("**Changes summary**\n");
builder.append("```\n");
builder.append("User: ").append(targetUser.getName()).append("#").append(targetUser.getDiscriminator()).append("\n");
builder.append("UID: ").append(targetUser.getID()).append("\n");
builder.append("Guild: ").append(targetGuild != null ? targetGuild.getName() : "[GLOBAL]").append("\n");
builder.append("Guild ID: ").append(targetGuild != null ? targetGuild.getID() : "N/A").append("\n");
builder.append("Command: ").append(targetCommand.getName()).append("\n");
builder.append("New perm: ").append(booleanToString(action)).append("\n");
builder.append("```\n");
provider.setUserPermission(targetGuild, targetUser, targetCommand, action);
builder.append("Changes OK");
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), builder.toString());
}
}

@ -1,82 +0,0 @@
package net.pingex.dcf.permissions;
import net.pingex.dcf.commands.Command;
import sx.blah.discord.handle.obj.IGuild;
import sx.blah.discord.handle.obj.IMessage;
import sx.blah.discord.handle.obj.IRole;
import sx.blah.discord.handle.obj.IUser;
/**
* Landing class of this package.
*/
@Deprecated
public class PermissionsHandler
{
/**
* Get provider used to check permissions.
*/
public static ICommandPermissionsProvider getProvider()
{
return PermissionCheck.getProvider();
}
/**
* Tells whether someone can run requested command.
*
* The validator will successively test the following until something other than `null` is returned.
* - Individual user permission on the guild using {@code ICommandPermissionsProvider.validateUser()}
* - Individual user permission globally using {@code ICommandPermissionsProvider.validateUser()}
* - The user's role list using {@code ICommandPermissionsProvider.validateGroup()}
* - Default behavior included in a Command object
*
* @param targetUser User to check against
* @param targetGuild Guild to check against
* @param command Requested command, as parsed by {@code CommandHandler}
* @return `true` if request is granted, `false` if denied.
*/
public static boolean canRunCommand(IUser targetUser, IGuild targetGuild, Command command)
{
// First check: user permission for guild, skipped if DM
if(targetGuild != null)
{
Boolean canUserGuildRun = getProvider().validateUser(targetGuild, targetUser, command);
if(canUserGuildRun != null) return canUserGuildRun;
}
// Second check: user permission globally
Boolean canUserRun = getProvider().validateUser(null, targetUser, command);
if(canUserRun != null) return canUserRun;
// Third check: user role permissions, skipped if DM
if(targetGuild != null && targetGuild.getRolesForUser(targetUser) != null)
for(IRole i : targetGuild.getRolesForUser(targetUser))
{
Boolean canRoleRun = getProvider().validateGroup(i, command);
if(canRoleRun != null) return canRoleRun;
}
// Fourth check: default behavior
return command.getDefaultPermission().test(new DefaultPermission.Tuple(targetUser, targetGuild));
}
/**
* Shortcut to the other {@code canRunCommand()} method.
* @param request Incoming message
* @param command Command to check against
* @return `true` if request is granted, `false` if denied.
*/
public static boolean canRunCommand(IMessage request, Command command)
{
IUser requestAuthor = request.getAuthor();
IGuild originatedGuild;
try
{
originatedGuild = request.getGuild();
}
catch(UnsupportedOperationException uoe) // DM
{
originatedGuild = null;
}
return canRunCommand(requestAuthor, originatedGuild, command);
}
}