From d8b76f3d0199f4c904c04afc8e5f4459c1b16585 Mon Sep 17 00:00:00 2001 From: Pingex Date: Thu, 12 Jan 2017 23:45:13 +0100 Subject: [PATCH] New permissions API. Deprecated old API until complete removal. --- .../dcf/permissions/DefaultPermission.java | 1 + .../permissions/DefaultPermissionOption.java | 80 +++++++++++++++++ .../dcf/permissions/PermissionCheck.java | 86 +++++++++++++++++++ .../dcf/permissions/PermissionsHandler.java | 11 ++- .../audit/DefaultPermissionCheck.java | 46 ++++++++++ .../permissions/audit/UserGlobalCheck.java | 57 ++++++++++++ .../dcf/permissions/audit/UserGuildCheck.java | 61 +++++++++++++ .../permissions/audit/UserGuildRoleCheck.java | 69 +++++++++++++++ .../dcf/permissions/audit/package-info.java | 4 + 9 files changed, 409 insertions(+), 6 deletions(-) create mode 100644 src/main/java/net/pingex/dcf/permissions/DefaultPermissionOption.java create mode 100644 src/main/java/net/pingex/dcf/permissions/PermissionCheck.java create mode 100644 src/main/java/net/pingex/dcf/permissions/audit/DefaultPermissionCheck.java create mode 100644 src/main/java/net/pingex/dcf/permissions/audit/UserGlobalCheck.java create mode 100644 src/main/java/net/pingex/dcf/permissions/audit/UserGuildCheck.java create mode 100644 src/main/java/net/pingex/dcf/permissions/audit/UserGuildRoleCheck.java create mode 100644 src/main/java/net/pingex/dcf/permissions/audit/package-info.java diff --git a/src/main/java/net/pingex/dcf/permissions/DefaultPermission.java b/src/main/java/net/pingex/dcf/permissions/DefaultPermission.java index 6c44c6b..fafc953 100644 --- a/src/main/java/net/pingex/dcf/permissions/DefaultPermission.java +++ b/src/main/java/net/pingex/dcf/permissions/DefaultPermission.java @@ -8,6 +8,7 @@ import java.util.function.Predicate; /** * Default behavior when a permissions provider doesn't return `true` or `false` */ +@Deprecated public enum DefaultPermission implements Predicate { /** diff --git a/src/main/java/net/pingex/dcf/permissions/DefaultPermissionOption.java b/src/main/java/net/pingex/dcf/permissions/DefaultPermissionOption.java new file mode 100644 index 0000000..939b3cf --- /dev/null +++ b/src/main/java/net/pingex/dcf/permissions/DefaultPermissionOption.java @@ -0,0 +1,80 @@ +package net.pingex.dcf.permissions; + +import net.pingex.dcf.commands.Context; +import net.pingex.dcf.commands.options.ICommandOption; +import net.pingex.dcf.core.Configuration; +import java.util.function.Predicate; + +/** + * This class defines the default behavior of a command when checking for permissions. + */ +public class DefaultPermissionOption implements ICommandOption +{ + private Value defaultValue; + + public DefaultPermissionOption(Value defaultValue) + { + this.defaultValue = defaultValue; + } + + public Value getDefaultValue() + { + return defaultValue; + } + + @Override + public String getOptionName() + { + return "Default permission"; + } + + @Override + public String getOptionDescription() + { + return "This option defines the default behavior of a command when checking for permissions."; + } + + /** + * Default behavior when a permissions provider doesn't return any value + */ + public enum Value implements Predicate + { + /** + * Everyone is allowed to run the command. + */ + EVERYONE(context -> true), + + /** + * Only the guild owner is allowed to run the command. + */ + GUILD_OWNER(context -> !context.getChannel().isPrivate() && context.getUser().getID().equals(context.getChannel().getGuild().getOwnerID())), + + /** + * Only the bot owner is allowed to run the command. + */ + BOT_OWNER(context -> context.getUser().getID().equals(Configuration.BOT_OWNER)), + + /** + * Guild Owner x Bot Owner + */ + ANY_OWNER(context -> GUILD_OWNER.test(context) || BOT_OWNER.test(context)), + + /** + * Nobody is allowed to run the command + */ + NONE(context -> false); + + Value(Predicate predicate) + { + this.predicate = predicate; + } + + private Predicate predicate; + + @Override + public boolean test(Context o) + { + return predicate.test(o); + } + } +} diff --git a/src/main/java/net/pingex/dcf/permissions/PermissionCheck.java b/src/main/java/net/pingex/dcf/permissions/PermissionCheck.java new file mode 100644 index 0000000..b07f3a8 --- /dev/null +++ b/src/main/java/net/pingex/dcf/permissions/PermissionCheck.java @@ -0,0 +1,86 @@ +package net.pingex.dcf.permissions; + +import net.pingex.dcf.commands.Context; +import net.pingex.dcf.commands.audit.AuditResult; +import net.pingex.dcf.commands.audit.IAuditComponentProvider; +import net.pingex.dcf.permissions.audit.DefaultPermissionCheck; +import net.pingex.dcf.permissions.audit.UserGlobalCheck; +import net.pingex.dcf.permissions.audit.UserGuildCheck; +import net.pingex.dcf.permissions.audit.UserGuildRoleCheck; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Set; +import java.util.TreeSet; + +/** + * This class is used to check whether an user can run a command. + */ +public class PermissionCheck implements IAuditComponentProvider +{ + private static final ICommandPermissionsProvider CURRENT_PROVIDER = new DefaultPermissionsProvider(); + + private static final Set SUB_CHECKS = new TreeSet<>(Comparator.comparingInt(IAuditComponentProvider::priority)); + + static + { + SUB_CHECKS.addAll(Arrays.asList( + new DefaultPermissionCheck(), + new UserGlobalCheck(getProvider()), + new UserGuildCheck(getProvider()), + new UserGuildRoleCheck(getProvider()) + )); + } + + /** + * Get provider used to check for permissions. + */ + public static ICommandPermissionsProvider getProvider() + { + return CURRENT_PROVIDER; + } + + @Override + public AuditResult doAudit(Context context) + { + AuditResult.Builder globalResult = new AuditResult.Builder(); + + for(IAuditComponentProvider i : SUB_CHECKS) + { + AuditResult interResult = i.doAudit(context); + globalResult.appendAuditResult(i, interResult); + + // Check opcode has not been written yet and interResult has opcode either FAIL or PASS. + if(globalResult.getOpcode() == null && (interResult.getOpcode() == AuditResult.ResultCode.FAIL || interResult.getOpcode() == AuditResult.ResultCode.PASS)) + { + globalResult.setOpcode(interResult.getOpcode()); + globalResult.setMessage(interResult.getMessage()); + } + } + + if(globalResult.getOpcode() == null) + { + globalResult.setOpcode(globalResult.getSubAuditsResults().get(globalResult.getSubAuditsResults().size()-1).getValue().getOpcode()); + globalResult.setMessage(globalResult.getSubAuditsResults().get(globalResult.getSubAuditsResults().size()-1).getValue().getMessage()); + } + + return globalResult.build(); + } + + @Override + public String name() + { + return "Permission check"; + } + + @Override + public String description() + { + return "Checks whether an user can run a command."; + } + + @Override + public int priority() + { + return 10; + } +} diff --git a/src/main/java/net/pingex/dcf/permissions/PermissionsHandler.java b/src/main/java/net/pingex/dcf/permissions/PermissionsHandler.java index 30762c8..1eaf080 100644 --- a/src/main/java/net/pingex/dcf/permissions/PermissionsHandler.java +++ b/src/main/java/net/pingex/dcf/permissions/PermissionsHandler.java @@ -9,16 +9,15 @@ import sx.blah.discord.handle.obj.IUser; /** * Landing class of this package. */ +@Deprecated public class PermissionsHandler { - private static final ICommandPermissionsProvider CURRENT_PROVIDER = new DefaultPermissionsProvider(); - /** * Get provider used to check permissions. */ public static ICommandPermissionsProvider getProvider() { - return CURRENT_PROVIDER; + return PermissionCheck.getProvider(); } /** @@ -40,19 +39,19 @@ public class PermissionsHandler // First check: user permission for guild, skipped if DM if(targetGuild != null) { - Boolean canUserGuildRun = CURRENT_PROVIDER.validateUser(targetGuild, targetUser, command); + Boolean canUserGuildRun = getProvider().validateUser(targetGuild, targetUser, command); if(canUserGuildRun != null) return canUserGuildRun; } // Second check: user permission globally - Boolean canUserRun = CURRENT_PROVIDER.validateUser(null, targetUser, command); + 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 = CURRENT_PROVIDER.validateGroup(i, command); + Boolean canRoleRun = getProvider().validateGroup(i, command); if(canRoleRun != null) return canRoleRun; } diff --git a/src/main/java/net/pingex/dcf/permissions/audit/DefaultPermissionCheck.java b/src/main/java/net/pingex/dcf/permissions/audit/DefaultPermissionCheck.java new file mode 100644 index 0000000..bcac043 --- /dev/null +++ b/src/main/java/net/pingex/dcf/permissions/audit/DefaultPermissionCheck.java @@ -0,0 +1,46 @@ +package net.pingex.dcf.permissions.audit; + +import net.pingex.dcf.commands.Context; +import net.pingex.dcf.commands.audit.AuditResult; +import net.pingex.dcf.commands.audit.IAuditComponentProvider; +import net.pingex.dcf.commands.options.ICommandOption; +import net.pingex.dcf.permissions.DefaultPermissionOption; +import java.util.Optional; + +/** + * This class checks permission against a default behavior defined in a command. + */ +public class DefaultPermissionCheck implements IAuditComponentProvider +{ + @Override + public AuditResult doAudit(Context context) + { + Optional option = context.getCommand().getOptions().stream().filter(i -> i instanceof DefaultPermissionOption).findFirst(); + + if(!option.isPresent()) + return new AuditResult(AuditResult.ResultCode.NOOP, "No default permission defined."); + + if(((DefaultPermissionOption) option.get()).getDefaultValue().test(context)) + return new AuditResult(AuditResult.ResultCode.PASS); + else + return new AuditResult(AuditResult.ResultCode.FAIL, "Default policy denied access to command."); + } + + @Override + public String name() + { + return "Default permission check"; + } + + @Override + public String description() + { + return "Checks against a default permission option defined in a command."; + } + + @Override + public int priority() + { + return 5; + } +} diff --git a/src/main/java/net/pingex/dcf/permissions/audit/UserGlobalCheck.java b/src/main/java/net/pingex/dcf/permissions/audit/UserGlobalCheck.java new file mode 100644 index 0000000..7e5c217 --- /dev/null +++ b/src/main/java/net/pingex/dcf/permissions/audit/UserGlobalCheck.java @@ -0,0 +1,57 @@ +package net.pingex.dcf.permissions.audit; + +import net.pingex.dcf.commands.Context; +import net.pingex.dcf.commands.audit.AuditResult; +import net.pingex.dcf.commands.audit.IAuditComponentProvider; +import net.pingex.dcf.permissions.ICommandPermissionsProvider; + +/** + * This class checks permission against a rule defined for a specific user globally. + */ +public class UserGlobalCheck implements IAuditComponentProvider +{ + /** + * Permissions provider to use + */ + private ICommandPermissionsProvider provider; + + /** + * Provider for this object to use + * @param provider Permissions provider this object will use + */ + public UserGlobalCheck(ICommandPermissionsProvider provider) + { + this.provider = provider; + } + + @Override + public AuditResult doAudit(Context context) + { + Boolean returnedValue = provider.validateUser(null, context.getUser(), context.getCommand()); + + if(returnedValue == null) // No rule for user + return new AuditResult(AuditResult.ResultCode.NOOP, "No global rule for this user."); + else if(returnedValue) // Rule says yes + return new AuditResult(AuditResult.ResultCode.PASS); + else // Rule says no + return new AuditResult(AuditResult.ResultCode.FAIL, "Global rule denied access for this user."); + } + + @Override + public String name() + { + return "User global check"; + } + + @Override + public String description() + { + return "Checks against a global rule defined for a specific user."; + } + + @Override + public int priority() + { + return 2; + } +} diff --git a/src/main/java/net/pingex/dcf/permissions/audit/UserGuildCheck.java b/src/main/java/net/pingex/dcf/permissions/audit/UserGuildCheck.java new file mode 100644 index 0000000..34c2522 --- /dev/null +++ b/src/main/java/net/pingex/dcf/permissions/audit/UserGuildCheck.java @@ -0,0 +1,61 @@ +package net.pingex.dcf.permissions.audit; + +import net.pingex.dcf.commands.Context; +import net.pingex.dcf.commands.audit.AuditResult; +import net.pingex.dcf.commands.audit.IAuditComponentProvider; +import net.pingex.dcf.permissions.ICommandPermissionsProvider; + +/** + * This class checks permission against a rule defined for a specific user in a specific guild. + */ +public class UserGuildCheck implements IAuditComponentProvider +{ + /** + * Permissions provider to use + */ + private ICommandPermissionsProvider provider; + + /** + * Provider for this object to use + * @param provider Permissions provider this object will use + */ + public UserGuildCheck(ICommandPermissionsProvider provider) + { + this.provider = provider; + } + + @Override + public AuditResult doAudit(Context context) + { + // Check for guild + if(context.getChannel().getGuild() == null) + return new AuditResult(AuditResult.ResultCode.NOOP, "This channel is not part of a guild."); + + Boolean returnedValue = provider.validateUser(context.getChannel().getGuild(), context.getUser(), context.getCommand()); + + if(returnedValue == null) // No rule for user in this guild + return new AuditResult(AuditResult.ResultCode.NOOP, "No guild rule for this user."); + else if(returnedValue) // Rule says yes + return new AuditResult(AuditResult.ResultCode.PASS); + else // Rule says no + return new AuditResult(AuditResult.ResultCode.FAIL, "Guild rule denied access for this user."); + } + + @Override + public String name() + { + return "User vs guild check"; + } + + @Override + public String description() + { + return "Checks against a guild rule defined for an user."; + } + + @Override + public int priority() + { + return 1; + } +} diff --git a/src/main/java/net/pingex/dcf/permissions/audit/UserGuildRoleCheck.java b/src/main/java/net/pingex/dcf/permissions/audit/UserGuildRoleCheck.java new file mode 100644 index 0000000..c9e03dd --- /dev/null +++ b/src/main/java/net/pingex/dcf/permissions/audit/UserGuildRoleCheck.java @@ -0,0 +1,69 @@ +package net.pingex.dcf.permissions.audit; + +import net.pingex.dcf.commands.Context; +import net.pingex.dcf.commands.audit.AuditResult; +import net.pingex.dcf.commands.audit.IAuditComponentProvider; +import net.pingex.dcf.permissions.ICommandPermissionsProvider; +import sx.blah.discord.handle.obj.IRole; + +/** + * This class checks permission against a rule defined for a specific role in a guild. + */ +public class UserGuildRoleCheck implements IAuditComponentProvider +{ + /** + * Permissions provider to use + */ + private ICommandPermissionsProvider provider; + + /** + * Provider for this object to use + * @param provider Permissions provider this object will use + */ + public UserGuildRoleCheck(ICommandPermissionsProvider provider) + { + this.provider = provider; + } + + @Override + public AuditResult doAudit(Context context) + { + // Check for guild + if(context.getChannel().getGuild() == null) + return new AuditResult(AuditResult.ResultCode.NOOP, "This channel is not part of a guild."); + + if(context.getChannel().getGuild().getRolesForUser(context.getUser()) != null) // User has roles + for(IRole i : context.getChannel().getGuild().getRolesForUser(context.getUser())) + { + Boolean returnedQuery = provider.validateGroup(i, context.getCommand()); + if(returnedQuery != null) + { + if(returnedQuery) return new AuditResult(AuditResult.ResultCode.PASS); + else return new AuditResult(AuditResult.ResultCode.FAIL, "Role rule denied access for this user."); + } + } + else // User has no role + return new AuditResult(AuditResult.ResultCode.NOOP, "No role defined for this user."); + + // No rule for any role + return new AuditResult(AuditResult.ResultCode.NOOP, "No rule defined for any role this user has."); + } + + @Override + public String name() + { + return "Guild role check"; + } + + @Override + public String description() + { + return "Checks permission against a rule defined for a specific role in a guild."; + } + + @Override + public int priority() + { + return 3; + } +} diff --git a/src/main/java/net/pingex/dcf/permissions/audit/package-info.java b/src/main/java/net/pingex/dcf/permissions/audit/package-info.java new file mode 100644 index 0000000..b459e53 --- /dev/null +++ b/src/main/java/net/pingex/dcf/permissions/audit/package-info.java @@ -0,0 +1,4 @@ +/** + * This package contains all sub-checks for PermissionCheck. + */ +package net.pingex.dcf.permissions.audit; \ No newline at end of file