Improve permission checks and throw permission exceptions when the caller lacks permissions

This commit is contained in:
Alexander Söderberg 2020-09-07 19:57:03 +02:00
parent a8d3fa7c5e
commit cbe7771b63
No known key found for this signature in database
GPG key ID: C0207FF7EA146678
2 changed files with 84 additions and 14 deletions

View file

@ -27,6 +27,7 @@ import com.intellectualsites.commands.components.CommandComponent;
import com.intellectualsites.commands.components.StaticComponent; import com.intellectualsites.commands.components.StaticComponent;
import com.intellectualsites.commands.context.CommandContext; import com.intellectualsites.commands.context.CommandContext;
import com.intellectualsites.commands.exceptions.InvalidSyntaxException; import com.intellectualsites.commands.exceptions.InvalidSyntaxException;
import com.intellectualsites.commands.exceptions.NoPermissionException;
import com.intellectualsites.commands.exceptions.NoSuchCommandException; import com.intellectualsites.commands.exceptions.NoSuchCommandException;
import com.intellectualsites.commands.internal.CommandRegistrationHandler; import com.intellectualsites.commands.internal.CommandRegistrationHandler;
import com.intellectualsites.commands.components.parser.ComponentParseResult; import com.intellectualsites.commands.components.parser.ComponentParseResult;
@ -86,17 +87,22 @@ public class CommandTree<C extends CommandSender> {
private Optional<Command<C>> parseCommand(@Nonnull final CommandContext<C> commandContext, private Optional<Command<C>> parseCommand(@Nonnull final CommandContext<C> commandContext,
@Nonnull final Queue<String> commandQueue, @Nonnull final Queue<String> commandQueue,
@Nonnull final Node<CommandComponent<C, ?>> root) { @Nonnull final Node<CommandComponent<C, ?>> root) {
if (!this.isPermitted(commandContext.getCommandSender(), root)) { String permission;
/* TODO: Send not allowed */ if ((permission = this.isPermitted(commandContext.getCommandSender(), root)) != null) {
throw new RuntimeException("Nope!"); throw new NoPermissionException(permission, commandContext.getCommandSender(), this.getChain(root)
.stream()
.map(Node::getValue)
.collect(Collectors.toList()));
} }
final List<Node<CommandComponent<C, ?>>> children = root.getChildren(); final List<Node<CommandComponent<C, ?>>> children = root.getChildren();
if (children.size() == 1 && !(children.get(0).getValue() instanceof StaticComponent)) { if (children.size() == 1 && !(children.get(0).getValue() instanceof StaticComponent)) {
// The value has to be a variable // The value has to be a variable
final Node<CommandComponent<C, ?>> child = children.get(0); final Node<CommandComponent<C, ?>> child = children.get(0);
if (!this.isPermitted(commandContext.getCommandSender(), child)) { if ((permission = this.isPermitted(commandContext.getCommandSender(), child)) != null) {
/* TODO: Send not allowed */ throw new NoPermissionException(permission, commandContext.getCommandSender(), this.getChain(child)
throw new RuntimeException("Nope!"); .stream()
.map(Node::getValue)
.collect(Collectors.toList()));
} }
if (child.getValue() != null) { if (child.getValue() != null) {
if (commandQueue.isEmpty()) { if (commandQueue.isEmpty()) {
@ -204,7 +210,7 @@ public class CommandTree<C extends CommandSender> {
@Nonnull final Node<CommandComponent<C, ?>> root) { @Nonnull final Node<CommandComponent<C, ?>> root) {
/* If the sender isn't allowed to access the root node, no suggestions are needed */ /* 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(); return Collections.emptyList();
} }
final List<Node<CommandComponent<C, ?>>> children = root.getChildren(); final List<Node<CommandComponent<C, ?>>> children = root.getChildren();
@ -254,7 +260,7 @@ public class CommandTree<C extends CommandSender> {
} }
final List<String> suggestions = new LinkedList<>(); final List<String> suggestions = new LinkedList<>();
for (final Node<CommandComponent<C, ?>> component : root.getChildren()) { for (final Node<CommandComponent<C, ?>> component : root.getChildren()) {
if (component.getValue() == null || !this.isPermitted(commandContext.getCommandSender(), component)) { if (component.getValue() == null || this.isPermitted(commandContext.getCommandSender(), component) != null) {
continue; continue;
} }
suggestions.addAll( suggestions.addAll(
@ -297,25 +303,31 @@ public class CommandTree<C extends CommandSender> {
this.verifyAndRegister(); this.verifyAndRegister();
} }
private boolean isPermitted(@Nonnull final C sender, @Nonnull final Node<CommandComponent<C, ?>> node) { @Nullable
private String isPermitted(@Nonnull final C sender, @Nonnull final Node<CommandComponent<C, ?>> node) {
final String permission = node.nodeMeta.get("permission"); final String permission = node.nodeMeta.get("permission");
if (permission != null) { if (permission != null) {
return sender.hasPermission(permission); return sender.hasPermission(permission) ? null : permission;
} }
if (node.isLeaf()) { if (node.isLeaf()) {
// noinspection all // 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 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 chain to execute, and so we allow them to execute the root
*/ */
final List<String> missingPermissions = new LinkedList<>();
for (final Node<CommandComponent<C, ?>> child : node.getChildren()) { for (final Node<CommandComponent<C, ?>> child : node.getChildren()) {
if (this.isPermitted(sender, child)) { final String check = this.isPermitted(sender, child);
return true; if (check == null) {
return null;
} else {
missingPermissions.add(check);
} }
} }
return false; return String.join(", ", missingPermissions);
} }
/** /**

View file

@ -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<CommandComponent<?, ?>> 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;
}
}