From 31d435dc2b49eceee747edc06f33f131d16632aa Mon Sep 17 00:00:00 2001 From: Luck Date: Thu, 22 Feb 2018 22:09:37 +0000 Subject: [PATCH] Update web related functionality to point to new locations and APIs (#770) (#784) --- .../luckperms/bukkit/LPBukkitPlugin.java | 11 +- .../luckperms/bungee/LPBungeePlugin.java | 11 +- .../delegates/manager/ApiContextManager.java | 4 +- .../impl/generic/other/HolderEditor.java | 8 +- .../commands/impl/misc/CheckCommand.java | 2 +- .../commands/impl/misc/DebugCommand.java | 110 ++------- .../commands/impl/misc/EditorCommand.java | 8 +- .../commands/impl/misc/TreeCommand.java | 41 +--- .../commands/impl/misc/VerboseCommand.java | 6 +- .../common/commands/impl/user/UserInfo.java | 2 +- .../luckperms/common/config/ConfigKeys.java | 14 +- .../common/{inheritance => }/graph/Graph.java | 2 +- .../graph/GraphTraversers.java | 2 +- .../graph/TraversalAlgorithm.java | 2 +- .../common/inheritance/InheritanceGraph.java | 6 +- .../luckperms/common/locale/CommandSpec.java | 8 +- .../common/plugin/LuckPermsPlugin.java | 5 +- .../AllParentsByWeightHolder.java | 2 +- .../primarygroup/ParentsByWeightHolder.java | 2 +- .../common/treeview/ImmutableTreeNode.java | 14 ++ .../luckperms/common/treeview/TreeView.java | 201 +++++----------- .../me/lucko/luckperms/common/utils/Gist.java | 189 --------------- .../common/utils/StackTracePrinter.java | 110 +++++++++ .../gson/JArray.java} | 59 +++-- .../luckperms/common/utils/gson/JElement.java | 37 +++ .../luckperms/common/utils/gson/JObject.java | 74 ++++++ .../common/utils/{ => web}/HttpClient.java | 2 +- .../luckperms/common/utils/web/Pastebin.java | 43 ++++ .../common/utils/web/StandardPastebin.java | 161 +++++++++++++ .../luckperms/common/verbose/CheckData.java | 36 +++ .../common/verbose/VerboseHandler.java | 6 +- .../common/verbose/VerboseListener.java | 217 +++++------------- .../luckperms/common/webeditor/WebEditor.java | 116 ++++------ .../luckperms/nukkit/LPNukkitPlugin.java | 11 +- .../luckperms/sponge/LPSpongePlugin.java | 11 +- 35 files changed, 749 insertions(+), 784 deletions(-) rename common/src/main/java/me/lucko/luckperms/common/{inheritance => }/graph/Graph.java (96%) rename common/src/main/java/me/lucko/luckperms/common/{inheritance => }/graph/GraphTraversers.java (99%) rename common/src/main/java/me/lucko/luckperms/common/{inheritance => }/graph/TraversalAlgorithm.java (97%) delete mode 100644 common/src/main/java/me/lucko/luckperms/common/utils/Gist.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/utils/StackTracePrinter.java rename common/src/main/java/me/lucko/luckperms/common/{treeview/TreeViewBuilder.java => utils/gson/JArray.java} (55%) create mode 100644 common/src/main/java/me/lucko/luckperms/common/utils/gson/JElement.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/utils/gson/JObject.java rename common/src/main/java/me/lucko/luckperms/common/utils/{ => web}/HttpClient.java (98%) create mode 100644 common/src/main/java/me/lucko/luckperms/common/utils/web/Pastebin.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/utils/web/StandardPastebin.java diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java index 7690ce44..e1aeebcc 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java @@ -105,8 +105,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.stream.Stream; -import javax.annotation.Nullable; - /** * LuckPerms implementation for the Bukkit API. */ @@ -190,7 +188,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { private void enable() { this.startTime = System.currentTimeMillis(); sendStartupBanner(getConsoleSender()); - this.verboseHandler = new VerboseHandler(this.scheduler.asyncBukkit(), getVersion()); + this.verboseHandler = new VerboseHandler(this.scheduler.asyncBukkit()); this.permissionVault = new PermissionVault(this.scheduler.asyncBukkit()); this.logDispatcher = new LogDispatcher(this); @@ -539,14 +537,13 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { } } - @Nullable @Override - public Contexts getContextForUser(User user) { + public Optional getContextForUser(User user) { Player player = getPlayer(user); if (player == null) { - return null; + return Optional.empty(); } - return this.contextManager.getApplicableContexts(player); + return Optional.of(this.contextManager.getApplicableContexts(player)); } @Override diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeePlugin.java b/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeePlugin.java index 3ebc9e28..9e5610cf 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeePlugin.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeePlugin.java @@ -90,8 +90,6 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; -import javax.annotation.Nullable; - /** * LuckPerms implementation for the BungeeCord API. */ @@ -141,7 +139,7 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin { public void onEnable() { this.startTime = System.currentTimeMillis(); sendStartupBanner(getConsoleSender()); - this.verboseHandler = new VerboseHandler(this.scheduler.async(), getVersion()); + this.verboseHandler = new VerboseHandler(this.scheduler.async()); this.permissionVault = new PermissionVault(this.scheduler.async()); this.logDispatcher = new LogDispatcher(this); @@ -339,14 +337,13 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin { return Optional.empty(); } - @Nullable @Override - public Contexts getContextForUser(User user) { + public Optional getContextForUser(User user) { ProxiedPlayer player = getPlayer(user); if (player == null) { - return null; + return Optional.empty(); } - return this.contextManager.getApplicableContexts(player); + return Optional.of(this.contextManager.getApplicableContexts(player)); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/api/delegates/manager/ApiContextManager.java b/common/src/main/java/me/lucko/luckperms/common/api/delegates/manager/ApiContextManager.java index ed434fb3..59f7b7bd 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/delegates/manager/ApiContextManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/delegates/manager/ApiContextManager.java @@ -74,14 +74,14 @@ public class ApiContextManager implements me.lucko.luckperms.api.context.Context @Override public Optional lookupApplicableContext(@Nonnull User user) { Objects.requireNonNull(user, "user"); - return Optional.ofNullable(this.plugin.getContextForUser(ApiUser.cast(user))).map(c -> c.getContexts().makeImmutable()); + return this.plugin.getContextForUser(ApiUser.cast(user)).map(c -> c.getContexts().makeImmutable()); } @Nonnull @Override public Optional lookupApplicableContexts(@Nonnull User user) { Objects.requireNonNull(user, "user"); - return Optional.ofNullable(this.plugin.getContextForUser(ApiUser.cast(user))); + return this.plugin.getContextForUser(ApiUser.cast(user)); } @Nonnull diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderEditor.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderEditor.java index acb660ab..7b66ee43 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderEditor.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderEditor.java @@ -25,7 +25,6 @@ package me.lucko.luckperms.common.commands.impl.generic.other; -import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; import me.lucko.luckperms.common.commands.ArgumentPermissions; @@ -40,6 +39,7 @@ import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.utils.Predicates; +import me.lucko.luckperms.common.utils.web.StandardPastebin; import me.lucko.luckperms.common.webeditor.WebEditor; import net.kyori.text.Component; @@ -69,14 +69,14 @@ public class HolderEditor extends SubCommand { JsonObject payload = WebEditor.formPayload(Collections.singletonList(holder), sender, label, plugin); // upload the payload data to gist - String gistId = WebEditor.postToGist(new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create().toJson(payload)); - if (gistId == null) { + String pasteId = StandardPastebin.BYTEBIN.postJson(payload).id(); + if (pasteId == null) { Message.EDITOR_UPLOAD_FAILURE.send(sender); return CommandResult.STATE_ERROR; } // form a url for the editor - String url = plugin.getConfiguration().get(ConfigKeys.WEB_EDITOR_URL_PATTERN) + "?" + gistId; + String url = plugin.getConfiguration().get(ConfigKeys.WEB_EDITOR_URL_PATTERN) + "?" + pasteId; Message.EDITOR_URL.send(sender); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/CheckCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/CheckCommand.java index f0df5929..64a05ed9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/CheckCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/CheckCommand.java @@ -68,7 +68,7 @@ public class CheckCommand extends SingleCommand { return CommandResult.STATE_ERROR; } - Tristate tristate = user.getCachedData().getPermissionData(plugin.getContextForUser(user)).getPermissionValue(permission, CheckOrigin.INTERNAL); + Tristate tristate = user.getCachedData().getPermissionData(plugin.getContextForUser(user).orElse(plugin.getContextManager().getStaticContexts())).getPermissionValue(permission, CheckOrigin.INTERNAL); Message.CHECK_RESULT.send(sender, user.getFriendlyName(), permission, CommandUtils.formatTristate(tristate)); return CommandResult.SUCCESS; } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/DebugCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/DebugCommand.java index 687d737f..75e886b2 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/DebugCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/DebugCommand.java @@ -27,9 +27,6 @@ package me.lucko.luckperms.common.commands.impl.misc; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.caching.MetaContexts; @@ -51,9 +48,11 @@ import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.processors.PermissionProcessor; -import me.lucko.luckperms.common.utils.Gist; import me.lucko.luckperms.common.utils.Predicates; -import me.lucko.luckperms.common.utils.TextUtils; +import me.lucko.luckperms.common.utils.gson.JArray; +import me.lucko.luckperms.common.utils.gson.JObject; +import me.lucko.luckperms.common.utils.web.Pastebin; +import me.lucko.luckperms.common.utils.web.StandardPastebin; import net.kyori.text.Component; import net.kyori.text.TextComponent; @@ -66,7 +65,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; -import java.util.function.Supplier; +import java.util.function.BiConsumer; import java.util.stream.Collectors; public class DebugCommand extends SingleCommand { @@ -80,19 +79,26 @@ public class DebugCommand extends SingleCommand { public CommandResult execute(LuckPermsPlugin plugin, Sender sender, List args, String label) { Message.DEBUG_START.send(sender); - Gist gist = Gist.builder() - .description("LuckPerms Debug Output") - .file("__DEBUG__.md", TextUtils.joinNewline("# Debug Output", "The debugging data can be found in the files below.")) - .file("platform.json", GSON.toJson(getPlatformData(plugin).toJson())) - .file("storage.json", GSON.toJson(getStorageData(plugin).toJson())) - .file("context.json", GSON.toJson(getContextData(plugin).toJson())) - .file("players.json", GSON.toJson(getPlayersData(plugin).toJson())) - .upload(); + StringBuilder sb = new StringBuilder(); + sb.append("LuckPerms Debug Output\n\n\n"); + + BiConsumer builder = (name, content) -> { + sb.append("-- ").append(name).append(" --\n"); + sb.append(GSON.toJson(content.toJson())); + sb.append("\n\n"); + }; + + builder.accept("platform.json", getPlatformData(plugin)); + builder.accept("storage.json", getStorageData(plugin)); + builder.accept("context.json", getContextData(plugin)); + builder.accept("players.json", getPlayersData(plugin)); + + Pastebin.Paste paste = StandardPastebin.HASTEBIN.postPlain(sb.toString()); Message.DEBUG_URL.send(sender); - Component message = TextComponent.builder(gist.getUrl()).color(TextColor.AQUA) - .clickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, String.valueOf(gist.getUrl()))) + Component message = TextComponent.builder(paste.url()).color(TextColor.AQUA) + .clickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, String.valueOf(paste.url()))) .hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to open the debugging data.").color(TextColor.GRAY))) .build(); @@ -190,7 +196,7 @@ public class DebugCommand extends SingleCommand { ) .add("activeContext", () -> { JObject obj = new JObject(); - Contexts contexts = plugin.getContextForUser(user); + Contexts contexts = plugin.getContextForUser(user).orElse(null); if (contexts != null) { MetaContexts metaContexts = plugin.getContextManager().formMetaContexts(contexts); obj.add("data", new JObject() @@ -298,74 +304,4 @@ public class DebugCommand extends SingleCommand { }); } - // stupidly simply fluent gson wrappers - - private interface JElement { - JsonElement toJson(); - } - - private static final class JObject implements JElement { - private final JsonObject o = new JsonObject(); - - @Override - public JsonElement toJson() { - return this.o; - } - - public JObject add(String key, String value) { - this.o.addProperty(key, value); - return this; - } - - public JObject add(String key, Number value) { - this.o.addProperty(key, value); - return this; - } - - public JObject add(String key, Boolean value) { - this.o.addProperty(key, value); - return this; - } - - public JObject add(String key, JsonElement value) { - this.o.add(key, value); - return this; - } - - public JObject add(String key, JElement value) { - return add(key, value.toJson()); - } - - public JObject add(String key, Supplier value) { - return add(key, value.get().toJson()); - } - } - - private static final class JArray implements JElement { - private final JsonArray o = new JsonArray(); - - @Override - public JsonElement toJson() { - return this.o; - } - - public JArray add(String value) { - this.o.add(value); - return this; - } - - public JArray add(JsonElement value) { - this.o.add(value); - return this; - } - - public JArray add(JElement value) { - return add(value.toJson()); - } - - public JArray add(Supplier value) { - return add(value.get().toJson()); - } - } - } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/EditorCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/EditorCommand.java index 3cade9a3..51f74544 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/EditorCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/EditorCommand.java @@ -25,7 +25,6 @@ package me.lucko.luckperms.common.commands.impl.misc; -import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; import me.lucko.luckperms.common.commands.ArgumentPermissions; @@ -41,6 +40,7 @@ import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.utils.Predicates; +import me.lucko.luckperms.common.utils.web.StandardPastebin; import me.lucko.luckperms.common.webeditor.WebEditor; import net.kyori.text.Component; @@ -95,14 +95,14 @@ public class EditorCommand extends SingleCommand { JsonObject payload = WebEditor.formPayload(holders, sender, label, plugin); // upload the payload data to gist - String gistId = WebEditor.postToGist(new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create().toJson(payload)); - if (gistId == null) { + String pasteId = StandardPastebin.BYTEBIN.postJson(payload).id(); + if (pasteId == null) { Message.EDITOR_UPLOAD_FAILURE.send(sender); return CommandResult.STATE_ERROR; } // form a url for the editor - String url = plugin.getConfiguration().get(ConfigKeys.WEB_EDITOR_URL_PATTERN) + "?" + gistId; + String url = plugin.getConfiguration().get(ConfigKeys.WEB_EDITOR_URL_PATTERN) + "?" + pasteId; Message.EDITOR_URL.send(sender); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/TreeCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/TreeCommand.java index 77ee13aa..0085b87f 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/TreeCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/TreeCommand.java @@ -30,14 +30,13 @@ import me.lucko.luckperms.common.commands.CommandPermission; import me.lucko.luckperms.common.commands.CommandResult; import me.lucko.luckperms.common.commands.abstraction.SingleCommand; import me.lucko.luckperms.common.commands.sender.Sender; -import me.lucko.luckperms.common.commands.utils.ArgumentUtils; +import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.locale.CommandSpec; import me.lucko.luckperms.common.locale.LocaleManager; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.treeview.TreeView; -import me.lucko.luckperms.common.treeview.TreeViewBuilder; import me.lucko.luckperms.common.utils.Predicates; import me.lucko.luckperms.common.utils.Uuids; @@ -58,21 +57,17 @@ public class TreeCommand extends SingleCommand { @Override public CommandResult execute(LuckPermsPlugin plugin, Sender sender, List args, String label) { String selection = "."; - int maxLevel = 5; String player = null; if (!args.isEmpty()) { selection = args.get(0); } if (args.size() > 1) { - maxLevel = ArgumentUtils.handleIntOrElse(1, args, 5); - } - if (args.size() > 2) { - player = args.get(2); + player = args.get(1); } + User user; if (player != null) { - User user; UUID u = Uuids.parseNullable(player); if (u != null) { user = plugin.getUserManager().getIfLoaded(u); @@ -84,38 +79,20 @@ public class TreeCommand extends SingleCommand { Message.USER_NOT_ONLINE.send(sender, player); return CommandResult.STATE_ERROR; } - - PermissionCache permissionData = user.getCachedData().getPermissionData(plugin.getContextForUser(user)); - TreeView view = TreeViewBuilder.newBuilder().rootPosition(selection).maxLevels(maxLevel).build(plugin.getPermissionVault()); - - if (!view.hasData()) { - Message.TREE_EMPTY.send(sender); - return CommandResult.FAILURE; - } - - Message.TREE_UPLOAD_START.send(sender); - String url = view.uploadPasteData(plugin.getVersion(), user.getFriendlyName(), permissionData); - - Message.TREE_URL.send(sender); - - Component message = TextComponent.builder(url).color(TextColor.AQUA) - .clickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, String.valueOf(url))) - .hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to open the tree view.").color(TextColor.GRAY))) - .build(); - - sender.sendMessage(message); - return CommandResult.SUCCESS; + } else { + user = null; } - TreeView view = TreeViewBuilder.newBuilder().rootPosition(selection).maxLevels(maxLevel).build(plugin.getPermissionVault()); - + TreeView view = new TreeView(plugin.getPermissionVault(), selection); if (!view.hasData()) { Message.TREE_EMPTY.send(sender); return CommandResult.FAILURE; } Message.TREE_UPLOAD_START.send(sender); - String url = view.uploadPasteData(plugin.getVersion()); + PermissionCache permissionData = user == null ? null : user.getCachedData().getPermissionData(plugin.getContextForUser(user).orElse(plugin.getContextManager().getStaticContexts())); + String id = view.uploadPasteData(sender, user, permissionData); + String url = plugin.getConfiguration().get(ConfigKeys.TREE_VIEWER_URL_PATTERN) + "?" + id; Message.TREE_URL.send(sender); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/VerboseCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/VerboseCommand.java index d3bf68b9..d0fa0b81 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/VerboseCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/VerboseCommand.java @@ -31,6 +31,7 @@ import me.lucko.luckperms.common.commands.CommandPermission; import me.lucko.luckperms.common.commands.CommandResult; import me.lucko.luckperms.common.commands.abstraction.SingleCommand; import me.lucko.luckperms.common.commands.sender.Sender; +import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.locale.CommandSpec; import me.lucko.luckperms.common.locale.LocaleManager; import me.lucko.luckperms.common.locale.Message; @@ -64,8 +65,6 @@ public class VerboseCommand extends SingleCommand { return CommandResult.INVALID_ARGS; } - boolean noTraces = args.remove("--notrace") || args.remove("--notraces") || args.remove("--slim") || args.remove("-s"); - boolean attachRaw = args.remove("--raw"); String mode = args.get(0).toLowerCase(); if (mode.equals("on") || mode.equals("true") || mode.equals("record")) { @@ -114,7 +113,8 @@ public class VerboseCommand extends SingleCommand { Message.VERBOSE_OFF.send(sender); } else { Message.VERBOSE_UPLOAD_START.send(sender); - String url = listener.uploadPasteData(!noTraces, attachRaw); + String id = listener.uploadPasteData(); + String url = plugin.getConfiguration().get(ConfigKeys.VERBOSE_VIEWER_URL_PATTERN) + "?" + id; Message.VERBOSE_RESULTS_URL.send(sender); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserInfo.java index 2ced264e..e08b9f80 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserInfo.java @@ -100,7 +100,7 @@ public class UserInfo extends SubCommand { String prefix = "&bNone"; String suffix = "&bNone"; String meta = "&bNone"; - Contexts contexts = plugin.getContextForUser(user); + Contexts contexts = plugin.getContextForUser(user).orElse(null); if (contexts != null) { ContextSet contextSet = contexts.getContexts(); if (!contextSet.isEmpty()) { diff --git a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java index b6bc56fe..91c65816 100644 --- a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java +++ b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java @@ -37,7 +37,7 @@ import me.lucko.luckperms.common.config.keys.IntegerKey; import me.lucko.luckperms.common.config.keys.LowercaseStringKey; import me.lucko.luckperms.common.config.keys.MapKey; import me.lucko.luckperms.common.config.keys.StringKey; -import me.lucko.luckperms.common.inheritance.graph.TraversalAlgorithm; +import me.lucko.luckperms.common.graph.TraversalAlgorithm; import me.lucko.luckperms.common.metastacking.SimpleMetaStackDefinition; import me.lucko.luckperms.common.metastacking.StandardStackElements; import me.lucko.luckperms.common.model.TemporaryModifier; @@ -476,7 +476,17 @@ public class ConfigKeys { /** * The URL of the web editor */ - public static final ConfigKey WEB_EDITOR_URL_PATTERN = StringKey.of("web-editor-url", "https://lpedit.lucko.me/"); + public static final ConfigKey WEB_EDITOR_URL_PATTERN = StringKey.of("web-editor-url", "https://luckperms.github.io/editor/"); + + /** + * The URL of the verbose viewer + */ + public static final ConfigKey VERBOSE_VIEWER_URL_PATTERN = StringKey.of("verbose-viewer-url", "https://luckperms.github.io/verbose/"); + + /** + * The URL of the tree viewer + */ + public static final ConfigKey TREE_VIEWER_URL_PATTERN = StringKey.of("tree-viewer-url", "https://luckperms.github.io/treeview/"); private static Map> KEYS = null; diff --git a/common/src/main/java/me/lucko/luckperms/common/inheritance/graph/Graph.java b/common/src/main/java/me/lucko/luckperms/common/graph/Graph.java similarity index 96% rename from common/src/main/java/me/lucko/luckperms/common/inheritance/graph/Graph.java rename to common/src/main/java/me/lucko/luckperms/common/graph/Graph.java index b223d0ad..b3477d43 100644 --- a/common/src/main/java/me/lucko/luckperms/common/inheritance/graph/Graph.java +++ b/common/src/main/java/me/lucko/luckperms/common/graph/Graph.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.inheritance.graph; +package me.lucko.luckperms.common.graph; /** * A minimal functional interface for graph-structured data. diff --git a/common/src/main/java/me/lucko/luckperms/common/inheritance/graph/GraphTraversers.java b/common/src/main/java/me/lucko/luckperms/common/graph/GraphTraversers.java similarity index 99% rename from common/src/main/java/me/lucko/luckperms/common/inheritance/graph/GraphTraversers.java rename to common/src/main/java/me/lucko/luckperms/common/graph/GraphTraversers.java index 1453333e..3d4aa5e6 100644 --- a/common/src/main/java/me/lucko/luckperms/common/inheritance/graph/GraphTraversers.java +++ b/common/src/main/java/me/lucko/luckperms/common/graph/GraphTraversers.java @@ -39,7 +39,7 @@ * limitations under the License. */ -package me.lucko.luckperms.common.inheritance.graph; +package me.lucko.luckperms.common.graph; import com.google.common.collect.AbstractIterator; diff --git a/common/src/main/java/me/lucko/luckperms/common/inheritance/graph/TraversalAlgorithm.java b/common/src/main/java/me/lucko/luckperms/common/graph/TraversalAlgorithm.java similarity index 97% rename from common/src/main/java/me/lucko/luckperms/common/inheritance/graph/TraversalAlgorithm.java rename to common/src/main/java/me/lucko/luckperms/common/graph/TraversalAlgorithm.java index cd058f25..b1534218 100644 --- a/common/src/main/java/me/lucko/luckperms/common/inheritance/graph/TraversalAlgorithm.java +++ b/common/src/main/java/me/lucko/luckperms/common/graph/TraversalAlgorithm.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.inheritance.graph; +package me.lucko.luckperms.common.graph; public enum TraversalAlgorithm { diff --git a/common/src/main/java/me/lucko/luckperms/common/inheritance/InheritanceGraph.java b/common/src/main/java/me/lucko/luckperms/common/inheritance/InheritanceGraph.java index 9c96d5dc..dc9b0279 100644 --- a/common/src/main/java/me/lucko/luckperms/common/inheritance/InheritanceGraph.java +++ b/common/src/main/java/me/lucko/luckperms/common/inheritance/InheritanceGraph.java @@ -27,9 +27,9 @@ package me.lucko.luckperms.common.inheritance; import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.Node; -import me.lucko.luckperms.common.inheritance.graph.Graph; -import me.lucko.luckperms.common.inheritance.graph.GraphTraversers; -import me.lucko.luckperms.common.inheritance.graph.TraversalAlgorithm; +import me.lucko.luckperms.common.graph.Graph; +import me.lucko.luckperms.common.graph.GraphTraversers; +import me.lucko.luckperms.common.graph.TraversalAlgorithm; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; diff --git a/common/src/main/java/me/lucko/luckperms/common/locale/CommandSpec.java b/common/src/main/java/me/lucko/luckperms/common/locale/CommandSpec.java index ba2bd461..07bfc8dd 100644 --- a/common/src/main/java/me/lucko/luckperms/common/locale/CommandSpec.java +++ b/common/src/main/java/me/lucko/luckperms/common/locale/CommandSpec.java @@ -58,14 +58,12 @@ public enum CommandSpec { VERBOSE("Manage verbose permission checking", "/%s verbose [filter]", Arg.list( Arg.create("on|record|off|paste", true, "whether to enable/disable logging, or to paste the logged output"), - Arg.create("filter", false, "the filter to match entries against"), - Arg.create("--slim", false, "add \"--slim\" to exclude trace data from the pasted output") + Arg.create("filter", false, "the filter to match entries against") ) ), - TREE("Generate a tree view of permissions", "/%s tree [selection] [max level] [player]", + TREE("Generate a tree view of permissions", "/%s tree [scope] [player]", Arg.list( - Arg.create("selection", false, "the root of the tree. specify \".\" to include all permissions"), - Arg.create("max level", false, "how many branch levels should be returned"), + Arg.create("scope", false, "the root of the tree. specify \".\" to include all permissions"), Arg.create("player", false, "the name of an online player to check against") ) ), 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 258f8ecf..96adb28b 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 @@ -63,8 +63,6 @@ import java.util.Set; import java.util.UUID; import java.util.stream.Stream; -import javax.annotation.Nullable; - /** * Main internal interface for LuckPerms plugins, providing the base for * abstraction throughout the project. @@ -332,8 +330,7 @@ public interface LuckPermsPlugin { * @param user the user instance * @return a contexts object, or null if one couldn't be generated */ - @Nullable - Contexts getContextForUser(User user); + Optional getContextForUser(User user); /** * Gets the number of users online on the platform diff --git a/common/src/main/java/me/lucko/luckperms/common/primarygroup/AllParentsByWeightHolder.java b/common/src/main/java/me/lucko/luckperms/common/primarygroup/AllParentsByWeightHolder.java index 49a25254..27e09c25 100644 --- a/common/src/main/java/me/lucko/luckperms/common/primarygroup/AllParentsByWeightHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/primarygroup/AllParentsByWeightHolder.java @@ -43,7 +43,7 @@ public class AllParentsByWeightHolder extends CachedPrimaryGroupHolder { @Override protected String calculateValue() { - Contexts contexts = this.user.getPlugin().getContextForUser(this.user); + Contexts contexts = this.user.getPlugin().getContextForUser(this.user).orElse(null); if (contexts == null) { contexts = this.user.getPlugin().getContextManager().getStaticContexts(); } diff --git a/common/src/main/java/me/lucko/luckperms/common/primarygroup/ParentsByWeightHolder.java b/common/src/main/java/me/lucko/luckperms/common/primarygroup/ParentsByWeightHolder.java index b5941252..87b009db 100644 --- a/common/src/main/java/me/lucko/luckperms/common/primarygroup/ParentsByWeightHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/primarygroup/ParentsByWeightHolder.java @@ -40,7 +40,7 @@ public class ParentsByWeightHolder extends CachedPrimaryGroupHolder { @Override protected String calculateValue() { - Contexts contexts = this.user.getPlugin().getContextForUser(this.user); + Contexts contexts = this.user.getPlugin().getContextForUser(this.user).orElse(null); if (contexts == null) { contexts = this.user.getPlugin().getContextManager().getStaticContexts(); } diff --git a/common/src/main/java/me/lucko/luckperms/common/treeview/ImmutableTreeNode.java b/common/src/main/java/me/lucko/luckperms/common/treeview/ImmutableTreeNode.java index e5aad8ff..ffe69600 100644 --- a/common/src/main/java/me/lucko/luckperms/common/treeview/ImmutableTreeNode.java +++ b/common/src/main/java/me/lucko/luckperms/common/treeview/ImmutableTreeNode.java @@ -27,6 +27,7 @@ package me.lucko.luckperms.common.treeview; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; +import com.google.gson.JsonObject; import java.util.ArrayList; import java.util.Collections; @@ -101,6 +102,19 @@ public class ImmutableTreeNode implements Comparable { return results; } + public JsonObject toJson(String prefix) { + if (this.children == null) { + return new JsonObject(); + } + + JsonObject object = new JsonObject(); + for (Map.Entry entry : this.children.entrySet()) { + String name = prefix + entry.getKey(); + object.add(name, entry.getValue().toJson(name + ".")); + } + return object; + } + @Override public int compareTo(@Nonnull ImmutableTreeNode o) { return (this.children != null) == o.getChildren().isPresent() ? 0 : (this.children != null ? 1 : -1); diff --git a/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java b/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java index f3f28480..412d67b0 100644 --- a/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java +++ b/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java @@ -26,22 +26,19 @@ package me.lucko.luckperms.common.treeview; import com.google.common.base.Splitter; -import com.google.common.base.Strings; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Maps; +import com.google.gson.JsonObject; -import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.common.caching.type.PermissionCache; -import me.lucko.luckperms.common.utils.Gist; -import me.lucko.luckperms.common.verbose.CheckOrigin; +import me.lucko.luckperms.common.commands.sender.Sender; +import me.lucko.luckperms.common.model.User; +import me.lucko.luckperms.common.utils.gson.JObject; +import me.lucko.luckperms.common.utils.web.StandardPastebin; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; /** * A readable view of a branch of {@link TreeNode}s. @@ -52,15 +49,16 @@ public class TreeView { // the root of the tree private final String rootPosition; - // how many levels / branches to display - private final int maxLevel; - // the actual tree object private final ImmutableTreeNode view; - public TreeView(PermissionVault source, String rootPosition, int maxLevel) { + public TreeView(PermissionVault source, String rootPosition) { + if (rootPosition.equals("") || rootPosition.equals("*")) { + rootPosition = "."; + } else if (!rootPosition.equals(".") && rootPosition.endsWith(".")) { + rootPosition = rootPosition.substring(0, rootPosition.length() - 1); + } this.rootPosition = rootPosition; - this.maxLevel = maxLevel; Optional root = findRoot(rootPosition, source); this.view = root.map(TreeNode::makeImmutableCopy).orElse(null); @@ -115,152 +113,61 @@ public class TreeView { } /** - * Converts the view to a readable list + * Uploads the data contained in this TreeView and returns the id. * - *

The list contains KV pairs, where the key is the tree padding/structure, - * and the value is the actual permission.

- * - * @return a list of the nodes in this view + * @param sender the sender + * @param user the reference user, or null + * @param checker the permission data instance to check against, or null + * @return the id, or null */ - private List> asTreeList() { + public String uploadPasteData(Sender sender, User user, PermissionCache checker) { + // only paste if there is actually data here + if (!hasData()) { + throw new IllegalStateException(); + } + // work out the prefix to apply // since the view is relative, we need to prepend this to all permissions String prefix = this.rootPosition.equals(".") ? "" : (this.rootPosition + "."); + JsonObject jsonTree = this.view.toJson(prefix); + JObject metadata = new JObject() + .add("time", DATE_FORMAT.format(new Date(System.currentTimeMillis()))) + .add("root", this.rootPosition) + .add("uploader", new JObject() + .add("name", sender.getNameWithLocation()) + .add("uuid", sender.getUuid().toString()) + ); - List> ret = new ArrayList<>(); + JObject checks; + if (user != null && checker != null) { + metadata.add("referenceUser", new JObject() + .add("name", user.getFriendlyName()) + .add("uuid", user.getUuid().toString()) + ); - // iterate the node endings in the view - for (Map.Entry s : this.view.getNodeEndings()) { - // don't include the node if it exceeds the max level - if (s.getKey() >= this.maxLevel) { - continue; + checks = new JObject(); + for (Map.Entry node : this.view.getNodeEndings()) { + String permission = prefix + node.getValue(); + checks.add(permission, checker.getPermissionValue(permission).name().toLowerCase()); } - - // generate the tree padding characters from the node level - String treeStructure = Strings.repeat("│ ", s.getKey()) + "├── "; - // generate the permission, using the prefix and the node - String permission = prefix + s.getValue(); - - ret.add(Maps.immutableEntry(treeStructure, permission)); + } else { + checks = null; } - return ret; - } + JsonObject payload = new JObject() + .add("metadata", metadata) + .add("data", new JObject() + .add("tree", jsonTree) + .consume(obj -> { + if (checks != null) { + obj.add("checkResults", checks); + } + }) + ) + .toJson(); - /** - * Uploads the data contained in this TreeView to a paste, and returns the URL. - * - * @param version the plugin version string - * @return the url, or null - */ - public String uploadPasteData(String version) { - // only paste if there is actually data here - if (!hasData()) { - throw new IllegalStateException(); - } - - // get the data contained in the view in a list form - // for each entry, the key is the padding tree characters - // and the value is the actual permission string - List> ret = asTreeList(); - - // build the header of the paste - ImmutableList.Builder builder = getPasteHeader(version, "none", ret.size()); - - // add the tree data - builder.add("```"); - for (Map.Entry e : ret) { - builder.add(e.getKey() + e.getValue()); - } - builder.add("```"); - - // clear the initial data map - ret.clear(); - - // upload the return the data - Gist gist = Gist.builder() - .description("LuckPerms Permission Tree") - .file("luckperms-tree.md", builder.build().stream().collect(Collectors.joining("\n"))) - .upload(); - - return gist.getUrl(); - } - - /** - * Uploads the data contained in this TreeView to a paste, and returns the URL. - * - *

Unlike {@link #uploadPasteData(String)}, this method will check each permission - * against a corresponding user, and colorize the output depending on the check results.

- * - * @param version the plugin version string - * @param username the username of the reference user - * @param checker the permission data instance to check against - * @return the url, or null - */ - public String uploadPasteData(String version, String username, PermissionCache checker) { - // only paste if there is actually data here - if (!hasData()) { - throw new IllegalStateException(); - } - - // get the data contained in the view in a list form - // for each entry, the key is the padding tree characters - // and the value is the actual permission string - List> ret = asTreeList(); - - // build the header of the paste - ImmutableList.Builder builder = getPasteHeader(version, username, ret.size()); - - // add the tree data - builder.add("```diff"); - for (Map.Entry e : ret) { - - // lookup a permission value for the node - Tristate tristate = checker.getPermissionValue(e.getValue(), CheckOrigin.INTERNAL); - - // append the data to the paste - builder.add(getTristateDiffPrefix(tristate) + e.getKey() + e.getValue()); - } - builder.add("```"); - - // clear the initial data map - ret.clear(); - - // upload the return the data - Gist gist = Gist.builder() - .description("LuckPerms Permission Tree") - .file("luckperms-tree.md", builder.build().stream().collect(Collectors.joining("\n"))) - .upload(); - - return gist.getUrl(); - } - - private static String getTristateDiffPrefix(Tristate t) { - switch (t) { - case TRUE: - return "+ "; - case FALSE: - return "- "; - default: - return "# "; - } - } - - private ImmutableList.Builder getPasteHeader(String version, String referenceUser, int size) { - String date = DATE_FORMAT.format(new Date(System.currentTimeMillis())); - String selection = this.rootPosition.equals(".") ? "any" : "`" + this.rootPosition + "`"; - - return ImmutableList.builder() - .add("## Permission Tree") - .add("#### This file was automatically generated by [LuckPerms](https://github.com/lucko/LuckPerms) v" + version) - .add("") - .add("### Metadata") - .add("| Selection | Max Recursion | Reference User | Size | Produced at |") - .add("|-----------|---------------|----------------|------|-------------|") - .add("| " + selection + " | " + this.maxLevel + " | " + referenceUser + " | **" + size + "** | " + date + " |") - .add("") - .add("### Output"); + return StandardPastebin.BYTEBIN.postJson(payload).id(); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/Gist.java b/common/src/main/java/me/lucko/luckperms/common/utils/Gist.java deleted file mode 100644 index fe65777e..00000000 --- a/common/src/main/java/me/lucko/luckperms/common/utils/Gist.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * 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.utils; - -import com.google.common.collect.ImmutableList; -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import com.google.gson.stream.JsonWriter; - -import okhttp3.FormBody; -import okhttp3.MediaType; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import okhttp3.ResponseBody; - -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; - -/** - * Represents a posted GitHub Gist - */ -public class Gist { - private static final Gson GSON = new Gson(); - - private static final MediaType JSON_TYPE = MediaType.parse("application/json; charset=utf-8"); - - private static final String GIST_API_URL = "https://api.github.com/gists"; - private static final String GIT_IO_URL = "https://git.io"; - - public static Builder builder() { - return new Builder(); - } - - private final String url; - private final String id; - - private Gist(String url, String id) { - this.url = url; - this.id = id; - } - - public String getUrl() { - return this.url; - } - - public String getId() { - return this.id; - } - - private static final class GistFile { - private final String name; - private final String content; - - private GistFile(String name, String content) { - this.name = name; - this.content = content; - } - } - - public static final class Builder { - private final List files = new ArrayList<>(); - private boolean shorten = true; - private String description = "LuckPerms Gist"; - - private Builder() { - - } - - public Builder file(String name, String content) { - this.files.add(new GistFile(name, content)); - return this; - } - - public Builder shorten(boolean shorten) { - this.shorten = shorten; - return this; - } - - public Builder description(String description) { - this.description = description; - return this; - } - - public Gist upload() { - return Gist.upload(ImmutableList.copyOf(this.files), this.shorten, this.description); - } - } - - private static Gist upload(List files, boolean shorten, String description) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (JsonWriter jw = new JsonWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8))) { - jw.beginObject(); - jw.name("description").value(description); - jw.name("public").value(false); - jw.name("files").beginObject(); - for (GistFile file : files) { - jw.name(file.name).beginObject().name("content").value(file.content).endObject(); - } - jw.endObject().endObject(); - } catch (IOException e) { - throw new RuntimeException(e); - } - - RequestBody body = RequestBody.create(JSON_TYPE, out.toByteArray()); - Request request = new Request.Builder() - .url(GIST_API_URL) - .post(body) - .build(); - - try (Response response = HttpClient.makeCall(request)) { - try (ResponseBody responseBody = response.body()) { - if (responseBody == null) { - throw new RuntimeException("No response"); - } - - String id; - String pasteUrl; - try (InputStream inputStream = responseBody.byteStream()) { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { - JsonObject object = GSON.fromJson(reader, JsonObject.class); - id = object.get("id").getAsString(); - pasteUrl = object.get("html_url").getAsString(); - } - } - - if (shorten) { - pasteUrl = shortenUrl(pasteUrl); - } - - return new Gist(pasteUrl, id); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private static String shortenUrl(String pasteUrl) { - RequestBody requestBody = new FormBody.Builder() - .add("url", pasteUrl) - .build(); - - Request request = new Request.Builder() - .url(GIT_IO_URL) - .post(requestBody) - .build(); - - try (Response response = HttpClient.makeCall(request)) { - String location = response.header("Location"); - if (location == null) { - throw new RuntimeException("No location header"); - } - return location; - } catch (Exception e) { - e.printStackTrace(); - } - return pasteUrl; - } -} diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/StackTracePrinter.java b/common/src/main/java/me/lucko/luckperms/common/utils/StackTracePrinter.java new file mode 100644 index 00000000..f8492f40 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/utils/StackTracePrinter.java @@ -0,0 +1,110 @@ +/* + * 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.utils; + +import java.util.function.Consumer; +import java.util.function.Predicate; + +public final class StackTracePrinter { + public static Consumer elementToString(Consumer consumer) { + return e -> consumer.accept(e.getClassName() + "." + e.getMethodName() + (e.getLineNumber() >= 0 ? ":" + e.getLineNumber() : "")); + } + + public static Builder builder() { + return new Builder(); + } + + private final int truncateLength; + private final Predicate shouldPrintPredicate; + + private StackTracePrinter(int truncateLength, Predicate shouldPrintPredicate) { + this.truncateLength = truncateLength; + this.shouldPrintPredicate = shouldPrintPredicate; + } + + public int process(StackTraceElement[] stackTrace, Consumer consumer) { + // how many lines have been printed + int count = 0; + // if we're printing elements yet + boolean printing = false; + + for (StackTraceElement e : stackTrace) { + // start printing when the predicate passes + if (!printing && this.shouldPrintPredicate.test(e)) { + printing = true; + } + + if (!printing) continue; + if (count >= this.truncateLength) break; + + consumer.accept(e); + count++; + } + if (stackTrace.length > this.truncateLength) { + return stackTrace.length - this.truncateLength; + } + return 0; + } + + public Builder toBuilder() { + Builder builder = new Builder(); + builder.truncateLength = this.truncateLength; + builder.shouldPrintPredicate = this.shouldPrintPredicate; + return builder; + } + + public static final class Builder { + private int truncateLength = Integer.MAX_VALUE; + private Predicate shouldPrintPredicate = Predicates.alwaysTrue(); + + private Builder() { + + } + + public Builder truncateLength(int truncateLength) { + this.truncateLength = truncateLength; + return this; + } + + public Builder ignoreElementsMatching(Predicate predicate) { + this.shouldPrintPredicate = this.shouldPrintPredicate.and(predicate.negate()); + return this; + } + + public Builder ignoreClass(String className) { + return ignoreElementsMatching(e -> e.getClassName().equals(className)); + } + + public Builder ignoreClassStartingWith(String className) { + return ignoreElementsMatching(e -> e.getClassName().startsWith(className)); + } + + public StackTracePrinter build() { + return new StackTracePrinter(this.truncateLength, this.shouldPrintPredicate); + } + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/treeview/TreeViewBuilder.java b/common/src/main/java/me/lucko/luckperms/common/utils/gson/JArray.java similarity index 55% rename from common/src/main/java/me/lucko/luckperms/common/treeview/TreeViewBuilder.java rename to common/src/main/java/me/lucko/luckperms/common/utils/gson/JArray.java index d42bb5eb..663298a1 100644 --- a/common/src/main/java/me/lucko/luckperms/common/treeview/TreeViewBuilder.java +++ b/common/src/main/java/me/lucko/luckperms/common/utils/gson/JArray.java @@ -23,45 +23,42 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.treeview; +package me.lucko.luckperms.common.utils.gson; -/** - * Builds a {@link TreeView}. - */ -public class TreeViewBuilder { - public static TreeViewBuilder newBuilder() { - return new TreeViewBuilder(); +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class JArray implements JElement { + private final JsonArray array = new JsonArray(); + + @Override + public JsonArray toJson() { + return this.array; } - private String rootPosition; - private int maxLevels; - - private TreeViewBuilder() { - this.rootPosition = "."; - this.maxLevels = 5; - } - - public TreeViewBuilder rootPosition(String rootPosition) { - this.rootPosition = rootPosition; + public JArray add(String value) { + this.array.add(value); return this; } - public TreeViewBuilder maxLevels(int maxLevels) { - this.maxLevels = maxLevels; + public JArray add(JsonElement value) { + this.array.add(value); return this; } - public TreeView build(PermissionVault source) { - if (this.maxLevels < 1) { - this.maxLevels = 1; - } - if (this.rootPosition.equals("") || this.rootPosition.equals("*")) { - this.rootPosition = "."; - } else if (!this.rootPosition.equals(".") && this.rootPosition.endsWith(".")) { - this.rootPosition = this.rootPosition.substring(0, this.rootPosition.length() - 1); - } - - return new TreeView(source, this.rootPosition, this.maxLevels); + public JArray add(JElement value) { + return add(value.toJson()); } -} + public JArray add(Supplier value) { + return add(value.get().toJson()); + } + + public JArray consume(Consumer consumer) { + consumer.accept(this); + return this; + } +} \ No newline at end of file diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/gson/JElement.java b/common/src/main/java/me/lucko/luckperms/common/utils/gson/JElement.java new file mode 100644 index 00000000..7056c3b9 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/utils/gson/JElement.java @@ -0,0 +1,37 @@ +/* + * 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.utils.gson; + +import com.google.gson.JsonElement; + +/** + * Stupidly simple fluent gson wrappers + */ +public interface JElement { + + JsonElement toJson(); + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/gson/JObject.java b/common/src/main/java/me/lucko/luckperms/common/utils/gson/JObject.java new file mode 100644 index 00000000..61977dba --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/utils/gson/JObject.java @@ -0,0 +1,74 @@ +/* + * 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.utils.gson; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class JObject implements JElement { + private final JsonObject object = new JsonObject(); + + @Override + public JsonObject toJson() { + return this.object; + } + + public JObject add(String key, String value) { + this.object.addProperty(key, value); + return this; + } + + public JObject add(String key, Number value) { + this.object.addProperty(key, value); + return this; + } + + public JObject add(String key, Boolean value) { + this.object.addProperty(key, value); + return this; + } + + public JObject add(String key, JsonElement value) { + this.object.add(key, value); + return this; + } + + public JObject add(String key, JElement value) { + return add(key, value.toJson()); + } + + public JObject add(String key, Supplier value) { + return add(key, value.get().toJson()); + } + + public JObject consume(Consumer consumer) { + consumer.accept(this); + return this; + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/HttpClient.java b/common/src/main/java/me/lucko/luckperms/common/utils/web/HttpClient.java similarity index 98% rename from common/src/main/java/me/lucko/luckperms/common/utils/HttpClient.java rename to common/src/main/java/me/lucko/luckperms/common/utils/web/HttpClient.java index 2c069974..a2ebbf29 100644 --- a/common/src/main/java/me/lucko/luckperms/common/utils/HttpClient.java +++ b/common/src/main/java/me/lucko/luckperms/common/utils/web/HttpClient.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.utils; +package me.lucko.luckperms.common.utils.web; import okhttp3.Interceptor; import okhttp3.OkHttpClient; diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/web/Pastebin.java b/common/src/main/java/me/lucko/luckperms/common/utils/web/Pastebin.java new file mode 100644 index 00000000..a7f99de3 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/utils/web/Pastebin.java @@ -0,0 +1,43 @@ +/* + * 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.utils.web; + +import com.google.gson.JsonElement; + +public interface Pastebin { + + Paste postJson(JsonElement element); + + Paste postPlain(String content); + + String getRawUrl(String id); + + interface Paste { + String url(); + String id(); + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/web/StandardPastebin.java b/common/src/main/java/me/lucko/luckperms/common/utils/web/StandardPastebin.java new file mode 100644 index 00000000..5d11d999 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/utils/web/StandardPastebin.java @@ -0,0 +1,161 @@ +/* + * 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.utils.web; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import okhttp3.MediaType; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; + +public enum StandardPastebin implements Pastebin { + + BYTEBIN { + public static final String URL = "https://bytebin.lucko.me/"; + private static final String POST_URL = URL + "post"; + + @Override + public String getPostUrl() { + return POST_URL; + } + + @Override + protected String parseIdFromResult(BufferedReader reader) { + JsonObject object = GSON.fromJson(reader, JsonObject.class); + return object.get("key").getAsString(); + } + + @Override + public String getRawUrl(String id) { + return URL + id; + } + }, + + HASTEBIN { + private static final String URL = "https://hastebin.com/"; + private static final String RAW_URL = URL + "raw/"; + private static final String POST_URL = URL + "documents"; + + @Override + public String getPostUrl() { + return POST_URL; + } + + @Override + protected String parseIdFromResult(BufferedReader reader) { + JsonObject object = GSON.fromJson(reader, JsonObject.class); + return object.get("key").getAsString(); + } + + @Override + public String getRawUrl(String id) { + return RAW_URL + id; + } + }; + + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); + private static final MediaType JSON_TYPE = MediaType.parse("application/json; charset=utf-8"); + private static final MediaType PLAIN_TYPE = MediaType.parse("text/plain; charset=utf-8"); + + protected abstract String getPostUrl(); + protected abstract String parseIdFromResult(BufferedReader reader); + + @Override + public Pastebin.Paste postJson(JsonElement content) { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(bytes))) { + GSON.toJson(content, writer); + } catch (IOException e) { + throw new RuntimeException(e); + } + + return post(RequestBody.create(JSON_TYPE, bytes.toByteArray())); + } + + @Override + public Pastebin.Paste postPlain(String content) { + return post(RequestBody.create(PLAIN_TYPE, content)); + } + + private Pastebin.Paste post(RequestBody body) { + Request request = new Request.Builder() + .url(getPostUrl()) + .post(body) + .build(); + + try (Response response = HttpClient.makeCall(request)) { + try (ResponseBody responseBody = response.body()) { + if (responseBody == null) { + throw new RuntimeException("No response"); + } + + try (InputStream inputStream = responseBody.byteStream()) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { + String id = parseIdFromResult(reader); + String url = getRawUrl(id); + return new Paste(url, id); + } + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static final class Paste implements Pastebin.Paste { + private final String url; + private final String id; + + private Paste(String url, String id) { + this.url = url; + this.id = id; + } + + @Override + public String url() { + return this.url; + } + + @Override + public String id() { + return this.id; + } + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/CheckData.java b/common/src/main/java/me/lucko/luckperms/common/verbose/CheckData.java index 093a31e9..dec98c7b 100644 --- a/common/src/main/java/me/lucko/luckperms/common/verbose/CheckData.java +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/CheckData.java @@ -25,8 +25,15 @@ package me.lucko.luckperms.common.verbose; +import com.google.gson.JsonObject; + import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.context.ImmutableContextSet; +import me.lucko.luckperms.common.utils.StackTracePrinter; +import me.lucko.luckperms.common.utils.gson.JArray; +import me.lucko.luckperms.common.utils.gson.JObject; + +import java.util.Map; /** * Holds the data from a permission check @@ -95,4 +102,33 @@ public class CheckData { public Tristate getResult() { return this.result; } + + private JObject formBaseJson() { + return new JObject() + .add("who", new JObject() + .add("identifier", this.checkTarget) + ) + .add("permission", this.permission) + .add("result", this.result.name().toLowerCase()) + .add("origin", this.checkOrigin.name().toLowerCase()) + .add("context", new JArray() + .consume(arr -> { + for (Map.Entry contextPair : this.checkContext.toSet()) { + arr.add(new JObject().add("key", contextPair.getKey()).add("value", contextPair.getValue())); + } + }) + ); + } + + public JsonObject toJson() { + return formBaseJson().toJson(); + } + + public JsonObject toJson(StackTracePrinter tracePrinter) { + return formBaseJson() + .add("trace", new JArray() + .consume(arr -> tracePrinter.process(this.checkTrace, StackTracePrinter.elementToString(arr::add))) + ) + .toJson(); + } } diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseHandler.java b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseHandler.java index 1de2cbab..06b0ce02 100644 --- a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseHandler.java +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseHandler.java @@ -40,7 +40,6 @@ import java.util.concurrent.Executor; * Accepts {@link CheckData} and passes it onto registered {@link VerboseListener}s. */ public class VerboseHandler implements Runnable { - private final String pluginVersion; // the listeners currently registered private final Map listeners; @@ -54,8 +53,7 @@ public class VerboseHandler implements Runnable { // if the handler should shutdown private boolean shutdown = false; - public VerboseHandler(Executor executor, String pluginVersion) { - this.pluginVersion = "v" + pluginVersion; + public VerboseHandler(Executor executor) { this.listeners = new ConcurrentHashMap<>(); this.queue = new ConcurrentLinkedQueue<>(); @@ -95,7 +93,7 @@ public class VerboseHandler implements Runnable { * @param notify if the sender should be notified in chat on each check */ public void registerListener(Sender sender, VerboseFilter filter, boolean notify) { - this.listeners.put(sender.getUuid(), new VerboseListener(this.pluginVersion, sender, filter, notify)); + this.listeners.put(sender.getUuid(), new VerboseListener(sender, filter, notify)); this.listening = true; } diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java index 32a244e6..75146fa8 100644 --- a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java @@ -25,7 +25,7 @@ package me.lucko.luckperms.common.verbose; -import com.google.common.collect.ImmutableList; +import com.google.gson.JsonObject; import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.common.commands.CommandManager; @@ -33,8 +33,11 @@ import me.lucko.luckperms.common.commands.sender.Sender; import me.lucko.luckperms.common.commands.utils.CommandUtils; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.utils.DateUtil; -import me.lucko.luckperms.common.utils.Gist; +import me.lucko.luckperms.common.utils.StackTracePrinter; import me.lucko.luckperms.common.utils.TextUtils; +import me.lucko.luckperms.common.utils.gson.JArray; +import me.lucko.luckperms.common.utils.gson.JObject; +import me.lucko.luckperms.common.utils.web.StandardPastebin; import net.kyori.text.TextComponent; import net.kyori.text.event.HoverEvent; @@ -45,7 +48,6 @@ import java.util.Date; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; -import java.util.stream.Collectors; /** * Accepts and processes {@link CheckData}, passed from the {@link VerboseHandler}. @@ -55,17 +57,36 @@ public class VerboseListener { // how much data should we store before stopping. private static final int DATA_TRUNCATION = 10000; - // how many traces should we add - private static final int TRACE_DATA_TRUNCATION = 250; // how many lines should we include in each stack trace send as a chat message private static final int STACK_TRUNCATION_CHAT = 15; // how many lines should we include in each stack trace in the web output private static final int STACK_TRUNCATION_WEB = 30; + private static final StackTracePrinter FILTERING_PRINTER = StackTracePrinter.builder() + .ignoreClassStartingWith("me.lucko.luckperms.") + .ignoreClassStartingWith("com.github.benmanes.caffeine") + .ignoreClass("java.util.concurrent.CompletableFuture") + .ignoreClass("java.util.concurrent.ConcurrentHashMap") + .build(); + + private static final StackTracePrinter CHAT_FILTERED_PRINTER = FILTERING_PRINTER.toBuilder() + .truncateLength(STACK_TRUNCATION_CHAT) + .build(); + + private static final StackTracePrinter CHAT_UNFILTERED_PRINTER = StackTracePrinter.builder() + .truncateLength(STACK_TRUNCATION_CHAT) + .build(); + + private static final StackTracePrinter WEB_FILTERED_PRINTER = FILTERING_PRINTER.toBuilder() + .truncateLength(STACK_TRUNCATION_WEB) + .build(); + + private static final StackTracePrinter WEB_UNFILTERED_PRINTER = StackTracePrinter.builder() + .truncateLength(STACK_TRUNCATION_WEB) + .build(); + // the time when the listener was first registered private final long startTime = System.currentTimeMillis(); - // the version of the plugin. (used when we paste data to gist) - private final String pluginVersion; // the sender to notify each time the listener processes a check which passes the filter private final Sender notifiedSender; // the filter @@ -80,8 +101,7 @@ public class VerboseListener { // the checks which passed the filter, up to a max size of #DATA_TRUNCATION private final List results = new ArrayList<>(DATA_TRUNCATION / 10); - public VerboseListener(String pluginVersion, Sender notifiedSender, VerboseFilter filter, boolean notify) { - this.pluginVersion = pluginVersion; + public VerboseListener(Sender notifiedSender, VerboseFilter filter, boolean notify) { this.notifiedSender = notifiedSender; this.filter = filter; this.notify = notify; @@ -132,7 +152,13 @@ public class VerboseListener { hover.add("&bContext: &r" + CommandUtils.contextSetToString(data.getCheckContext())); hover.add("&bTrace: &r"); - int overflow = readStack(data, STACK_TRUNCATION_CHAT, e -> hover.add("&7" + e.getClassName() + "." + e.getMethodName() + (e.getLineNumber() >= 0 ? ":" + e.getLineNumber() : ""))); + Consumer printer = StackTracePrinter.elementToString(str -> hover.add("&7" + str)); + int overflow; + if (data.getCheckOrigin() == CheckOrigin.API || data.getCheckOrigin() == CheckOrigin.INTERNAL) { + overflow = CHAT_UNFILTERED_PRINTER.process(data.getCheckTrace(), printer); + } else { + overflow = CHAT_FILTERED_PRINTER.process(data.getCheckTrace(), printer); + } if (overflow != 0) { hover.add("&f... and " + overflow + " more"); } @@ -146,11 +172,9 @@ public class VerboseListener { /** * Uploads the captured data in this listener to a paste and returns the url * - * @param showTraces if stack traces should be included in the output - * @param attachRaw if the rawdata should be attached to the gist * @return the url */ - public String uploadPasteData(boolean showTraces, boolean attachRaw) { + public String uploadPasteData() { // retrieve variables long now = System.currentTimeMillis(); @@ -163,148 +187,42 @@ public class VerboseListener { if (this.filter.isBlank()){ filter = "any"; } else { - filter = "`" + this.filter.toString() + "`"; + filter = this.filter.toString(); } - // start building the message output - ImmutableList.Builder prettyOutput = ImmutableList.builder() - .add("## Verbose Checking Output") - .add("#### This file was automatically generated by [LuckPerms](https://github.com/lucko/LuckPerms) " + this.pluginVersion) - .add("") - .add("### Metadata") - .add("| Key | Value |") - .add("|-----|-------|") - .add("| Start Time | " + startDate + " |") - .add("| End Time | " + endDate + " |") - .add("| Duration | " + duration +" |") - .add("| Count | **" + this.matchedCounter.get() + "** / " + this.counter.get() + " |") - .add("| User | " + this.notifiedSender.getNameWithLocation() + " |") - .add("| Filter | " + filter + " |") - .add("| Include traces | " + showTraces + " |") - .add(""); + boolean truncated = this.matchedCounter.get() > this.results.size(); - // warn if data was truncated - if (this.matchedCounter.get() > this.results.size()) { - prettyOutput.add("**WARN:** Result set exceeded max size of " + DATA_TRUNCATION + ". The output below was truncated to " + DATA_TRUNCATION + " entries."); - prettyOutput.add(""); - } - - // explain why some traces may be missing - if (showTraces && this.results.size() > TRACE_DATA_TRUNCATION) { - prettyOutput.add("**WARN:** Result set exceeded size of " + TRACE_DATA_TRUNCATION + ". The traced output below was truncated to " + TRACE_DATA_TRUNCATION + " entries. "); - prettyOutput.add("Either refine the query using a more specific filter, or disable tracing by adding '--slim' to the end of the paste command."); - prettyOutput.add(""); - } - - // print the format of the output - prettyOutput.add("### Output") - .add("Format: `` `` ``") - .add("") - .add("___") - .add(""); - - // build the csv output - will only be appended to if this is enabled. - ImmutableList.Builder csvOutput = ImmutableList.builder() - .add("User,Permission,Result"); - - // how many instances have been printed so far - AtomicInteger printedCount = new AtomicInteger(0); + JObject metadata = new JObject() + .add("startTime", startDate) + .add("endTime", endDate) + .add("duration", duration) + .add("count", new JObject() + .add("matched", this.matchedCounter.get()) + .add("total", this.counter.get()) + ) + .add("uploader", new JObject() + .add("name", this.notifiedSender.getNameWithLocation()) + .add("uuid", this.notifiedSender.getUuid().toString()) + ) + .add("filter", filter) + .add("truncated", truncated); + JArray data = new JArray(); for (CheckData c : this.results) { - if (!showTraces) { - - // if traces aren't being shown, just append using raw markdown - prettyOutput.add("`" + c.getCheckTarget() + "` - " + c.getPermission() + " - " + getTristateSymbol(c.getResult()) + " "); - - } else if (printedCount.incrementAndGet() > TRACE_DATA_TRUNCATION) { - - // if we've gone over the trace truncation, just append the raw info. - // we still have to use html, as the rest of this section is still using it. - prettyOutput.add("
" + c.getCheckTarget() + " - " + c.getPermission() + " - " + getTristateSymbol(c.getResult())); - + if (c.getCheckOrigin() == CheckOrigin.API || c.getCheckOrigin() == CheckOrigin.INTERNAL) { + data.add(c.toJson(WEB_UNFILTERED_PRINTER)); } else { - - // append the full output. - prettyOutput.add("
" + c.getCheckTarget() + " - " + c.getPermission() + " - " + getTristateSymbol(c.getResult()) + "

"); - - // append the spoiler text - prettyOutput.add("
Origin: " + c.getCheckOrigin().name() + ""); - prettyOutput.add("
Context: " + CommandUtils.stripColor(CommandUtils.contextSetToString(c.getCheckContext())) + ""); - prettyOutput.add("
Trace:

");
-
-                int overflow = readStack(c, STACK_TRUNCATION_WEB, e -> prettyOutput.add(e.getClassName() + "." + e.getMethodName() + (e.getLineNumber() >= 0 ? ":" + e.getLineNumber() : "")));
-                if (overflow != 0) {
-                    prettyOutput.add("... and " + overflow + " more");
-                }
-
-                prettyOutput.add("

"); - } - - // if we're including a raw csv output, append that too - if (attachRaw) { - csvOutput.add(escapeCommas(c.getCheckTarget()) + "," + escapeCommas(c.getPermission()) + "," + c.getResult().name().toLowerCase()); + data.add(c.toJson(WEB_FILTERED_PRINTER)); } } this.results.clear(); - Gist.Builder gist = Gist.builder() - .description("LuckPerms Verbose Checking Output") - .file("luckperms-verbose.md", prettyOutput.build().stream().collect(Collectors.joining("\n"))); + JsonObject payload = new JObject() + .add("metadata", metadata) + .add("data", data) + .toJson(); - if (attachRaw) { - gist.file("raw-data.csv", csvOutput.build().stream().collect(Collectors.joining("\n"))); - } - - return gist.upload().getUrl(); - } - - /** - * Reads a stack trace from a {@link CheckData} instance. - * - * @param data the data to read from - * @param truncateLength the length when we should stop reading the stack - * @param consumer the element consumer - * @return how many elements were left unread, or 0 if everything was read - */ - private static int readStack(CheckData data, int truncateLength, Consumer consumer) { - StackTraceElement[] stack = data.getCheckTrace(); - - // how many lines have been printed - int count = 0; - // if we're printing elements yet - boolean printing = false; - - for (StackTraceElement e : stack) { - // start printing when we escape LP internals code - boolean shouldStartPrinting = !printing && ( - (data.getCheckOrigin() == CheckOrigin.API || data.getCheckOrigin() == CheckOrigin.INTERNAL) || ( - !e.getClassName().startsWith("me.lucko.luckperms.") && - // all used within the checking impl somewhere - !e.getClassName().equals("java.util.concurrent.CompletableFuture") && - !e.getClassName().startsWith("com.github.benmanes.caffeine") && - !e.getClassName().equals("java.util.concurrent.ConcurrentHashMap") - ) - ); - - if (shouldStartPrinting) { - printing = true; - } - - if (!printing) continue; - if (count >= truncateLength) break; - - consumer.accept(e); - count++; - } - - if (stack.length > truncateLength) { - return stack.length - truncateLength; - } - return 0; - } - - private static String escapeCommas(String s) { - return s.contains(",") ? "\"" + s + "\"" : s; + return StandardPastebin.BYTEBIN.postJson(payload).id(); } private static String getTristateColor(Tristate tristate) { @@ -318,17 +236,6 @@ public class VerboseListener { } } - private static String getTristateSymbol(Tristate tristate) { - switch (tristate) { - case TRUE: - return "✔️"; - case FALSE: - return "❌"; - default: - return "❔"; - } - } - public Sender getNotifiedSender() { return this.notifiedSender; } diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditor.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditor.java index f45a7435..e01220ce 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditor.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditor.java @@ -30,7 +30,6 @@ import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.common.commands.sender.Sender; @@ -41,9 +40,11 @@ import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.node.NodeModel; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; -import me.lucko.luckperms.common.utils.Gist; -import me.lucko.luckperms.common.utils.HttpClient; import me.lucko.luckperms.common.utils.Uuids; +import me.lucko.luckperms.common.utils.gson.JArray; +import me.lucko.luckperms.common.utils.gson.JObject; +import me.lucko.luckperms.common.utils.web.HttpClient; +import me.lucko.luckperms.common.utils.web.StandardPastebin; import okhttp3.Request; import okhttp3.Response; @@ -66,54 +67,49 @@ import java.util.stream.Stream; public final class WebEditor { private static final Gson GSON = new Gson(); - private static final String FILE_NAME = "luckperms-data.json"; - private static final String GIST_API_URL = "https://api.github.com/gists"; - private static final String USER_ID_PATTERN = "user/"; private static final String GROUP_ID_PATTERN = "group/"; - private static void writeData(PermissionHolder holder, JsonObject payload) { - payload.addProperty("who", getHolderIdentifier(holder)); - payload.addProperty("whoFriendly", holder.getFriendlyName()); - if (holder.getType().isUser()) { - payload.addProperty("whoUuid", ((User) holder).getUuid().toString()); - } - - // attach the holders permissions - payload.add("nodes", serializePermissions(holder.getEnduringNodes().values().stream().map(NodeModel::fromNode))); + private static JObject writeData(PermissionHolder holder) { + return new JObject() + .add("who", new JObject() + .add("id", getHolderIdentifier(holder)) + .add("friendly", holder.getFriendlyName()) + .consume(obj -> { + if (holder.getType().isUser()) { + obj.add("uuid", ((User) holder).getUuid().toString()); + } + })) + .add("nodes", serializePermissions(holder.getEnduringNodes().values().stream().map(NodeModel::fromNode))); } public static JsonObject formPayload(List holders, Sender sender, String cmdLabel, LuckPermsPlugin plugin) { Preconditions.checkArgument(!holders.isEmpty(), "holders is empty"); // form the payload data - JsonObject payload = new JsonObject(); - - payload.addProperty("cmdAlias", cmdLabel); - payload.addProperty("uploadedBy", sender.getNameWithLocation()); - payload.addProperty("uploadedByUuid", sender.getUuid().toString()); - payload.addProperty("time", System.currentTimeMillis()); - - if (holders.size() == 1) { - writeData(holders.get(0), payload); - } else { - JsonArray tabs = new JsonArray(); - for (PermissionHolder holder : holders) { - JsonObject o = new JsonObject(); - writeData(holder, o); - tabs.add(o); - } - payload.add("tabs", tabs); - } - - // attach an array of all permissions known to the server, to use for tab completion in the editor - JsonArray knownPermsArray = new JsonArray(); - for (String perm : plugin.getPermissionVault().rootAsList()) { - knownPermsArray.add(new JsonPrimitive(perm)); - } - payload.add("knownPermissions", knownPermsArray); - - return payload; + return new JObject() + .add("metadata", new JObject() + .add("cmdAlias", cmdLabel) + .add("uploader", new JObject() + .add("name", sender.getNameWithLocation()) + .add("uuid", sender.getUuid().toString()) + ) + .add("time", System.currentTimeMillis()) + ) + .add("sessions", new JArray() + .consume(arr -> { + for (PermissionHolder holder : holders) { + arr.add(writeData(holder)); + } + }) + ) + .add("knownPermissions", new JArray() + .consume(arr -> { + for (String perm : plugin.getPermissionVault().rootAsList()) { + arr.add(perm); + } + }) + ).toJson(); } private static String getHolderIdentifier(PermissionHolder holder) { @@ -152,19 +148,9 @@ public final class WebEditor { } } - public static String postToGist(String content) { - Gist gist = Gist.builder() - .description("LuckPerms Web Editor Data") - .shorten(false) - .file(FILE_NAME, content) - .upload(); - - return gist.getId(); - } - public static JsonObject getDataFromGist(String id) { Request request = new Request.Builder() - .url(GIST_API_URL + "/" + id) + .url(StandardPastebin.BYTEBIN.getRawUrl(id)) .build(); try (Response response = HttpClient.makeCall(request)) { @@ -175,29 +161,7 @@ public final class WebEditor { try (InputStream inputStream = responseBody.byteStream()) { try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { - JsonObject object = new Gson().fromJson(reader, JsonObject.class); - JsonObject files = object.get("files").getAsJsonObject(); - JsonObject permsFile = files.get(FILE_NAME).getAsJsonObject(); - - // uh.. - if (permsFile.get("truncated").getAsBoolean()) { - try (Response rawResponse = HttpClient.makeCall(new Request.Builder().url(permsFile.get("raw_url").getAsString()).build())) { - try (ResponseBody rawResponseBody = rawResponse.body()) { - if (rawResponseBody == null) { - throw new RuntimeException("No response"); - } - - try (InputStream rawInputStream = rawResponseBody.byteStream()) { - try (BufferedReader rawReader = new BufferedReader(new InputStreamReader(rawInputStream, StandardCharsets.UTF_8))) { - return GSON.fromJson(rawReader, JsonObject.class); - } - } - } - } - } else { - String content = permsFile.get("content").getAsString(); - return GSON.fromJson(content, JsonObject.class); - } + return GSON.fromJson(reader, JsonObject.class); } } } diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/LPNukkitPlugin.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/LPNukkitPlugin.java index 60fbfeb7..b6ea154c 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/LPNukkitPlugin.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/LPNukkitPlugin.java @@ -103,8 +103,6 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; -import javax.annotation.Nullable; - /** * LuckPerms implementation for the Nukkit API. */ @@ -157,7 +155,7 @@ public class LPNukkitPlugin extends PluginBase implements LuckPermsPlugin { public void onEnable() { this.startTime = System.currentTimeMillis(); sendStartupBanner(getConsoleSender()); - this.verboseHandler = new VerboseHandler(this.scheduler.asyncNukkit(), getVersion()); + this.verboseHandler = new VerboseHandler(this.scheduler.asyncNukkit()); this.permissionVault = new PermissionVault(this.scheduler.asyncNukkit()); this.logDispatcher = new LogDispatcher(this); @@ -469,14 +467,13 @@ public class LPNukkitPlugin extends PluginBase implements LuckPermsPlugin { return Optional.empty(); } - @Nullable @Override - public Contexts getContextForUser(User user) { + public Optional getContextForUser(User user) { Player player = getPlayer(user); if (player == null) { - return null; + return Optional.empty(); } - return this.contextManager.getApplicableContexts(player); + return Optional.of(this.contextManager.getApplicableContexts(player)); } @Override diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java index 2371da2b..20c5b365 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java @@ -123,8 +123,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.stream.Stream; -import javax.annotation.Nullable; - /** * LuckPerms implementation for the Sponge API. */ @@ -203,7 +201,7 @@ public class LPSpongePlugin implements LuckPermsSpongePlugin { this.dependencyManager.loadDependencies(DependencyRegistry.GLOBAL_DEPENDENCIES); sendStartupBanner(getConsoleSender()); - this.verboseHandler = new VerboseHandler(this.scheduler.async(), getVersion()); + this.verboseHandler = new VerboseHandler(this.scheduler.async()); this.permissionVault = new PermissionVault(this.scheduler.async()); this.logDispatcher = new LogDispatcher(this); @@ -429,14 +427,13 @@ public class LPSpongePlugin implements LuckPermsSpongePlugin { } } - @Nullable @Override - public Contexts getContextForUser(User user) { + public Optional getContextForUser(User user) { Player player = getPlayer(user); if (player == null) { - return null; + return Optional.empty(); } - return this.contextManager.getApplicableContexts(player); + return Optional.of(this.contextManager.getApplicableContexts(player)); } @Override