1
0
mirror of https://github.com/lucko/LuckPerms.git synced 2025-08-31 10:01:45 +02:00

Add configurable read-only mode for commands (#4031)

This commit is contained in:
lucko
2025-05-10 14:32:47 +01:00
committed by GitHub
parent f64bf04ba4
commit 94e7e11183
24 changed files with 644 additions and 142 deletions

View File

@@ -719,3 +719,33 @@ register-command-list-data: true
# If LuckPerms should attempt to resolve Vanilla command target selectors for LP commands.
# See here for more info: https://minecraft.wiki/w/Target_selectors
resolve-command-selectors: false
# If the plugin should run in "read-only" mode for commands.
#
# In this mode, players or the console will only be able to execute commands that read or view
# data, and will not be able to execute any LP commands that modify data.
#
# If enabling read-only mode for just players, be aware that some other plugins may allow players
# to execute commands as the console, allowing them to run LP commands indirectly.
#
# Note: This does not affect interactions with LuckPerms via the API.
commands-read-only-mode:
players: false
console: false
# If LuckPerms commands should be disabled. When true, this will prevent all LP commands from being
# executed. In a sense this is a more extreme version of read-only mode (see above).
#
# LuckPerms will still act as the permission manager, but server administrators will be unable to
# make changes via commands.
#
# If disabling commands just for players, be aware that some other plugins may allow players to
# execute commands as the console, allowing them to run commands indirectly.
#
# If commands are disabled for both players and the console, LuckPerms will not attempt to register
# a command with the server at all & in a sense will be invisible to both players and admins.
#
# Note: This does not affect interactions with LuckPerms via the API.
disable-luckperms-commands:
players: false
console: false

View File

@@ -611,3 +611,33 @@ disable-bulkupdate: false
#
# - When this happens, the plugin will set their primary group back to default.
prevent-primary-group-removal: false
# If the plugin should run in "read-only" mode for commands.
#
# In this mode, players or the console will only be able to execute commands that read or view
# data, and will not be able to execute any LP commands that modify data.
#
# If enabling read-only mode for just players, be aware that some other plugins may allow players
# to execute commands as the console, allowing them to run LP commands indirectly.
#
# Note: This does not affect interactions with LuckPerms via the API.
commands-read-only-mode:
players: false
console: false
# If LuckPerms commands should be disabled. When true, this will prevent all LP commands from being
# executed. In a sense this is a more extreme version of read-only mode (see above).
#
# LuckPerms will still act as the permission manager, but server administrators will be unable to
# make changes via commands.
#
# If disabling commands just for players, be aware that some other plugins may allow players to
# execute commands as the console, allowing them to run commands indirectly.
#
# If commands are disabled for both players and the console, LuckPerms will not attempt to register
# a command with the server at all & in a sense will be invisible to both players and admins.
#
# Note: This does not affect interactions with LuckPerms via the API.
disable-luckperms-commands:
players: false
console: false

View File

@@ -160,6 +160,15 @@ public class CommandManager {
return CompletableFuture.completedFuture(null);
}
boolean commandsDisabled = sender.isConsole()
? this.plugin.getConfiguration().get(ConfigKeys.DISABLE_LUCKPERMS_COMMANDS_CONSOLE)
: this.plugin.getConfiguration().get(ConfigKeys.DISABLE_LUCKPERMS_COMMANDS_PLAYERS);
if (commandsDisabled) {
Message.COMMANDS_DISABLED.send(sender);
return CompletableFuture.completedFuture(null);
}
SchedulerAdapter scheduler = this.plugin.getBootstrap().getScheduler();
List<String> argsCopy = new ArrayList<>(args);
@@ -286,6 +295,14 @@ public class CommandManager {
}
public List<String> tabCompleteCommand(Sender sender, List<String> arguments) {
boolean commandsDisabled = sender.isConsole()
? this.plugin.getConfiguration().get(ConfigKeys.DISABLE_LUCKPERMS_COMMANDS_CONSOLE)
: this.plugin.getConfiguration().get(ConfigKeys.DISABLE_LUCKPERMS_COMMANDS_PLAYERS);
if (commandsDisabled) {
return Collections.emptyList();
}
applyConvenienceAliases(arguments, false);
final List<Command<?>> mains = this.mainCommands.values().stream()

View File

@@ -32,143 +32,143 @@ import me.lucko.luckperms.common.sender.Sender;
*/
public enum CommandPermission {
SYNC("sync", Type.NONE),
INFO("info", Type.NONE),
EDITOR("editor", Type.NONE),
VERBOSE("verbose", Type.NONE),
VERBOSE_COMMAND_OTHERS("verbose.command.others", Type.NONE),
TREE("tree", Type.NONE),
SEARCH("search", Type.NONE),
IMPORT("import", Type.NONE),
EXPORT("export", Type.NONE),
RELOAD_CONFIG("reloadconfig", Type.NONE),
BULK_UPDATE("bulkupdate", Type.NONE),
APPLY_EDITS("applyedits", Type.NONE),
TRUST_EDITOR("trusteditor", Type.NONE),
TRANSLATIONS("translations", Type.NONE),
SYNC("sync", Type.NONE, true),
INFO("info", Type.NONE, true),
EDITOR("editor", Type.NONE, true),
VERBOSE("verbose", Type.NONE, true),
VERBOSE_COMMAND_OTHERS("verbose.command.others", Type.NONE, false),
TREE("tree", Type.NONE, true),
SEARCH("search", Type.NONE, true),
IMPORT("import", Type.NONE, false),
EXPORT("export", Type.NONE, true),
RELOAD_CONFIG("reloadconfig", Type.NONE, true),
BULK_UPDATE("bulkupdate", Type.NONE, false),
APPLY_EDITS("applyedits", Type.NONE, false),
TRUST_EDITOR("trusteditor", Type.NONE, false),
TRANSLATIONS("translations", Type.NONE, true),
CREATE_GROUP("creategroup", Type.NONE),
DELETE_GROUP("deletegroup", Type.NONE),
LIST_GROUPS("listgroups", Type.NONE),
CREATE_GROUP("creategroup", Type.NONE, false),
DELETE_GROUP("deletegroup", Type.NONE, false),
LIST_GROUPS("listgroups", Type.NONE, true),
CREATE_TRACK("createtrack", Type.NONE),
DELETE_TRACK("deletetrack", Type.NONE),
LIST_TRACKS("listtracks", Type.NONE),
CREATE_TRACK("createtrack", Type.NONE, false),
DELETE_TRACK("deletetrack", Type.NONE, false),
LIST_TRACKS("listtracks", Type.NONE, true),
USER_INFO("info", Type.USER),
USER_PERM_INFO("permission.info", Type.USER),
USER_PERM_SET("permission.set", Type.USER),
USER_PERM_UNSET("permission.unset", Type.USER),
USER_PERM_SET_TEMP("permission.settemp", Type.USER),
USER_PERM_UNSET_TEMP("permission.unsettemp", Type.USER),
USER_PERM_CHECK("permission.check", Type.USER),
USER_PERM_CLEAR("permission.clear", Type.USER),
USER_PARENT_INFO("parent.info", Type.USER),
USER_PARENT_SET("parent.set", Type.USER),
USER_PARENT_SET_TRACK("parent.settrack", Type.USER),
USER_PARENT_ADD("parent.add", Type.USER),
USER_PARENT_REMOVE("parent.remove", Type.USER),
USER_PARENT_ADD_TEMP("parent.addtemp", Type.USER),
USER_PARENT_REMOVE_TEMP("parent.removetemp", Type.USER),
USER_PARENT_CLEAR("parent.clear", Type.USER),
USER_PARENT_CLEAR_TRACK("parent.cleartrack", Type.USER),
USER_PARENT_SWITCHPRIMARYGROUP("parent.switchprimarygroup", Type.USER),
USER_META_INFO("meta.info", Type.USER),
USER_META_SET("meta.set", Type.USER),
USER_META_UNSET("meta.unset", Type.USER),
USER_META_SET_TEMP("meta.settemp", Type.USER),
USER_META_UNSET_TEMP("meta.unsettemp", Type.USER),
USER_META_ADD_PREFIX("meta.addprefix", Type.USER),
USER_META_ADD_SUFFIX("meta.addsuffix", Type.USER),
USER_META_SET_PREFIX("meta.setprefix", Type.USER),
USER_META_SET_SUFFIX("meta.setsuffix", Type.USER),
USER_META_REMOVE_PREFIX("meta.removeprefix", Type.USER),
USER_META_REMOVE_SUFFIX("meta.removesuffix", Type.USER),
USER_META_ADD_TEMP_PREFIX("meta.addtempprefix", Type.USER),
USER_META_ADD_TEMP_SUFFIX("meta.addtempsuffix", Type.USER),
USER_META_SET_TEMP_PREFIX("meta.settempprefix", Type.USER),
USER_META_SET_TEMP_SUFFIX("meta.settempsuffix", Type.USER),
USER_META_REMOVE_TEMP_PREFIX("meta.removetempprefix", Type.USER),
USER_META_REMOVE_TEMP_SUFFIX("meta.removetempsuffix", Type.USER),
USER_META_CLEAR("meta.clear", Type.USER),
USER_EDITOR("editor", Type.USER),
USER_SHOW_TRACKS("showtracks", Type.USER),
USER_PROMOTE("promote", Type.USER),
USER_DEMOTE("demote", Type.USER),
USER_CLEAR("clear", Type.USER),
USER_CLONE("clone", Type.USER),
USER_INFO("info", Type.USER, true),
USER_PERM_INFO("permission.info", Type.USER, true),
USER_PERM_SET("permission.set", Type.USER, false),
USER_PERM_UNSET("permission.unset", Type.USER, false),
USER_PERM_SET_TEMP("permission.settemp", Type.USER, false),
USER_PERM_UNSET_TEMP("permission.unsettemp", Type.USER, false),
USER_PERM_CHECK("permission.check", Type.USER, true),
USER_PERM_CLEAR("permission.clear", Type.USER, false),
USER_PARENT_INFO("parent.info", Type.USER, true),
USER_PARENT_SET("parent.set", Type.USER, false),
USER_PARENT_SET_TRACK("parent.settrack", Type.USER, false),
USER_PARENT_ADD("parent.add", Type.USER, false),
USER_PARENT_REMOVE("parent.remove", Type.USER, false),
USER_PARENT_ADD_TEMP("parent.addtemp", Type.USER, false),
USER_PARENT_REMOVE_TEMP("parent.removetemp", Type.USER, false),
USER_PARENT_CLEAR("parent.clear", Type.USER, false),
USER_PARENT_CLEAR_TRACK("parent.cleartrack", Type.USER, false),
USER_PARENT_SWITCHPRIMARYGROUP("parent.switchprimarygroup", Type.USER, false),
USER_META_INFO("meta.info", Type.USER, true),
USER_META_SET("meta.set", Type.USER, false),
USER_META_UNSET("meta.unset", Type.USER, false),
USER_META_SET_TEMP("meta.settemp", Type.USER, false),
USER_META_UNSET_TEMP("meta.unsettemp", Type.USER, false),
USER_META_ADD_PREFIX("meta.addprefix", Type.USER, false),
USER_META_ADD_SUFFIX("meta.addsuffix", Type.USER, false),
USER_META_SET_PREFIX("meta.setprefix", Type.USER, false),
USER_META_SET_SUFFIX("meta.setsuffix", Type.USER, false),
USER_META_REMOVE_PREFIX("meta.removeprefix", Type.USER, false),
USER_META_REMOVE_SUFFIX("meta.removesuffix", Type.USER, false),
USER_META_ADD_TEMP_PREFIX("meta.addtempprefix", Type.USER, false),
USER_META_ADD_TEMP_SUFFIX("meta.addtempsuffix", Type.USER, false),
USER_META_SET_TEMP_PREFIX("meta.settempprefix", Type.USER, false),
USER_META_SET_TEMP_SUFFIX("meta.settempsuffix", Type.USER, false),
USER_META_REMOVE_TEMP_PREFIX("meta.removetempprefix", Type.USER, false),
USER_META_REMOVE_TEMP_SUFFIX("meta.removetempsuffix", Type.USER, false),
USER_META_CLEAR("meta.clear", Type.USER, false),
USER_EDITOR("editor", Type.USER, true),
USER_SHOW_TRACKS("showtracks", Type.USER, true),
USER_PROMOTE("promote", Type.USER, false),
USER_DEMOTE("demote", Type.USER, false),
USER_CLEAR("clear", Type.USER, false),
USER_CLONE("clone", Type.USER, false),
GROUP_INFO("info", Type.GROUP),
GROUP_PERM_INFO("permission.info", Type.GROUP),
GROUP_PERM_SET("permission.set", Type.GROUP),
GROUP_PERM_UNSET("permission.unset", Type.GROUP),
GROUP_PERM_SET_TEMP("permission.settemp", Type.GROUP),
GROUP_PERM_UNSET_TEMP("permission.unsettemp", Type.GROUP),
GROUP_PERM_CHECK("permission.check", Type.GROUP),
GROUP_PERM_CLEAR("permission.clear", Type.GROUP),
GROUP_PARENT_INFO("parent.info", Type.GROUP),
GROUP_PARENT_SET("parent.set", Type.GROUP),
GROUP_PARENT_SET_TRACK("parent.settrack", Type.GROUP),
GROUP_PARENT_ADD("parent.add", Type.GROUP),
GROUP_PARENT_REMOVE("parent.remove", Type.GROUP),
GROUP_PARENT_ADD_TEMP("parent.addtemp", Type.GROUP),
GROUP_PARENT_REMOVE_TEMP("parent.removetemp", Type.GROUP),
GROUP_PARENT_CLEAR("parent.clear", Type.GROUP),
GROUP_PARENT_CLEAR_TRACK("parent.cleartrack", Type.GROUP),
GROUP_META_INFO("meta.info", Type.GROUP),
GROUP_META_SET("meta.set", Type.GROUP),
GROUP_META_UNSET("meta.unset", Type.GROUP),
GROUP_META_SET_TEMP("meta.settemp", Type.GROUP),
GROUP_META_UNSET_TEMP("meta.unsettemp", Type.GROUP),
GROUP_META_ADD_PREFIX("meta.addprefix", Type.GROUP),
GROUP_META_ADD_SUFFIX("meta.addsuffix", Type.GROUP),
GROUP_META_SET_PREFIX("meta.setprefix", Type.GROUP),
GROUP_META_SET_SUFFIX("meta.setsuffix", Type.GROUP),
GROUP_META_REMOVE_PREFIX("meta.removeprefix", Type.GROUP),
GROUP_META_REMOVE_SUFFIX("meta.removesuffix", Type.GROUP),
GROUP_META_ADD_TEMP_PREFIX("meta.addtempprefix", Type.GROUP),
GROUP_META_ADD_TEMP_SUFFIX("meta.addtempsuffix", Type.GROUP),
GROUP_META_SET_TEMP_PREFIX("meta.settempprefix", Type.GROUP),
GROUP_META_SET_TEMP_SUFFIX("meta.settempsuffix", Type.GROUP),
GROUP_META_REMOVE_TEMP_PREFIX("meta.removetempprefix", Type.GROUP),
GROUP_META_REMOVE_TEMP_SUFFIX("meta.removetempsuffix", Type.GROUP),
GROUP_META_CLEAR("meta.clear", Type.GROUP),
GROUP_EDITOR("editor", Type.GROUP),
GROUP_LIST_MEMBERS("listmembers", Type.GROUP),
GROUP_SHOW_TRACKS("showtracks", Type.GROUP),
GROUP_SET_WEIGHT("setweight", Type.GROUP),
GROUP_SET_DISPLAY_NAME("setdisplayname", Type.GROUP),
GROUP_CLEAR("clear", Type.GROUP),
GROUP_RENAME("rename", Type.GROUP),
GROUP_CLONE("clone", Type.GROUP),
GROUP_INFO("info", Type.GROUP, true),
GROUP_PERM_INFO("permission.info", Type.GROUP, true),
GROUP_PERM_SET("permission.set", Type.GROUP, false),
GROUP_PERM_UNSET("permission.unset", Type.GROUP, false),
GROUP_PERM_SET_TEMP("permission.settemp", Type.GROUP, false),
GROUP_PERM_UNSET_TEMP("permission.unsettemp", Type.GROUP, false),
GROUP_PERM_CHECK("permission.check", Type.GROUP, true),
GROUP_PERM_CLEAR("permission.clear", Type.GROUP, false),
GROUP_PARENT_INFO("parent.info", Type.GROUP, true),
GROUP_PARENT_SET("parent.set", Type.GROUP, false),
GROUP_PARENT_SET_TRACK("parent.settrack", Type.GROUP, false),
GROUP_PARENT_ADD("parent.add", Type.GROUP, false),
GROUP_PARENT_REMOVE("parent.remove", Type.GROUP, false),
GROUP_PARENT_ADD_TEMP("parent.addtemp", Type.GROUP, false),
GROUP_PARENT_REMOVE_TEMP("parent.removetemp", Type.GROUP, false),
GROUP_PARENT_CLEAR("parent.clear", Type.GROUP, false),
GROUP_PARENT_CLEAR_TRACK("parent.cleartrack", Type.GROUP, false),
GROUP_META_INFO("meta.info", Type.GROUP, true),
GROUP_META_SET("meta.set", Type.GROUP, false),
GROUP_META_UNSET("meta.unset", Type.GROUP, false),
GROUP_META_SET_TEMP("meta.settemp", Type.GROUP, false),
GROUP_META_UNSET_TEMP("meta.unsettemp", Type.GROUP, false),
GROUP_META_ADD_PREFIX("meta.addprefix", Type.GROUP, false),
GROUP_META_ADD_SUFFIX("meta.addsuffix", Type.GROUP, false),
GROUP_META_SET_PREFIX("meta.setprefix", Type.GROUP, false),
GROUP_META_SET_SUFFIX("meta.setsuffix", Type.GROUP, false),
GROUP_META_REMOVE_PREFIX("meta.removeprefix", Type.GROUP, false),
GROUP_META_REMOVE_SUFFIX("meta.removesuffix", Type.GROUP, false),
GROUP_META_ADD_TEMP_PREFIX("meta.addtempprefix", Type.GROUP, false),
GROUP_META_ADD_TEMP_SUFFIX("meta.addtempsuffix", Type.GROUP, false),
GROUP_META_SET_TEMP_PREFIX("meta.settempprefix", Type.GROUP, false),
GROUP_META_SET_TEMP_SUFFIX("meta.settempsuffix", Type.GROUP, false),
GROUP_META_REMOVE_TEMP_PREFIX("meta.removetempprefix", Type.GROUP, false),
GROUP_META_REMOVE_TEMP_SUFFIX("meta.removetempsuffix", Type.GROUP, false),
GROUP_META_CLEAR("meta.clear", Type.GROUP, false),
GROUP_EDITOR("editor", Type.GROUP, true),
GROUP_LIST_MEMBERS("listmembers", Type.GROUP, true),
GROUP_SHOW_TRACKS("showtracks", Type.GROUP, true),
GROUP_SET_WEIGHT("setweight", Type.GROUP, false),
GROUP_SET_DISPLAY_NAME("setdisplayname", Type.GROUP, false),
GROUP_CLEAR("clear", Type.GROUP, false),
GROUP_RENAME("rename", Type.GROUP, false),
GROUP_CLONE("clone", Type.GROUP, false),
TRACK_INFO("info", Type.TRACK),
TRACK_EDITOR("editor", Type.TRACK),
TRACK_APPEND("append", Type.TRACK),
TRACK_INSERT("insert", Type.TRACK),
TRACK_REMOVE("remove", Type.TRACK),
TRACK_CLEAR("clear", Type.TRACK),
TRACK_RENAME("rename", Type.TRACK),
TRACK_CLONE("clone", Type.TRACK),
TRACK_INFO("info", Type.TRACK, true),
TRACK_EDITOR("editor", Type.TRACK, true),
TRACK_APPEND("append", Type.TRACK, false),
TRACK_INSERT("insert", Type.TRACK, false),
TRACK_REMOVE("remove", Type.TRACK, false),
TRACK_CLEAR("clear", Type.TRACK, false),
TRACK_RENAME("rename", Type.TRACK, false),
TRACK_CLONE("clone", Type.TRACK, false),
LOG_RECENT("recent", Type.LOG),
LOG_USER_HISTORY("userhistory", Type.LOG),
LOG_GROUP_HISTORY("grouphistory", Type.LOG),
LOG_TRACK_HISTORY("trackhistory", Type.LOG),
LOG_SEARCH("search", Type.LOG),
LOG_NOTIFY("notify", Type.LOG),
LOG_RECENT("recent", Type.LOG, true),
LOG_USER_HISTORY("userhistory", Type.LOG, true),
LOG_GROUP_HISTORY("grouphistory", Type.LOG, true),
LOG_TRACK_HISTORY("trackhistory", Type.LOG, true),
LOG_SEARCH("search", Type.LOG, true),
LOG_NOTIFY("notify", Type.LOG, true),
SPONGE_PERMISSION_INFO("permission.info", Type.SPONGE),
SPONGE_PERMISSION_SET("permission.set", Type.SPONGE),
SPONGE_PERMISSION_CLEAR("permission.clear", Type.SPONGE),
SPONGE_PARENT_INFO("parent.info", Type.SPONGE),
SPONGE_PARENT_ADD("parent.add", Type.SPONGE),
SPONGE_PARENT_REMOVE("parent.remove", Type.SPONGE),
SPONGE_PARENT_CLEAR("parent.clear", Type.SPONGE),
SPONGE_OPTION_INFO("option.info", Type.SPONGE),
SPONGE_OPTION_SET("option.set", Type.SPONGE),
SPONGE_OPTION_UNSET("option.unset", Type.SPONGE),
SPONGE_OPTION_CLEAR("option.clear", Type.SPONGE);
SPONGE_PERMISSION_INFO("permission.info", Type.SPONGE, true),
SPONGE_PERMISSION_SET("permission.set", Type.SPONGE, false),
SPONGE_PERMISSION_CLEAR("permission.clear", Type.SPONGE, false),
SPONGE_PARENT_INFO("parent.info", Type.SPONGE, true),
SPONGE_PARENT_ADD("parent.add", Type.SPONGE, false),
SPONGE_PARENT_REMOVE("parent.remove", Type.SPONGE, false),
SPONGE_PARENT_CLEAR("parent.clear", Type.SPONGE, false),
SPONGE_OPTION_INFO("option.info", Type.SPONGE, true),
SPONGE_OPTION_SET("option.set", Type.SPONGE, false),
SPONGE_OPTION_UNSET("option.unset", Type.SPONGE, false),
SPONGE_OPTION_CLEAR("option.clear", Type.SPONGE, false);
public static final String ROOT = "luckperms.";
@@ -176,9 +176,11 @@ public enum CommandPermission {
private final String permission;
private final Type type;
private final boolean readOnly;
CommandPermission(String node, Type type) {
CommandPermission(String node, Type type, boolean readOnly) {
this.type = type;
this.readOnly = readOnly;
if (type == Type.NONE) {
this.node = node;
@@ -205,6 +207,10 @@ public enum CommandPermission {
return this.type;
}
public boolean isReadOnly() {
return this.readOnly;
}
public enum Type {
NONE(null),

View File

@@ -246,6 +246,26 @@ public final class ConfigKeys {
*/
public static final ConfigKey<Boolean> REQUIRE_SENDER_GROUP_MEMBERSHIP_TO_MODIFY = booleanKey("require-sender-group-membership-to-modify", false);
/**
* If the plugin is in read-only mode for players
*/
public static final ConfigKey<Boolean> READ_ONLY_MODE_PLAYERS = booleanKey("commands-read-only-mode.players", false);
/**
* If the plugin is in read-only mode for the server console
*/
public static final ConfigKey<Boolean> READ_ONLY_MODE_CONSOLE = booleanKey("commands-read-only-mode.console", false);
/**
* If LuckPerms commands are disabled for players
*/
public static final ConfigKey<Boolean> DISABLE_LUCKPERMS_COMMANDS_PLAYERS = booleanKey("disable-luckperms-commands.players", false);
/**
* If LuckPerms commands are disabled for the server console
*/
public static final ConfigKey<Boolean> DISABLE_LUCKPERMS_COMMANDS_CONSOLE = booleanKey("disable-luckperms-commands.console", false);
/**
* If wildcards are being applied
*/

View File

@@ -179,6 +179,13 @@ public interface Message {
.color(GRAY)
);
Args0 COMMANDS_DISABLED = () -> prefixed(translatable()
// "&3LuckPerms commands are disabled."
.key("luckperms.commandsystem.commands-disabled")
.color(DARK_AQUA)
.append(FULL_STOP)
);
Args2<String, String> FIRST_TIME_SETUP = (label, username) -> joinNewline(
// "&3It seems that no permissions have been setup yet!"
// "&3Before you can use any of the LuckPerms commands in-game, you need to use the console to give yourself access."

View File

@@ -208,7 +208,11 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin {
this.syncTaskBuffer = new SyncTask.Buffer(this);
// register commands
registerCommands();
if (skipCommandRegistration()) {
getLogger().warn("LuckPerms commands are disabled in the configuration for both console and players. Skipping command registration.");
} else {
registerCommands();
}
// load internal managers
getLogger().info("Loading internal permission managers...");
@@ -388,6 +392,11 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin {
return configFile;
}
protected boolean skipCommandRegistration() {
return getConfiguration().get(ConfigKeys.DISABLE_LUCKPERMS_COMMANDS_CONSOLE) &&
getConfiguration().get(ConfigKeys.DISABLE_LUCKPERMS_COMMANDS_PLAYERS);
}
@Override
public PluginLogger getLogger() {
return getBootstrap().getPluginLogger();

View File

@@ -26,6 +26,9 @@
package me.lucko.luckperms.common.sender;
import com.google.common.collect.Iterables;
import me.lucko.luckperms.common.command.access.CommandPermission;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.config.generic.key.ConfigKey;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
@@ -98,6 +101,19 @@ public final class AbstractSender<T> implements Sender {
return isConsole() || this.factory.hasPermission(this.sender, permission);
}
@Override
public boolean hasPermission(CommandPermission permission) {
boolean readOnlyMode = isConsole()
? this.plugin.getConfiguration().get(ConfigKeys.READ_ONLY_MODE_CONSOLE)
: this.plugin.getConfiguration().get(ConfigKeys.READ_ONLY_MODE_PLAYERS);
if (readOnlyMode && !permission.isReadOnly()) {
return false;
}
return hasPermission(permission.getPermission());
}
@Override
public void performCommand(String commandLine) {
this.factory.performCommand(this.sender, commandLine);

View File

@@ -25,6 +25,8 @@
package me.lucko.luckperms.common.sender;
import me.lucko.luckperms.common.command.access.CommandPermission;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import net.luckperms.api.util.Tristate;
@@ -47,6 +49,16 @@ public abstract class DummyConsoleSender implements Sender {
return true;
}
@Override
public boolean hasPermission(CommandPermission permission) {
boolean readOnlyMode = this.platform.getConfiguration().get(ConfigKeys.READ_ONLY_MODE_CONSOLE);
if (readOnlyMode && !permission.isReadOnly()) {
return false;
}
return true;
}
@Override
public void performCommand(String commandLine) {

View File

@@ -132,9 +132,7 @@ public interface Sender {
* @param permission the permission to check for
* @return true if the sender has the permission
*/
default boolean hasPermission(CommandPermission permission) {
return hasPermission(permission.getPermission());
}
boolean hasPermission(CommandPermission permission);
/**
* Makes the sender perform a command.

View File

@@ -14,6 +14,7 @@ luckperms.commandsystem.command-not-recognised=Command not recognised
luckperms.commandsystem.no-permission=You do not have permission to use this command!
luckperms.commandsystem.no-permission-subcommands=You do not have permission to use any sub commands
luckperms.commandsystem.already-executing-command=Another command is being executed, waiting for it to finish...
luckperms.commandsystem.commands-disabled=LuckPerms commands are disabled
luckperms.commandsystem.usage.sub-commands-header=Sub Commands
luckperms.commandsystem.usage.usage-header=Command Usage
luckperms.commandsystem.usage.arguments-header=Arguments

View File

@@ -0,0 +1,83 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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 me.lucko.luckperms.common.command.access;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.testcontainers.shaded.com.google.common.collect.ImmutableSet;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class CommandPermissionTest {
private static final ImmutableSet<CommandPermission> ALLOWED_READ_ONLY = ImmutableSet.of(
CommandPermission.SYNC,
CommandPermission.INFO,
CommandPermission.EDITOR,
CommandPermission.VERBOSE,
CommandPermission.TREE,
CommandPermission.SEARCH,
CommandPermission.EXPORT,
CommandPermission.RELOAD_CONFIG,
CommandPermission.TRANSLATIONS,
CommandPermission.LIST_GROUPS,
CommandPermission.LIST_TRACKS,
CommandPermission.USER_INFO,
CommandPermission.USER_PERM_INFO,
CommandPermission.USER_PERM_CHECK,
CommandPermission.USER_PARENT_INFO,
CommandPermission.USER_META_INFO,
CommandPermission.USER_EDITOR,
CommandPermission.USER_SHOW_TRACKS,
CommandPermission.GROUP_INFO,
CommandPermission.GROUP_PERM_INFO,
CommandPermission.GROUP_PERM_CHECK,
CommandPermission.GROUP_PARENT_INFO,
CommandPermission.GROUP_META_INFO,
CommandPermission.GROUP_EDITOR,
CommandPermission.GROUP_LIST_MEMBERS,
CommandPermission.GROUP_SHOW_TRACKS,
CommandPermission.TRACK_INFO,
CommandPermission.TRACK_EDITOR,
CommandPermission.LOG_RECENT,
CommandPermission.LOG_USER_HISTORY,
CommandPermission.LOG_GROUP_HISTORY,
CommandPermission.LOG_TRACK_HISTORY,
CommandPermission.LOG_SEARCH,
CommandPermission.LOG_NOTIFY,
CommandPermission.SPONGE_PERMISSION_INFO,
CommandPermission.SPONGE_PARENT_INFO,
CommandPermission.SPONGE_OPTION_INFO
);
@ParameterizedTest
@EnumSource(CommandPermission.class)
public void testReadOnly(CommandPermission permission) {
String name = permission.name();
assertEquals(ALLOWED_READ_ONLY.contains(permission), permission.isReadOnly(), name);
}
}

View File

@@ -90,8 +90,10 @@ public class LPFabricPlugin extends AbstractLuckPermsPlugin {
new FabricPermissionsApiListener(this).registerListeners();
// Command registration also need to occur early, and will persist across game states as well.
this.commandManager = new FabricCommandExecutor(this);
this.commandManager.register();
if (!skipCommandRegistration()) {
this.commandManager = new FabricCommandExecutor(this);
this.commandManager.register();
}
new FabricOtherListeners(this).registerListeners();
}

View File

@@ -650,3 +650,35 @@ update-client-command-list = true
# If LuckPerms should attempt to resolve Vanilla command target selectors for LP commands.
# See here for more info: https://minecraft.wiki/w/Target_selectors
resolve-command-selectors = false
# If the plugin should run in "read-only" mode for commands.
#
# In this mode, players or the console will only be able to execute commands that read or view
# data, and will not be able to execute any LP commands that modify data.
#
# If enabling read-only mode for just players, be aware that some other plugins may allow players
# to execute commands as the console, allowing them to run LP commands indirectly.
#
# Note: This does not affect interactions with LuckPerms via the API.
commands-read-only-mode {
players = false
console = false
# If LuckPerms commands should be disabled. When true, this will prevent all LP commands from being
# executed. In a sense this is a more extreme version of read-only mode (see above).
#
# LuckPerms will still act as the permission manager, but server administrators will be unable to
# make changes via commands.
#
# If disabling commands just for players, be aware that some other plugins may allow players to
# execute commands as the console, allowing them to run commands indirectly.
#
# If commands are disabled for both players and the console, LuckPerms will not attempt to register
# a command with the server at all & in a sense will be invisible to both players and admins.
#
# Note: This does not affect interactions with LuckPerms via the API.
disable-luckperms-commands {
players = false
console = false
}

View File

@@ -99,8 +99,10 @@ public class LPForgePlugin extends AbstractLuckPermsPlugin {
ForgePermissionHandlerListener permissionHandlerListener = new ForgePermissionHandlerListener(this);
this.bootstrap.registerListeners(permissionHandlerListener);
this.commandManager = new ForgeCommandExecutor(this);
this.bootstrap.registerListeners(this.commandManager);
if (!skipCommandRegistration()) {
this.commandManager = new ForgeCommandExecutor(this);
this.bootstrap.registerListeners(this.commandManager);
}
PluginMessageMessenger.registerChannel();
}

View File

@@ -639,3 +639,35 @@ update-client-command-list = true
# If LuckPerms should attempt to resolve Vanilla command target selectors for LP commands.
# See here for more info: https://minecraft.wiki/w/Target_selectors
resolve-command-selectors = false
# If the plugin should run in "read-only" mode for commands.
#
# In this mode, players or the console will only be able to execute commands that read or view
# data, and will not be able to execute any LP commands that modify data.
#
# If enabling read-only mode for just players, be aware that some other plugins may allow players
# to execute commands as the console, allowing them to run LP commands indirectly.
#
# Note: This does not affect interactions with LuckPerms via the API.
commands-read-only-mode {
players = false
console = false
# If LuckPerms commands should be disabled. When true, this will prevent all LP commands from being
# executed. In a sense this is a more extreme version of read-only mode (see above).
#
# LuckPerms will still act as the permission manager, but server administrators will be unable to
# make changes via commands.
#
# If disabling commands just for players, be aware that some other plugins may allow players to
# execute commands as the console, allowing them to run commands indirectly.
#
# If commands are disabled for both players and the console, LuckPerms will not attempt to register
# a command with the server at all & in a sense will be invisible to both players and admins.
#
# Note: This does not affect interactions with LuckPerms via the API.
disable-luckperms-commands {
players = false
console = false
}

View File

@@ -95,8 +95,10 @@ public class LPNeoForgePlugin extends AbstractLuckPermsPlugin {
NeoForgePermissionHandlerListener permissionHandlerListener = new NeoForgePermissionHandlerListener(this);
this.bootstrap.registerListeners(permissionHandlerListener);
this.commandManager = new NeoForgeCommandExecutor(this);
this.bootstrap.registerListeners(this.commandManager);
if (!skipCommandRegistration()) {
this.commandManager = new NeoForgeCommandExecutor(this);
this.bootstrap.registerListeners(this.commandManager);
}
PluginMessageMessenger.registerChannel();
}

View File

@@ -639,3 +639,35 @@ update-client-command-list = true
# If LuckPerms should attempt to resolve Vanilla command target selectors for LP commands.
# See here for more info: https://minecraft.wiki/w/Target_selectors
resolve-command-selectors = false
# If the plugin should run in "read-only" mode for commands.
#
# In this mode, players or the console will only be able to execute commands that read or view
# data, and will not be able to execute any LP commands that modify data.
#
# If enabling read-only mode for just players, be aware that some other plugins may allow players
# to execute commands as the console, allowing them to run LP commands indirectly.
#
# Note: This does not affect interactions with LuckPerms via the API.
commands-read-only-mode {
players = false
console = false
# If LuckPerms commands should be disabled. When true, this will prevent all LP commands from being
# executed. In a sense this is a more extreme version of read-only mode (see above).
#
# LuckPerms will still act as the permission manager, but server administrators will be unable to
# make changes via commands.
#
# If disabling commands just for players, be aware that some other plugins may allow players to
# execute commands as the console, allowing them to run commands indirectly.
#
# If commands are disabled for both players and the console, LuckPerms will not attempt to register
# a command with the server at all & in a sense will be invisible to both players and admins.
#
# Note: This does not affect interactions with LuckPerms via the API.
disable-luckperms-commands {
players = false
console = false
}

View File

@@ -653,3 +653,33 @@ disable-bulkupdate: false
#
# - When this happens, the plugin will set their primary group back to default.
prevent-primary-group-removal: false
# If the plugin should run in "read-only" mode for commands.
#
# In this mode, players or the console will only be able to execute commands that read or view
# data, and will not be able to execute any LP commands that modify data.
#
# If enabling read-only mode for just players, be aware that some other plugins may allow players
# to execute commands as the console, allowing them to run LP commands indirectly.
#
# Note: This does not affect interactions with LuckPerms via the API.
commands-read-only-mode:
players: false
console: false
# If LuckPerms commands should be disabled. When true, this will prevent all LP commands from being
# executed. In a sense this is a more extreme version of read-only mode (see above).
#
# LuckPerms will still act as the permission manager, but server administrators will be unable to
# make changes via commands.
#
# If disabling commands just for players, be aware that some other plugins may allow players to
# execute commands as the console, allowing them to run commands indirectly.
#
# If commands are disabled for both players and the console, LuckPerms will not attempt to register
# a command with the server at all & in a sense will be invisible to both players and admins.
#
# Note: This does not affect interactions with LuckPerms via the API.
disable-luckperms-commands:
players: false
console: false

View File

@@ -637,3 +637,35 @@ update-client-command-list = true
# If LuckPerms should attempt to resolve Vanilla command target selectors for LP commands.
# See here for more info: https://minecraft.wiki/w/Target_selectors
resolve-command-selectors = false
# If the plugin should run in "read-only" mode for commands.
#
# In this mode, players or the console will only be able to execute commands that read or view
# data, and will not be able to execute any LP commands that modify data.
#
# If enabling read-only mode for just players, be aware that some other plugins may allow players
# to execute commands as the console, allowing them to run LP commands indirectly.
#
# Note: This does not affect interactions with LuckPerms via the API.
commands-read-only-mode {
players = false
console = false
# If LuckPerms commands should be disabled. When true, this will prevent all LP commands from being
# executed. In a sense this is a more extreme version of read-only mode (see above).
#
# LuckPerms will still act as the permission manager, but server administrators will be unable to
# make changes via commands.
#
# If disabling commands just for players, be aware that some other plugins may allow players to
# execute commands as the console, allowing them to run commands indirectly.
#
# If commands are disabled for both players and the console, LuckPerms will not attempt to register
# a command with the server at all & in a sense will be invisible to both players and admins.
#
# Note: This does not affect interactions with LuckPerms via the API.
disable-luckperms-commands {
players = false
console = false
}

View File

@@ -576,3 +576,33 @@ disable-bulkupdate: false
#
# - When this happens, the plugin will set their primary group back to default.
prevent-primary-group-removal: false
# If the plugin should run in "read-only" mode for commands.
#
# In this mode, players or the console will only be able to execute commands that read or view
# data, and will not be able to execute any LP commands that modify data.
#
# If enabling read-only mode for just players, be aware that some other plugins may allow players
# to execute commands as the console, allowing them to run LP commands indirectly.
#
# Note: This does not affect interactions with LuckPerms via the API.
commands-read-only-mode:
players: false
console: false
# If LuckPerms commands should be disabled. When true, this will prevent all LP commands from being
# executed. In a sense this is a more extreme version of read-only mode (see above).
#
# LuckPerms will still act as the permission manager, but server administrators will be unable to
# make changes via commands.
#
# If disabling commands just for players, be aware that some other plugins may allow players to
# execute commands as the console, allowing them to run commands indirectly.
#
# If commands are disabled for both players and the console, LuckPerms will not attempt to register
# a command with the server at all & in a sense will be invisible to both players and admins.
#
# Note: This does not affect interactions with LuckPerms via the API.
disable-luckperms-commands:
players: false
console: false

View File

@@ -1407,4 +1407,42 @@ public class CommandsIntegrationTest {
});
}
@Test
public void testReadOnlyMode(@TempDir Path tempDir) {
Map<String, String> config = new HashMap<>(CONFIG);
config.put("commands-read-only-mode.players", "true");
TestPluginProvider.use(tempDir, config, (app, bootstrap, plugin) -> {
CommandExecutor executor = app.getCommandExecutor();
new CommandTester(executor)
.givenHasPermissions("luckperms.group.permission.info")
.whenRunCommand("group default permission info")
.thenExpect("[LP] default does not have any permissions set.")
.givenHasPermissions("luckperms.group.permission.info", "luckperms.group.permission.set")
.whenRunCommand("group default permission set test true")
.thenExpect("[LP] You do not have permission to use this command!", false)
.givenHasAllPermissions()
.whenRunCommand("group default permission set test true")
.thenExpect("[LP] You do not have permission to use this command!");
});
}
@Test
public void testCommandsDisabled(@TempDir Path tempDir) {
Map<String, String> config = new HashMap<>(CONFIG);
config.put("disable-luckperms-commands.players", "true");
TestPluginProvider.use(tempDir, config, (app, bootstrap, plugin) -> {
CommandExecutor executor = app.getCommandExecutor();
new CommandTester(executor)
.givenHasAllPermissions()
.whenRunCommand("info")
.thenExpect("[LP] LuckPerms commands are disabled.");
});
}
}

View File

@@ -154,10 +154,21 @@ public final class CommandTester implements Consumer<Component>, Function<String
* @return this
*/
public CommandTester thenExpect(String expected) {
return thenExpect(expected, true);
}
/**
* Asserts that the current contents of the message buffer matches the given input string.
*
* @param expected the expected contents
* @param checkPermissions whether to assert that the exact permissions were checked
* @return this
*/
public CommandTester thenExpect(String expected, boolean checkPermissions) {
String actual = this.renderBuffer();
assertEquals(expected.trim(), actual.trim());
if (this.permissions != null) {
if (checkPermissions && this.permissions != null) {
assertEquals(this.checkedPermissions, this.permissions.keySet());
}

View File

@@ -597,3 +597,33 @@ disable-bulkupdate: false
#
# - When this happens, the plugin will set their primary group back to default.
prevent-primary-group-removal: false
# If the plugin should run in "read-only" mode for commands.
#
# In this mode, players or the console will only be able to execute commands that read or view
# data, and will not be able to execute any LP commands that modify data.
#
# If enabling read-only mode for just players, be aware that some other plugins may allow players
# to execute commands as the console, allowing them to run LP commands indirectly.
#
# Note: This does not affect interactions with LuckPerms via the API.
commands-read-only-mode:
players: false
console: false
# If LuckPerms commands should be disabled. When true, this will prevent all LP commands from being
# executed. In a sense this is a more extreme version of read-only mode (see above).
#
# LuckPerms will still act as the permission manager, but server administrators will be unable to
# make changes via commands.
#
# If disabling commands just for players, be aware that some other plugins may allow players to
# execute commands as the console, allowing them to run commands indirectly.
#
# If commands are disabled for both players and the console, LuckPerms will not attempt to register
# a command with the server at all & in a sense will be invisible to both players and admins.
#
# Note: This does not affect interactions with LuckPerms via the API.
disable-luckperms-commands:
players: false
console: false