From 77950cb6e8be0ce567755973a008d19906d99c6e Mon Sep 17 00:00:00 2001 From: NovaFox161 Date: Mon, 13 Apr 2020 23:23:27 -0500 Subject: [PATCH] Fix permissions checking for correct permissions This makes sure that users with admin permissions can run commands like `!event create` without needing the bot's control role as admins have full access to everything on the server. This commit also wraps the command registration in a fromRunnable call so that it doesn't block the client login --- .../discal/client/DisCalClient.java | 45 +++++-------- .../module/command/AnnouncementCommand.java | 12 ++-- .../module/command/CalendarCommand.java | 19 +++--- .../client/module/command/DisCalCommand.java | 7 +- .../client/module/command/EventCommand.java | 20 +++--- .../discal/core/utils/PermissionChecker.java | 66 +++++++++++++++---- 6 files changed, 97 insertions(+), 72 deletions(-) diff --git a/client/src/main/java/org/dreamexposure/discal/client/DisCalClient.java b/client/src/main/java/org/dreamexposure/discal/client/DisCalClient.java index ae58210c..1a612b0b 100644 --- a/client/src/main/java/org/dreamexposure/discal/client/DisCalClient.java +++ b/client/src/main/java/org/dreamexposure/discal/client/DisCalClient.java @@ -49,12 +49,13 @@ import discord4j.store.jdk.JdkStoreService; import discord4j.store.redis.RedisStoreService; import io.lettuce.core.RedisClient; import io.lettuce.core.RedisURI; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; @SpringBootApplication(exclude = SessionAutoConfiguration.class) public class DisCalClient { private static GatewayDiscordClient client; - @SuppressWarnings("CallingSubscribeInNonBlockingScope") public static void main(String[] args) throws IOException { //Get settings Properties p = new Properties(); @@ -109,17 +110,6 @@ public class DisCalClient { DisCalClient.client = client; //Register listeners - client.on(ReadyEvent.class) - .flatMap(ReadyEventListener::handle) - .subscribe(); - client.on(TextChannelDeleteEvent.class) - .flatMap(ChannelDeleteListener::handle) - .subscribe(); - client.on(RoleDeleteEvent.class) - .flatMap(RoleDeleteListener::handle) - .subscribe(); - - /* Mono onReady = client.on(ReadyEvent.class) .flatMap(ReadyEventListener::handle) .then(); @@ -131,24 +121,25 @@ public class DisCalClient { Mono onRoleDelete = client.on(RoleDeleteEvent.class) .flatMap(RoleDeleteListener::handle) .then(); - */ //Register commands - CommandExecutor executor = CommandExecutor.getExecutor().enable(); - executor.registerCommand(new HelpCommand()); - executor.registerCommand(new DisCalCommand()); - executor.registerCommand(new CalendarCommand()); - executor.registerCommand(new AddCalendarCommand()); - executor.registerCommand(new TimeCommand()); - executor.registerCommand(new LinkCalendarCommand()); - executor.registerCommand(new EventListCommand()); - executor.registerCommand(new EventCommand()); - executor.registerCommand(new RsvpCommand()); - executor.registerCommand(new AnnouncementCommand()); - executor.registerCommand(new DevCommand()); + Mono commands = Mono.fromRunnable(() -> { + CommandExecutor executor = CommandExecutor.getExecutor().enable(); + executor.registerCommand(new HelpCommand()); + executor.registerCommand(new DisCalCommand()); + executor.registerCommand(new CalendarCommand()); + executor.registerCommand(new AddCalendarCommand()); + executor.registerCommand(new TimeCommand()); + executor.registerCommand(new LinkCalendarCommand()); + executor.registerCommand(new EventListCommand()); + executor.registerCommand(new EventCommand()); + executor.registerCommand(new RsvpCommand()); + executor.registerCommand(new AnnouncementCommand()); + executor.registerCommand(new DevCommand()); + }).subscribeOn(Schedulers.boundedElastic()) + .then(); - //return Mono.when(onReady, onTextChannelDelete, onRoleDelete); - return client.onDisconnect(); + return Mono.when(onReady, onTextChannelDelete, onRoleDelete, commands); }).block(); } diff --git a/client/src/main/java/org/dreamexposure/discal/client/module/command/AnnouncementCommand.java b/client/src/main/java/org/dreamexposure/discal/client/module/command/AnnouncementCommand.java index 6624c10e..2ed67b83 100644 --- a/client/src/main/java/org/dreamexposure/discal/client/module/command/AnnouncementCommand.java +++ b/client/src/main/java/org/dreamexposure/discal/client/module/command/AnnouncementCommand.java @@ -125,25 +125,25 @@ public class AnnouncementCommand implements ICommand { } else { switch (args[0].toLowerCase()) { case "create": - if (PermissionChecker.hasSufficientRole(event, settings).blockOptional().orElse(false)) + if (PermissionChecker.hasDisCalRole(event, settings).block()) moduleCreate(event, settings); else MessageManager.sendMessageAsync(MessageManager.getMessage("Notification.Perm.CONTROL_ROLE", settings), event); break; case "confirm": - if (PermissionChecker.hasSufficientRole(event, settings).blockOptional().orElse(false)) + if (PermissionChecker.hasDisCalRole(event, settings).block()) moduleConfirm(event, settings); else MessageManager.sendMessageAsync(MessageManager.getMessage("Notification.Perm.CONTROL_ROLE", settings), event); break; case "cancel": - if (PermissionChecker.hasSufficientRole(event, settings).blockOptional().orElse(false)) + if (PermissionChecker.hasDisCalRole(event, settings).block()) moduleCancel(event, settings); else MessageManager.sendMessageAsync(MessageManager.getMessage("Notification.Perm.CONTROL_ROLE", settings), event); break; case "delete": - if (PermissionChecker.hasSufficientRole(event, settings).blockOptional().orElse(false)) + if (PermissionChecker.hasDisCalRole(event, settings).block()) moduleDelete(args, event, settings); else MessageManager.sendMessageAsync(MessageManager.getMessage("Notification.Perm.CONTROL_ROLE", settings), event); @@ -194,13 +194,13 @@ public class AnnouncementCommand implements ICommand { moduleColor(args, event, settings); break; case "copy": - if (PermissionChecker.hasSufficientRole(event, settings).blockOptional().orElse(false)) + if (PermissionChecker.hasDisCalRole(event, settings).block()) moduleCopy(args, event, settings); else MessageManager.sendMessageAsync(MessageManager.getMessage("Notification.Perm.CONTROL_ROLE", settings), event); break; case "edit": - if (PermissionChecker.hasSufficientRole(event, settings).blockOptional().orElse(false)) + if (PermissionChecker.hasDisCalRole(event, settings).block()) moduleEdit(args, event, settings); else MessageManager.sendMessageAsync(MessageManager.getMessage("Notification.Perm.CONTROL_ROLE", settings), event); diff --git a/client/src/main/java/org/dreamexposure/discal/client/module/command/CalendarCommand.java b/client/src/main/java/org/dreamexposure/discal/client/module/command/CalendarCommand.java index 9aeba578..2bc3f57e 100644 --- a/client/src/main/java/org/dreamexposure/discal/client/module/command/CalendarCommand.java +++ b/client/src/main/java/org/dreamexposure/discal/client/module/command/CalendarCommand.java @@ -99,13 +99,13 @@ public class CalendarCommand implements ICommand { switch (args[0].toLowerCase()) { case "create": - if (PermissionChecker.hasSufficientRole(event, settings).blockOptional().orElse(false)) + if (PermissionChecker.hasDisCalRole(event, settings).block()) moduleCreate(args, event, calendarData, settings); else MessageManager.sendMessageAsync(MessageManager.getMessage("Notification.Perm.CONTROL_ROLE", settings), event); break; case "cancel": - if (PermissionChecker.hasSufficientRole(event, settings).blockOptional().orElse(false)) + if (PermissionChecker.hasDisCalRole(event, settings).block()) moduleCancel(event, calendarData, settings); else MessageManager.sendMessageAsync(MessageManager.getMessage("Notification.Perm.CONTROL_ROLE", settings), event); @@ -115,14 +115,14 @@ public class CalendarCommand implements ICommand { moduleView(event, calendarData, settings); break; case "confirm": - if (PermissionChecker.hasSufficientRole(event, settings).blockOptional().orElse(false)) + if (PermissionChecker.hasDisCalRole(event, settings).block()) moduleConfirm(event, calendarData, settings); else MessageManager.sendMessageAsync(MessageManager.getMessage("Notification.Perm.CONTROL_ROLE", settings), event); break; case "delete": case "remove": - if (PermissionChecker.hasSufficientRole(event, settings).blockOptional().orElse(false)) + if (PermissionChecker.hasDisCalRole(event, settings).block()) moduleDelete(event, calendarData, settings); else MessageManager.sendMessageAsync(MessageManager.getMessage("Notification.Perm.CONTROL_ROLE", settings), event); @@ -138,7 +138,7 @@ public class CalendarCommand implements ICommand { moduleTimezone(args, event, calendarData, settings); break; case "edit": - if (PermissionChecker.hasSufficientRole(event, settings).blockOptional().orElse(false)) + if (PermissionChecker.hasDisCalRole(event, settings).block()) moduleEdit(event, calendarData, settings); else MessageManager.sendMessageAsync(MessageManager.getMessage("Notification.Perm.CONTROL_ROLE", settings), event); @@ -184,12 +184,9 @@ public class CalendarCommand implements ICommand { boolean editing = CalendarCreator.getCreator().getPreCalendar(settings.getGuildID()).isEditing(); if (CalendarCreator.getCreator().terminate(settings.getGuildID())) { if (message != null) { - if (!editing) { - MessageManager.deleteMessage(event); - MessageManager.deleteMessage(CalendarCreator.getCreator().getCreatorMessage(settings.getGuildID())); - } else { - MessageManager.deleteMessage(event); - MessageManager.deleteMessage(CalendarCreator.getCreator().getCreatorMessage(settings.getGuildID())); + MessageManager.deleteMessage(event); + MessageManager.deleteMessage(CalendarCreator.getCreator().getCreatorMessage(settings.getGuildID())); + if (editing) { CalendarCreator.getCreator().setCreatorMessage(MessageManager.sendMessageSync(MessageManager.getMessage("Creator.Calendar.Cancel.Edit.Success", settings), event)); } } else { diff --git a/client/src/main/java/org/dreamexposure/discal/client/module/command/DisCalCommand.java b/client/src/main/java/org/dreamexposure/discal/client/module/command/DisCalCommand.java index b9f56d80..cb7d20de 100644 --- a/client/src/main/java/org/dreamexposure/discal/client/module/command/DisCalCommand.java +++ b/client/src/main/java/org/dreamexposure/discal/client/module/command/DisCalCommand.java @@ -1,6 +1,5 @@ package org.dreamexposure.discal.client.module.command; -import org.dreamexposure.discal.client.DisCalClient; import org.dreamexposure.discal.client.message.MessageManager; import org.dreamexposure.discal.core.database.DatabaseManager; import org.dreamexposure.discal.core.object.BotSettings; @@ -152,7 +151,7 @@ public class DisCalCommand implements ICommand { spec.addField(MessageManager.getMessage("Embed.Discal.Info.Version", settings), GlobalConst.version, true); spec.addField(MessageManager.getMessage("Embed.DisCal.Info.Library", settings), "Discord4J, version 3.0.6", false); spec.addField("Shard Index", BotSettings.SHARD_INDEX.get() + "/" + BotSettings.SHARD_COUNT.get(), true); - spec.addField(MessageManager.getMessage("Embed.DisCal.Info.TotalGuilds", settings), DisCalClient.getClient().getGuilds().count().block() + "", true); + spec.addField(MessageManager.getMessage("Embed.DisCal.Info.TotalGuilds", settings), event.getClient().getGuilds().count().block() + "", true); spec.addField(MessageManager.getMessage("Embed.DisCal.Info.TotalCalendars", settings), DatabaseManager.getCalendarCount().block() + "", true); spec.addField(MessageManager.getMessage("Embed.DisCal.Info.TotalAnnouncements", settings), DatabaseManager.getAnnouncementCount().block() + "", true); spec.setFooter(MessageManager.getMessage("Embed.DisCal.Info.Patron", settings) + ": https://www.patreon.com/Novafox", null); @@ -169,7 +168,7 @@ public class DisCalCommand implements ICommand { * @param event The event received. */ private void moduleControlRole(String[] args, MessageCreateEvent event, GuildSettings settings) { - if (PermissionChecker.hasSufficientRole(event, settings).blockOptional().orElse(false)) { + if (PermissionChecker.hasDisCalRole(event, settings).block()) { if (args.length > 1) { String roleName = GeneralUtils.getContent(args, 1); Role controlRole; @@ -342,7 +341,7 @@ public class DisCalCommand implements ICommand { } private void moduleBrand(MessageCreateEvent event, GuildSettings settings) { - if (PermissionChecker.hasSufficientRole(event, settings).blockOptional().orElse(false)) { + if (PermissionChecker.hasDisCalRole(event, settings).block()) { if (settings.isPatronGuild()) { settings.setBranded(!settings.isBranded()); DatabaseManager.updateSettings(settings).subscribe(); diff --git a/client/src/main/java/org/dreamexposure/discal/client/module/command/EventCommand.java b/client/src/main/java/org/dreamexposure/discal/client/module/command/EventCommand.java index 220f4b54..deb799e1 100644 --- a/client/src/main/java/org/dreamexposure/discal/client/module/command/EventCommand.java +++ b/client/src/main/java/org/dreamexposure/discal/client/module/command/EventCommand.java @@ -127,37 +127,37 @@ public class EventCommand implements ICommand { } else { switch (args[0].toLowerCase()) { case "create": - if (PermissionChecker.hasSufficientRole(event, settings).blockOptional().orElse(false)) + if (PermissionChecker.hasDisCalRole(event, settings).block()) moduleCreate(args, event, calendarData, settings); else MessageManager.sendMessageAsync(MessageManager.getMessage("Notification.Perm.CONTROL_ROLE", settings), event); break; case "copy": - if (PermissionChecker.hasSufficientRole(event, settings).blockOptional().orElse(false)) + if (PermissionChecker.hasDisCalRole(event, settings).block()) moduleCopy(args, event, calendarData, settings); else MessageManager.sendMessageAsync(MessageManager.getMessage("Notification.Perm.CONTROL_ROLE", settings), event); break; case "edit": - if (PermissionChecker.hasSufficientRole(event, settings).blockOptional().orElse(false)) + if (PermissionChecker.hasDisCalRole(event, settings).block()) moduleEdit(args, event, calendarData, settings); else MessageManager.sendMessageAsync(MessageManager.getMessage("Notification.Perm.CONTROL_ROLE", settings), event); break; case "restart": - if (PermissionChecker.hasSufficientRole(event, settings).blockOptional().orElse(false)) + if (PermissionChecker.hasDisCalRole(event, settings).block()) moduleRestart(args, event, calendarData, settings); else MessageManager.sendMessageAsync(MessageManager.getMessage("Notification.Perm.CONTROL_ROLE", settings), event); break; case "cancel": - if (PermissionChecker.hasSufficientRole(event, settings).blockOptional().orElse(false)) + if (PermissionChecker.hasDisCalRole(event, settings).block()) moduleCancel(event, settings); else MessageManager.sendMessageAsync(MessageManager.getMessage("Notification.Perm.CONTROL_ROLE", settings), event); break; case "delete": - if (PermissionChecker.hasSufficientRole(event, settings).blockOptional().orElse(false)) + if (PermissionChecker.hasDisCalRole(event, settings).block()) moduleDelete(args, event, calendarData, settings); else MessageManager.sendMessageAsync(MessageManager.getMessage("Notification.Perm.CONTROL_ROLE", settings), event); @@ -167,7 +167,7 @@ public class EventCommand implements ICommand { moduleView(args, event, calendarData, settings); break; case "confirm": - if (PermissionChecker.hasSufficientRole(event, settings).blockOptional().orElse(false)) + if (PermissionChecker.hasDisCalRole(event, settings).block()) moduleConfirm(event, calendarData, settings); else MessageManager.sendMessageAsync(MessageManager.getMessage("Notification.Perm.CONTROL_ROLE", settings), event); @@ -324,10 +324,8 @@ public class EventCommand implements ICommand { if (msg != null) { MessageManager.deleteMessage(event); MessageManager.deleteMessage(msg); - MessageManager.sendMessageAsync(MessageManager.getMessage("Creator.Event.Cancel.Success", settings), event); - } else { - MessageManager.sendMessageAsync(MessageManager.getMessage("Creator.Event.Cancel.Success", settings), event); } + MessageManager.sendMessageAsync(MessageManager.getMessage("Creator.Event.Cancel.Success", settings), event); } else { MessageManager.sendMessageAsync(MessageManager.getMessage("Creator.Event.NotInit", settings), event); } @@ -876,7 +874,7 @@ public class EventCommand implements ICommand { return; } try { - boolean value = Boolean.valueOf(valueString); + boolean value = Boolean.parseBoolean(valueString); EventCreator.getCreator().getPreEvent(settings.getGuildID()).setShouldRecur(value); if (value) { if (EventCreator.getCreator().hasCreatorMessage(settings.getGuildID())) { diff --git a/core/src/main/java/org/dreamexposure/discal/core/utils/PermissionChecker.java b/core/src/main/java/org/dreamexposure/discal/core/utils/PermissionChecker.java index ff25fa23..02dd1a4f 100644 --- a/core/src/main/java/org/dreamexposure/discal/core/utils/PermissionChecker.java +++ b/core/src/main/java/org/dreamexposure/discal/core/utils/PermissionChecker.java @@ -26,26 +26,70 @@ import reactor.core.publisher.Mono; * For Project: DisCal */ public class PermissionChecker { - /** - * Checks if the user who sent the received message has the proper role to use a command. - * - * @param event The Event received to check for the user and guild. - * @return true if the user has the proper role, otherwise false. - */ + public static Mono hasDisCalRole(MessageCreateEvent event, GuildSettings settings) { + if (settings.getControlRole().equalsIgnoreCase("everyone")) + return Mono.just(true); + if (Snowflake.of(settings.getControlRole()).equals(settings.getGuildID())) //also everyone + return Mono.just(true); + + Mono member = Mono.justOrEmpty(event.getMember()); + + //User doesn't need bot control role if they have admin permissions. + return member.flatMap(Member::getBasePermissions) + .map(perms -> + perms.contains(Permission.ADMINISTRATOR) + || perms.contains(Permission.MANAGE_GUILD) + ).flatMap(hasAdmin -> { + if (hasAdmin) { + return Mono.just(true); + } else { + return member.flatMapMany(Member::getRoles) + .map(Role::getId) + .any(id -> id.equals(Snowflake.of(settings.getControlRole()))); + } + }); + } + + public static Mono hasDisCalRole(Member member, GuildSettings settings) { + if (settings.getControlRole().equalsIgnoreCase("everyone")) + return Mono.just(true); + if (Snowflake.of(settings.getControlRole()).equals(settings.getGuildID())) //also everyone + return Mono.just(true); + + //User doesn't need bot control role if they have admin permissions. + Mono hasAdmin = Mono.just(member).flatMap(Member::getBasePermissions).map(perms -> + perms.contains(Permission.ADMINISTRATOR) || perms.contains(Permission.MANAGE_GUILD)); + + return hasAdmin.flatMap(has -> { + if (has) { + return Mono.just(true); + } else { + return Mono.just(member).flatMapMany(Member::getRoles) + .map(Role::getId) + .any(id -> id.equals(Snowflake.of(settings.getControlRole()))); + } + }); + } + + @Deprecated public static Mono hasSufficientRole(MessageCreateEvent event, GuildSettings settings) { if (settings.getControlRole().equalsIgnoreCase("everyone")) return Mono.just(true); + if (Snowflake.of(settings.getControlRole()).equals(settings.getGuildID())) //also everyone + return Mono.just(true); return Mono.justOrEmpty(event.getMember()) .flatMapMany(Member::getRoles) .map(Role::getId) .any(snowflake -> snowflake.equals(Snowflake.of(settings.getControlRole()))); - } + @Deprecated public static Mono hasSufficientRole(Member member, GuildSettings settings) { if (settings.getControlRole().equalsIgnoreCase("everyone")) return Mono.just(true); + if (Snowflake.of(settings.getControlRole()).equals(settings.getGuildID())) //also everyone + return Mono.just(true); return Mono.from(member.getRoles() .map(Role::getId) @@ -56,6 +100,8 @@ public class PermissionChecker { public static Mono hasSufficientRole(RestMember member, GuildSettings settings) { if (settings.getControlRole().equalsIgnoreCase("everyone")) return Mono.just(true); + if (Snowflake.of(settings.getControlRole()).equals(settings.getGuildID())) //also everyone + return Mono.just(true); return member.getData() .map(MemberData::roles) @@ -96,12 +142,6 @@ public class PermissionChecker { ); } - /** - * Checks if the user sent the command in a DisCal channel (if set). - * - * @param event The event received to check for the correct channel. - * @return true if in correct channel, otherwise false. - */ public static Mono isCorrectChannel(MessageCreateEvent event, GuildSettings settings) { if (settings.getDiscalChannel().equalsIgnoreCase("all")) return Mono.just(true);