From cbe7771b632967abc85c6fd057f3146b9baa4cfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Mon, 7 Sep 2020 19:57:03 +0200 Subject: [PATCH] Improve permission checks and throw permission exceptions when the caller lacks permissions --- .../commands/CommandTree.java | 40 ++++++++----- .../exceptions/NoPermissionException.java | 58 +++++++++++++++++++ 2 files changed, 84 insertions(+), 14 deletions(-) create mode 100644 commands-core/src/main/java/com/intellectualsites/commands/exceptions/NoPermissionException.java diff --git a/commands-core/src/main/java/com/intellectualsites/commands/CommandTree.java b/commands-core/src/main/java/com/intellectualsites/commands/CommandTree.java index c70aac0b..1589b6d1 100644 --- a/commands-core/src/main/java/com/intellectualsites/commands/CommandTree.java +++ b/commands-core/src/main/java/com/intellectualsites/commands/CommandTree.java @@ -27,6 +27,7 @@ import com.intellectualsites.commands.components.CommandComponent; import com.intellectualsites.commands.components.StaticComponent; import com.intellectualsites.commands.context.CommandContext; import com.intellectualsites.commands.exceptions.InvalidSyntaxException; +import com.intellectualsites.commands.exceptions.NoPermissionException; import com.intellectualsites.commands.exceptions.NoSuchCommandException; import com.intellectualsites.commands.internal.CommandRegistrationHandler; import com.intellectualsites.commands.components.parser.ComponentParseResult; @@ -86,17 +87,22 @@ public class CommandTree { private Optional> parseCommand(@Nonnull final CommandContext commandContext, @Nonnull final Queue commandQueue, @Nonnull final Node> root) { - if (!this.isPermitted(commandContext.getCommandSender(), root)) { - /* TODO: Send not allowed */ - throw new RuntimeException("Nope!"); + String permission; + if ((permission = this.isPermitted(commandContext.getCommandSender(), root)) != null) { + throw new NoPermissionException(permission, commandContext.getCommandSender(), this.getChain(root) + .stream() + .map(Node::getValue) + .collect(Collectors.toList())); } final List>> children = root.getChildren(); if (children.size() == 1 && !(children.get(0).getValue() instanceof StaticComponent)) { // The value has to be a variable final Node> child = children.get(0); - if (!this.isPermitted(commandContext.getCommandSender(), child)) { - /* TODO: Send not allowed */ - throw new RuntimeException("Nope!"); + if ((permission = this.isPermitted(commandContext.getCommandSender(), child)) != null) { + throw new NoPermissionException(permission, commandContext.getCommandSender(), this.getChain(child) + .stream() + .map(Node::getValue) + .collect(Collectors.toList())); } if (child.getValue() != null) { if (commandQueue.isEmpty()) { @@ -204,7 +210,7 @@ public class CommandTree { @Nonnull final Node> root) { /* If the sender isn't allowed to access the root node, no suggestions are needed */ - if (!this.isPermitted(commandContext.getCommandSender(), root)) { + if (this.isPermitted(commandContext.getCommandSender(), root) != null) { return Collections.emptyList(); } final List>> children = root.getChildren(); @@ -254,7 +260,7 @@ public class CommandTree { } final List suggestions = new LinkedList<>(); for (final Node> component : root.getChildren()) { - if (component.getValue() == null || !this.isPermitted(commandContext.getCommandSender(), component)) { + if (component.getValue() == null || this.isPermitted(commandContext.getCommandSender(), component) != null) { continue; } suggestions.addAll( @@ -297,25 +303,31 @@ public class CommandTree { this.verifyAndRegister(); } - private boolean isPermitted(@Nonnull final C sender, @Nonnull final Node> node) { + @Nullable + private String isPermitted(@Nonnull final C sender, @Nonnull final Node> node) { final String permission = node.nodeMeta.get("permission"); if (permission != null) { - return sender.hasPermission(permission); + return sender.hasPermission(permission) ? null : permission; } if (node.isLeaf()) { // noinspection all - return sender.hasPermission(node.value.getOwningCommand().getCommandPermission()); + return sender.hasPermission(node.value.getOwningCommand().getCommandPermission()) + ? null : node.value.getOwningCommand().getCommandPermission(); } /* if any of the children would permit the execution, then the sender has a valid chain to execute, and so we allow them to execute the root */ + final List missingPermissions = new LinkedList<>(); for (final Node> child : node.getChildren()) { - if (this.isPermitted(sender, child)) { - return true; + final String check = this.isPermitted(sender, child); + if (check == null) { + return null; + } else { + missingPermissions.add(check); } } - return false; + return String.join(", ", missingPermissions); } /** diff --git a/commands-core/src/main/java/com/intellectualsites/commands/exceptions/NoPermissionException.java b/commands-core/src/main/java/com/intellectualsites/commands/exceptions/NoPermissionException.java new file mode 100644 index 00000000..f655ad0c --- /dev/null +++ b/commands-core/src/main/java/com/intellectualsites/commands/exceptions/NoPermissionException.java @@ -0,0 +1,58 @@ +// +// MIT License +// +// Copyright (c) 2020 IntellectualSites +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +package com.intellectualsites.commands.exceptions; + +import com.intellectualsites.commands.components.CommandComponent; +import com.intellectualsites.commands.sender.CommandSender; + +import javax.annotation.Nonnull; +import java.util.List; + +/** + * Exception thrown when a {@link CommandSender} misses a permission required + * to execute a {@link com.intellectualsites.commands.Command} + */ +public class NoPermissionException extends CommandParseException { + + private final String missingPermission; + + public NoPermissionException(@Nonnull final String missingPermission, + @Nonnull final CommandSender commandSender, + @Nonnull final List> currentChain) { + super(commandSender, currentChain); + this.missingPermission = missingPermission; + } + + @Override + public String getMessage() { + return String.format("Missing permission '%s'", this.missingPermission); + } + + @Nonnull + public String getMissingPermission() { + return this.missingPermission; + } + + +}