diff --git a/api/src/main/java/com/velocitypowered/api/command/Command.java b/api/src/main/java/com/velocitypowered/api/command/Command.java index d1cfd9f49..ecbbcb379 100644 --- a/api/src/main/java/com/velocitypowered/api/command/Command.java +++ b/api/src/main/java/com/velocitypowered/api/command/Command.java @@ -26,4 +26,19 @@ public interface Command { default List suggest(@NonNull CommandSource source, @NonNull String[] currentArgs) { return ImmutableList.of(); } + + /** + * Tests to check if the {@code source} has permission to use this command + * with the provided {@code args}. + * + *

If this method returns false, the handling will be forwarded onto + * the players current server.

+ * + * @param source the source of the command + * @param args the arguments for this command + * @return whether the source has permission + */ + default boolean hasPermission(@NonNull CommandSource source, @NonNull String[] args) { + return true; + } } diff --git a/api/src/main/java/com/velocitypowered/api/permission/PermissionFunction.java b/api/src/main/java/com/velocitypowered/api/permission/PermissionFunction.java index 102e36eba..f9b70e366 100644 --- a/api/src/main/java/com/velocitypowered/api/permission/PermissionFunction.java +++ b/api/src/main/java/com/velocitypowered/api/permission/PermissionFunction.java @@ -29,5 +29,5 @@ public interface PermissionFunction { * @param permission the permission * @return the value the permission is set to */ - @NonNull Tristate getPermissionSetting(@NonNull String permission); + @NonNull Tristate getPermissionValue(@NonNull String permission); } diff --git a/api/src/main/java/com/velocitypowered/api/permission/PermissionSubject.java b/api/src/main/java/com/velocitypowered/api/permission/PermissionSubject.java index 8377f0729..67bb18a10 100644 --- a/api/src/main/java/com/velocitypowered/api/permission/PermissionSubject.java +++ b/api/src/main/java/com/velocitypowered/api/permission/PermissionSubject.java @@ -12,5 +12,15 @@ public interface PermissionSubject { * @param permission the permission to check for * @return whether or not the subject has the permission */ - boolean hasPermission(@NonNull String permission); + default boolean hasPermission(@NonNull String permission) { + return getPermissionValue(permission).asBoolean(); + } + + /** + * Gets the subjects setting for a particular permission. + * + * @param permission the permission + * @return the value the permission is set to + */ + @NonNull Tristate getPermissionValue(@NonNull String permission); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index d4d67a6ad..2cd0df2d9 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -8,6 +8,7 @@ import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.event.EventManager; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; +import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.api.plugin.PluginManager; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; @@ -40,6 +41,7 @@ import net.kyori.text.serializer.ComponentSerializers; import net.kyori.text.serializer.GsonComponentSerializer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.checkerframework.checker.nullness.qual.NonNull; import java.net.InetSocketAddress; import java.nio.file.Files; @@ -78,8 +80,8 @@ public class VelocityServer implements ProxyServer { } @Override - public boolean hasPermission(String permission) { - return true; + public @NonNull Tristate getPermissionValue(@NonNull String permission) { + return Tristate.TRUE; } }; private Ratelimiter ipAttemptLimiter; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/ServerCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/ServerCommand.java index 2926bb038..b466ae5f7 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/ServerCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/ServerCommand.java @@ -3,6 +3,7 @@ package com.velocitypowered.proxy.command; import com.google.common.collect.ImmutableList; import com.velocitypowered.api.command.Command; import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ServerConnection; @@ -12,6 +13,7 @@ import net.kyori.text.TextComponent; import net.kyori.text.event.ClickEvent; import net.kyori.text.event.HoverEvent; import net.kyori.text.format.TextColor; +import org.checkerframework.checker.nullness.qual.NonNull; import java.util.List; import java.util.Optional; @@ -88,4 +90,9 @@ public class ServerCommand implements Command { return ImmutableList.of(); } } + + @Override + public boolean hasPermission(@NonNull CommandSource source, @NonNull String[] args) { + return source.getPermissionValue("velocity.command.server") != Tristate.FALSE; + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/ShutdownCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/ShutdownCommand.java index 7f60c81ff..210a9a026 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/ShutdownCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/ShutdownCommand.java @@ -5,6 +5,7 @@ import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.proxy.VelocityServer; import net.kyori.text.TextComponent; import net.kyori.text.format.TextColor; +import org.checkerframework.checker.nullness.qual.NonNull; public class ShutdownCommand implements Command { private final VelocityServer server; @@ -21,4 +22,9 @@ public class ShutdownCommand implements Command { } server.shutdown(); } + + @Override + public boolean hasPermission(@NonNull CommandSource source, @NonNull String[] args) { + return source == server.getConsoleCommandSource(); + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommand.java index f3acb9caf..c8b72dbcd 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommand.java @@ -2,10 +2,12 @@ package com.velocitypowered.proxy.command; import com.velocitypowered.api.command.Command; import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.proxy.VelocityServer; import net.kyori.text.TextComponent; import net.kyori.text.event.ClickEvent; import net.kyori.text.format.TextColor; +import org.checkerframework.checker.nullness.qual.NonNull; public class VelocityCommand implements Command { @Override @@ -37,4 +39,9 @@ public class VelocityCommand implements Command { source.sendMessage(velocityInfo); source.sendMessage(velocityWebsite); } + + @Override + public boolean hasPermission(@NonNull CommandSource source, @NonNull String[] args) { + return source.getPermissionValue("velocity.command.info") != Tristate.FALSE; + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java index a6ec96c69..1b952e1dd 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java @@ -46,6 +46,10 @@ public class VelocityCommandManager implements CommandManager { } try { + if (!command.hasPermission(source, actualArgs)) { + return false; + } + command.execute(source, actualArgs); return true; } catch (Exception e) { @@ -62,24 +66,29 @@ public class VelocityCommandManager implements CommandManager { return Optional.empty(); } - String command = split[0]; + String alias = split[0]; if (split.length == 1) { - return Optional.of(commands.keySet().stream() - .filter(cmd -> cmd.regionMatches(true, 0, command, 0, command.length())) - .map(cmd -> "/" + cmd) + return Optional.of(commands.entrySet().stream() + .filter(ent -> ent.getKey().regionMatches(true, 0, alias, 0, alias.length())) + .filter(ent -> ent.getValue().hasPermission(source, new String[0])) + .map(ent -> "/" + ent.getKey()) .collect(Collectors.toList())); } String[] actualArgs = Arrays.copyOfRange(split, 1, split.length); - Command executor = commands.get(command); - if (executor == null) { + Command command = commands.get(alias.toLowerCase(Locale.ENGLISH)); + if (command == null) { return Optional.empty(); } try { - return Optional.of(executor.suggest(source, actualArgs)); + if (!command.hasPermission(source, actualArgs)) { + return Optional.empty(); + } + + return Optional.of(command.suggest(source, actualArgs)); } catch (Exception e) { - throw new RuntimeException("Unable to invoke suggestions for command " + command + " for " + source, e); + throw new RuntimeException("Unable to invoke suggestions for command " + alias + " for " + source, e); } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java index 9cbe478aa..f38586b94 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java @@ -7,6 +7,7 @@ import com.velocitypowered.api.event.player.PlayerSettingsChangedEvent; import com.velocitypowered.api.event.player.ServerPreConnectEvent; import com.velocitypowered.api.permission.PermissionFunction; import com.velocitypowered.api.permission.PermissionProvider; +import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.api.proxy.ConnectionRequestBuilder; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ServerConnection; @@ -390,8 +391,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { } @Override - public boolean hasPermission(String permission) { - return permissionFunction.getPermissionSetting(permission).asBoolean(); + public @NonNull Tristate getPermissionValue(@NonNull String permission) { + return permissionFunction.getPermissionValue(permission); } @Override