diff --git a/cloud-annotations/src/main/java/cloud/commandframework/annotations/Flag.java b/cloud-annotations/src/main/java/cloud/commandframework/annotations/Flag.java index f0db3de1..0c2cc73c 100644 --- a/cloud-annotations/src/main/java/cloud/commandframework/annotations/Flag.java +++ b/cloud-annotations/src/main/java/cloud/commandframework/annotations/Flag.java @@ -88,4 +88,11 @@ public @interface Flag { */ @NonNull String description() default ""; + /** + * The flag permission + * + * @return Flag permission + */ + @NonNull String permission() default ""; + } diff --git a/cloud-annotations/src/main/java/cloud/commandframework/annotations/FlagExtractor.java b/cloud-annotations/src/main/java/cloud/commandframework/annotations/FlagExtractor.java index 93166c1f..64b01dae 100644 --- a/cloud-annotations/src/main/java/cloud/commandframework/annotations/FlagExtractor.java +++ b/cloud-annotations/src/main/java/cloud/commandframework/annotations/FlagExtractor.java @@ -30,6 +30,7 @@ import cloud.commandframework.arguments.flags.CommandFlag; import cloud.commandframework.arguments.parser.ArgumentParser; import cloud.commandframework.arguments.parser.ParserParameters; import cloud.commandframework.arguments.parser.ParserRegistry; +import cloud.commandframework.permission.Permission; import io.leangen.geantyref.TypeToken; import org.checkerframework.checker.nullness.qual.NonNull; @@ -61,7 +62,8 @@ final class FlagExtractor implements Function<@NonNull Method, Collection<@NonNu final CommandFlag.Builder builder = this.commandManager .flagBuilder(flag.value()) .withDescription(ArgumentDescription.of(flag.description())) - .withAliases(flag.aliases()); + .withAliases(flag.aliases()) + .withPermission(Permission.of(flag.permission())); if (parameter.getType().equals(boolean.class)) { flags.add(builder.build()); } else { diff --git a/cloud-core/src/main/java/cloud/commandframework/arguments/compound/FlagArgument.java b/cloud-core/src/main/java/cloud/commandframework/arguments/compound/FlagArgument.java index 2ca52c2a..7ce8b72e 100644 --- a/cloud-core/src/main/java/cloud/commandframework/arguments/compound/FlagArgument.java +++ b/cloud-core/src/main/java/cloud/commandframework/arguments/compound/FlagArgument.java @@ -218,7 +218,7 @@ public final class FlagArgument extends CommandArgument { final List strings = new LinkedList<>(); /* Recommend "primary" flags */ for (final CommandFlag flag : this.flags) { - if (usedFlags.contains(flag)) { + if (usedFlags.contains(flag) || !commandContext.hasPermission(flag.getCommandPermission())) { continue; } strings.add( @@ -231,7 +231,7 @@ public final class FlagArgument extends CommandArgument { /* Recommend aliases */ final boolean suggestCombined = input.length() > 1 && input.charAt(0) == '-' && input.charAt(1) != '-'; for (final CommandFlag flag : this.flags) { - if (usedFlags.contains(flag)) { + if (usedFlags.contains(flag) || !commandContext.hasPermission(flag.getCommandPermission())) { continue; } for (final String alias : flag.getAliases()) { @@ -279,7 +279,9 @@ public final class FlagArgument extends CommandArgument { } } } - if (currentFlag != null && currentFlag.getCommandArgument() != null) { + if (currentFlag != null + && commandContext.hasPermission(currentFlag.getCommandPermission()) + && currentFlag.getCommandArgument() != null) { return (List) ((BiFunction) currentFlag.getCommandArgument().getSuggestionsProvider()) .apply(commandContext, input); } @@ -394,6 +396,12 @@ public final class FlagArgument extends CommandArgument { FailureReason.DUPLICATE_FLAG, commandContext )); + } else if (!commandContext.hasPermission(currentFlag.getCommandPermission())) { + return ArgumentParseResult.failure(new FlagParseException( + string, + FailureReason.NO_PERMISSION, + commandContext + )); } parsedFlags.add(currentFlag); if (currentFlag.getCommandArgument() == null) { @@ -499,7 +507,8 @@ public final class FlagArgument extends CommandArgument { UNKNOWN_FLAG(StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_FLAG_UNKNOWN_FLAG), DUPLICATE_FLAG(StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_FLAG_DUPLICATE_FLAG), NO_FLAG_STARTED(StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_FLAG_NO_FLAG_STARTED), - MISSING_ARGUMENT(StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_FLAG_MISSING_ARGUMENT); + MISSING_ARGUMENT(StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_FLAG_MISSING_ARGUMENT), + NO_PERMISSION(StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_FLAG_NO_PERMISSION); private final Caption caption; diff --git a/cloud-core/src/main/java/cloud/commandframework/arguments/flags/CommandFlag.java b/cloud-core/src/main/java/cloud/commandframework/arguments/flags/CommandFlag.java index 393e6d3b..73258429 100644 --- a/cloud-core/src/main/java/cloud/commandframework/arguments/flags/CommandFlag.java +++ b/cloud-core/src/main/java/cloud/commandframework/arguments/flags/CommandFlag.java @@ -25,6 +25,8 @@ package cloud.commandframework.arguments.flags; import cloud.commandframework.ArgumentDescription; import cloud.commandframework.arguments.CommandArgument; +import cloud.commandframework.permission.CommandPermission; +import cloud.commandframework.permission.Permission; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -47,6 +49,7 @@ public final class CommandFlag { private final @NonNull String name; private final @NonNull String @NonNull [] aliases; private final @NonNull ArgumentDescription description; + private final @NonNull CommandPermission permission; private final @Nullable CommandArgument commandArgument; @@ -54,11 +57,13 @@ public final class CommandFlag { final @NonNull String name, final @NonNull String @NonNull [] aliases, final @NonNull ArgumentDescription description, + final @NonNull CommandPermission permission, final @Nullable CommandArgument commandArgument ) { this.name = Objects.requireNonNull(name, "name cannot be null"); this.aliases = Objects.requireNonNull(aliases, "aliases cannot be null"); this.description = Objects.requireNonNull(description, "description cannot be null"); + this.permission = Objects.requireNonNull(permission, "permission cannot be null"); this.commandArgument = commandArgument; } @@ -125,6 +130,15 @@ public final class CommandFlag { return this.commandArgument; } + /** + * Get the command permission, if it exists + * + * @return Command permission, or {@code null} + */ + public CommandPermission getCommandPermission() { + return this.permission; + } + @Override public String toString() { return String.format("--%s", this.name); @@ -153,22 +167,25 @@ public final class CommandFlag { private final String name; private final String[] aliases; private final ArgumentDescription description; + private final CommandPermission permission; private final CommandArgument commandArgument; private Builder( final @NonNull String name, final @NonNull String[] aliases, final @NonNull ArgumentDescription description, + final @NonNull CommandPermission permission, final @Nullable CommandArgument commandArgument ) { this.name = name; this.aliases = aliases; this.description = description; + this.permission = permission; this.commandArgument = commandArgument; } private Builder(final @NonNull String name) { - this(name, new String[0], ArgumentDescription.empty(), null); + this(name, new String[0], ArgumentDescription.empty(), Permission.empty(), null); } /** @@ -198,6 +215,7 @@ public final class CommandFlag { this.name, filteredAliases.toArray(new String[0]), this.description, + this.permission, this.commandArgument ); } @@ -214,7 +232,7 @@ public final class CommandFlag { return this.withDescription((ArgumentDescription) description); } - /**d + /** * Create a new builder instance using the given flag description * * @param description Flag description @@ -222,7 +240,7 @@ public final class CommandFlag { * @since 1.4.0 */ public Builder withDescription(final @NonNull ArgumentDescription description) { - return new Builder<>(this.name, this.aliases, description, this.commandArgument); + return new Builder<>(this.name, this.aliases, description, this.permission, this.commandArgument); } /** @@ -233,7 +251,7 @@ public final class CommandFlag { * @return New builder instance */ public Builder withArgument(final @NonNull CommandArgument argument) { - return new Builder<>(this.name, this.aliases, this.description, argument); + return new Builder<>(this.name, this.aliases, this.description, this.permission, argument); } /** @@ -247,13 +265,24 @@ public final class CommandFlag { return this.withArgument(builder.build()); } + /** + * Create a new builder instance using the given flag permission + * + * @param permission Flag permission + * @return New builder instance + * @since 1.6.0 + */ + public Builder withPermission(final @NonNull CommandPermission permission) { + return new Builder<>(this.name, this.aliases, this.description, permission, this.commandArgument); + } + /** * Build a new command flag instance * * @return Constructed instance */ public @NonNull CommandFlag build() { - return new CommandFlag<>(this.name, this.aliases, this.description, this.commandArgument); + return new CommandFlag<>(this.name, this.aliases, this.description, this.permission, this.commandArgument); } } diff --git a/cloud-core/src/main/java/cloud/commandframework/captions/SimpleCaptionRegistry.java b/cloud-core/src/main/java/cloud/commandframework/captions/SimpleCaptionRegistry.java index a328c723..634facd5 100644 --- a/cloud-core/src/main/java/cloud/commandframework/captions/SimpleCaptionRegistry.java +++ b/cloud-core/src/main/java/cloud/commandframework/captions/SimpleCaptionRegistry.java @@ -80,6 +80,10 @@ public class SimpleCaptionRegistry implements FactoryDelegatingCaptionRegistr * Default caption for {@link StandardCaptionKeys#ARGUMENT_PARSE_FAILURE_FLAG_MISSING_ARGUMENT} */ public static final String ARGUMENT_PARSE_FAILURE_FLAG_MISSING_ARGUMENT = "Missing argument for '{flag}'"; + /** + * Default caption for {@link StandardCaptionKeys#ARGUMENT_PARSE_FAILURE_FLAG_NO_PERMISSION} + */ + public static final String ARGUMENT_PARSE_FAILURE_FLAG_NO_PERMISSION = "You don't have permission to use '{flag}'"; /** * Default caption for {@link StandardCaptionKeys#ARGUMENT_PARSE_FAILURE_COLOR} */ @@ -132,6 +136,10 @@ public class SimpleCaptionRegistry implements FactoryDelegatingCaptionRegistr StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_FLAG_MISSING_ARGUMENT, (caption, sender) -> ARGUMENT_PARSE_FAILURE_FLAG_MISSING_ARGUMENT ); + this.registerMessageFactory( + StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_FLAG_NO_PERMISSION, + (caption, sender) -> ARGUMENT_PARSE_FAILURE_FLAG_NO_PERMISSION + ); this.registerMessageFactory( StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_COLOR, (caption, sender) -> ARGUMENT_PARSE_FAILURE_COLOR diff --git a/cloud-core/src/main/java/cloud/commandframework/captions/StandardCaptionKeys.java b/cloud-core/src/main/java/cloud/commandframework/captions/StandardCaptionKeys.java index 96060db4..a9584cf2 100644 --- a/cloud-core/src/main/java/cloud/commandframework/captions/StandardCaptionKeys.java +++ b/cloud-core/src/main/java/cloud/commandframework/captions/StandardCaptionKeys.java @@ -84,6 +84,10 @@ public final class StandardCaptionKeys { * Variables: {flag} */ public static final Caption ARGUMENT_PARSE_FAILURE_FLAG_MISSING_ARGUMENT = of("argument.parse.failure.flag.missing_argument"); + /** + * Variables: {flag} + */ + public static final Caption ARGUMENT_PARSE_FAILURE_FLAG_NO_PERMISSION = of("argument.parse.failure.flag.no_permission"); /** * Variables: {input} */ diff --git a/cloud-core/src/main/java/cloud/commandframework/context/CommandContext.java b/cloud-core/src/main/java/cloud/commandframework/context/CommandContext.java index 2ebeed1c..cbc70e6d 100644 --- a/cloud-core/src/main/java/cloud/commandframework/context/CommandContext.java +++ b/cloud-core/src/main/java/cloud/commandframework/context/CommandContext.java @@ -35,6 +35,7 @@ import cloud.commandframework.captions.SimpleCaptionVariableReplacementHandler; import cloud.commandframework.keys.CloudKey; import cloud.commandframework.keys.CloudKeyHolder; import cloud.commandframework.keys.SimpleCloudKey; +import cloud.commandframework.permission.CommandPermission; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -152,6 +153,27 @@ public final class CommandContext { return this.commandSender; } + /** + * Check whether the sender that executed the command has a permission. + * + * @param permission The permission + * @return Command sender + */ + public boolean hasPermission(final @NonNull CommandPermission permission) { + return this.commandManager.hasPermission(this.commandSender, permission); + } + + /** + * Check whether the sender that executed the command has a permission. + * + * @param permission The permission + * @return Command sender + */ + public boolean hasPermission(final @NonNull String permission) { + return this.commandManager.hasPermission(this.commandSender, permission); + } + + /** * Check if this context was created for tab completion purposes *