diff --git a/bukkit/src/main/resources/luckperms.commodore b/bukkit/src/main/resources/luckperms.commodore index c74b52525..1d922effd 100644 --- a/bukkit/src/main/resources/luckperms.commodore +++ b/bukkit/src/main/resources/luckperms.commodore @@ -54,6 +54,9 @@ luckperms { translations { install; } + applyedits { + code brigadier:string single_word; + } creategroup { name brigadier:string single_word { weight brigadier:integer { diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/ApplyEditsCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/ApplyEditsCommand.java index dec561285..90c584d03 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/ApplyEditsCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/ApplyEditsCommand.java @@ -47,6 +47,8 @@ public class ApplyEditsCommand extends SingleCommand { @Override public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, String label) { + boolean ignoreSessionWarning = args.remove("--force"); + String code = args.get(0); if (code.isEmpty()) { @@ -71,7 +73,7 @@ public class ApplyEditsCommand extends SingleCommand { return; } - new WebEditorResponse(data).apply(plugin, sender); + new WebEditorResponse(code, data).apply(plugin, sender, label, ignoreSessionWarning); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java index f6c51ae5c..20da8fa5e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java +++ b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java @@ -844,6 +844,42 @@ public interface Message { }) .build(); + Args2 APPLY_EDITS_SESSION_UNKNOWN = (code, label) -> join(newline(), + // "&4The changes received from the web editor were not made in a session started on this server!" + // "&cAre you sure you're running the /lp applyedits command in the right place?" + // "&cTo ignore this warning and apply the changes anyway, run: &4/lp applyedits --force" + prefixed(translatable() + .key("luckperms.command.editor.apply-edits.unknown-session") + .color(DARK_RED)), + prefixed(translatable() + .key("luckperms.command.editor.apply-edits.right-server-question") + .color(RED) + .args(text("/" + label + " applyedits"))), + prefixed(translatable() + .key("luckperms.command.editor.apply-edits.bypass-warning") + .color(RED) + .append(text(": ")) + .append(text("/" + label + " applyedits " + code + " --force", DARK_RED))) + ); + + Args2 APPLY_EDITS_SESSION_APPLIED_ALREADY = (code, label) -> join(newline(), + // "&4The changes received from the web editor are based on an initial session which has already been applied!" + // "&cTo avoid conflicts, you should never re-use the same editor session after the changes from it have been applied once already." + // "&cTo ignore this warning and apply the changes anyway, run: /lp applyedits --force" + prefixed(translatable() + .key("luckperms.command.editor.apply-edits.already-applied") + .color(DARK_RED)), + prefixed(translatable() + .key("luckperms.command.editor.apply-edits.how-to-avoid-conflicts") + .color(RED) + .append(FULL_STOP)), + prefixed(translatable() + .key("luckperms.command.editor.apply-edits.bypass-warning") + .color(RED) + .append(text(": ")) + .append(text("/" + label + " applyedits " + code + " --force"))) + ); + Args1 APPLY_EDITS_INVALID_CODE = code -> prefixed(text() // "&cInvalid code. &7({})" .color(RED) diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java index f058a478d..871fefa99 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java @@ -57,6 +57,7 @@ import me.lucko.luckperms.common.tasks.ExpireTemporaryTask; import me.lucko.luckperms.common.tasks.SyncTask; import me.lucko.luckperms.common.treeview.PermissionRegistry; import me.lucko.luckperms.common.verbose.VerboseHandler; +import me.lucko.luckperms.common.webeditor.WebEditorSessionStore; import net.luckperms.api.LuckPerms; @@ -89,6 +90,7 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin { private LogDispatcher logDispatcher; private LuckPermsConfiguration configuration; private BytebinClient bytebin; + private WebEditorSessionStore webEditorSessionStore; private TranslationRepository translationRepository; private FileWatcher fileWatcher = null; private Storage storage; @@ -134,6 +136,7 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin { .build(); this.bytebin = new BytebinClient(httpClient, getConfiguration().get(ConfigKeys.BYTEBIN_URL), "luckperms"); + this.webEditorSessionStore = new WebEditorSessionStore(); // init translation repo and update bundle files this.translationRepository = new TranslationRepository(this); @@ -416,6 +419,11 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin { return this.bytebin; } + @Override + public WebEditorSessionStore getWebEditorSessionStore() { + return this.webEditorSessionStore; + } + @Override public TranslationRepository getTranslationRepository() { return this.translationRepository; diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java index 25fc4b544..89e83ada1 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java @@ -55,6 +55,7 @@ import me.lucko.luckperms.common.storage.implementation.file.watcher.FileWatcher import me.lucko.luckperms.common.tasks.SyncTask; import me.lucko.luckperms.common.treeview.PermissionRegistry; import me.lucko.luckperms.common.verbose.VerboseHandler; +import me.lucko.luckperms.common.webeditor.WebEditorSessionStore; import net.luckperms.api.query.QueryOptions; @@ -248,6 +249,13 @@ public interface LuckPermsPlugin { */ BytebinClient getBytebin(); + /** + * Gets the web editor session store + * + * @return the web editor session store + */ + WebEditorSessionStore getWebEditorSessionStore(); + /** * Gets a calculated context instance for the user using the rules of the platform. * diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/SessionState.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/SessionState.java new file mode 100644 index 000000000..a99574357 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/SessionState.java @@ -0,0 +1,48 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * 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.webeditor; + +/** + * Represents the state of a web editor session + */ +enum SessionState { + + /** + * The session is not known to this server. + */ + NOT_KNOWN, + + /** + * The session is in progress - it has been created, but updates have not been applied. + */ + IN_PROGRESS, + + /** + * The session is complete - updates have been applied. + */ + COMPLETED + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java index 868303c57..24ddbb252 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java @@ -181,6 +181,8 @@ public class WebEditorRequest { return; } + plugin.getWebEditorSessionStore().addNewSession(pasteId); + // form a url for the editor String url = plugin.getConfiguration().get(ConfigKeys.WEB_EDITOR_URL_PATTERN) + pasteId; Message.EDITOR_URL.send(sender, url); diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorResponse.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorResponse.java index 6791f56a1..b90bcd967 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorResponse.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorResponse.java @@ -65,12 +65,18 @@ import java.util.UUID; */ public class WebEditorResponse { + /** + * The id of the response payload + */ + private final String id; + /** * The encoded json object this payload is made up of */ private final JsonObject payload; - public WebEditorResponse(JsonObject payload) { + public WebEditorResponse(String id, JsonObject payload) { + this.id = id; this.payload = payload; } @@ -80,7 +86,34 @@ public class WebEditorResponse { * @param plugin the plugin * @param sender the sender who is applying the session */ - public void apply(LuckPermsPlugin plugin, Sender sender) { + public void apply(LuckPermsPlugin plugin, Sender sender, String commandLabel, boolean ignoreSessionWarning) { + JsonElement sessionIdJson = this.payload.get("sessionId"); + if (sessionIdJson != null) { + String sessionId = sessionIdJson.getAsString(); + WebEditorSessionStore sessionStore = plugin.getWebEditorSessionStore(); + + SessionState state = sessionStore.getSessionState(sessionId); + switch (state) { + case COMPLETED: + if (!ignoreSessionWarning) { + Message.APPLY_EDITS_SESSION_APPLIED_ALREADY.send(sender, this.id, commandLabel); + return; + } + break; + case NOT_KNOWN: + if (!ignoreSessionWarning) { + Message.APPLY_EDITS_SESSION_UNKNOWN.send(sender, this.id, commandLabel); + return; + } + break; + case IN_PROGRESS: + sessionStore.markSessionCompleted(sessionId); + break; + default: + throw new AssertionError(state); + } + } + Session session = new Session(plugin, sender); boolean work = false; diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSessionStore.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSessionStore.java new file mode 100644 index 000000000..be7d897f8 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSessionStore.java @@ -0,0 +1,67 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * 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.webeditor; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Contains a store of known web editor sessions. + */ +public class WebEditorSessionStore { + private final Map sessions = new ConcurrentHashMap<>(); + + /** + * Adds a newly created session to the store. + * + * @param id the id of the session + */ + public void addNewSession(String id) { + this.sessions.put(id, SessionState.IN_PROGRESS); + } + + /** + * Gets the session state for the given session id. + * + * @param id the id of the session + * @return the session state + */ + public @NonNull SessionState getSessionState(String id) { + return this.sessions.getOrDefault(id, SessionState.NOT_KNOWN); + } + + /** + * Marks a given session as complete. + * + * @param id the id of the session + */ + public void markSessionCompleted(String id) { + this.sessions.put(id, SessionState.COMPLETED); + } + +} diff --git a/common/src/main/resources/luckperms_en.properties b/common/src/main/resources/luckperms_en.properties index 0d5202ad5..e85b99eef 100644 --- a/common/src/main/resources/luckperms_en.properties +++ b/common/src/main/resources/luckperms_en.properties @@ -93,6 +93,11 @@ luckperms.command.editor.apply-edits.success.deletions-singular=deletion luckperms.command.editor.apply-edits.no-changes=No changes were applied from the web editor, the returned data didn''t contain any edits luckperms.command.editor.apply-edits.unknown-type=Unable to apply edit to the specified object type luckperms.command.editor.apply-edits.unable-to-read=Unable to read data using the given code +luckperms.command.editor.apply-edits.unknown-session=The changes received from the web editor were not made in a session started on this server! +luckperms.command.editor.apply-edits.right-server-question=Are you sure you''re running the {0} command in the right place? +luckperms.command.editor.apply-edits.already-applied=The changes received from the web editor are based on an initial session which has already been applied! +luckperms.command.editor.apply-edits.how-to-avoid-conflicts=To avoid conflicts, you should never re-use the same editor session after the changes from it have been applied once already +luckperms.command.editor.apply-edits.bypass-warning=To ignore this warning and apply the changes anyway, run luckperms.command.search.searching.permission=Searching for users and groups with {0} luckperms.command.search.searching.inherit=Searching for users and groups who inherit from {0} luckperms.command.search.result=Found {0} entries from {1} users and {2} groups