From 85045e1e4a15e0a5657d189e83dd202a2c37f2b0 Mon Sep 17 00:00:00 2001 From: Yuuta Liang Date: Wed, 13 Jul 2022 11:16:27 -0700 Subject: First Commit Signed-off-by: Trumeet --- src/main/java/moe/ymc/acron/config/Config.java | 30 ++++++ .../java/moe/ymc/acron/config/ConfigReloadCmd.java | 41 ++++++++ .../java/moe/ymc/acron/config/json/Client.java | 45 +++++++++ .../java/moe/ymc/acron/config/json/Config.java | 103 +++++++++++++++++++++ .../json/ConfigDeserializationException.java | 18 ++++ .../ymc/acron/config/json/ConfigDeserializer.java | 27 ++++++ .../ymc/acron/config/json/ConfigJsonObject.java | 7 ++ src/main/java/moe/ymc/acron/config/json/Rule.java | 35 +++++++ 8 files changed, 306 insertions(+) create mode 100644 src/main/java/moe/ymc/acron/config/Config.java create mode 100644 src/main/java/moe/ymc/acron/config/ConfigReloadCmd.java create mode 100644 src/main/java/moe/ymc/acron/config/json/Client.java create mode 100644 src/main/java/moe/ymc/acron/config/json/Config.java create mode 100644 src/main/java/moe/ymc/acron/config/json/ConfigDeserializationException.java create mode 100644 src/main/java/moe/ymc/acron/config/json/ConfigDeserializer.java create mode 100644 src/main/java/moe/ymc/acron/config/json/ConfigJsonObject.java create mode 100644 src/main/java/moe/ymc/acron/config/json/Rule.java (limited to 'src/main/java/moe/ymc/acron/config') diff --git a/src/main/java/moe/ymc/acron/config/Config.java b/src/main/java/moe/ymc/acron/config/Config.java new file mode 100644 index 0000000..3749c25 --- /dev/null +++ b/src/main/java/moe/ymc/acron/config/Config.java @@ -0,0 +1,30 @@ +package moe.ymc.acron.config; + +import moe.ymc.acron.auth.Client; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; + +import java.net.InetAddress; +import java.util.Map; + +public record Config(@NotNull InetAddress address, + int port, + boolean useNativeTransport, + @NotNull Map clients) { + private static final Logger LOGGER = LogManager.getLogger(); + private static Config globalConfig; + + @NotNull + public static Config getGlobalConfig() { + return globalConfig; + } + + public static void setGlobalConfig(@NotNull Config globalConfig) { + Config.globalConfig = globalConfig; + LOGGER.info("Config loaded with {} clients. Listening on {}:{}.", + globalConfig.clients.size(), + globalConfig.address.toString(), + globalConfig.port); + } +} \ No newline at end of file diff --git a/src/main/java/moe/ymc/acron/config/ConfigReloadCmd.java b/src/main/java/moe/ymc/acron/config/ConfigReloadCmd.java new file mode 100644 index 0000000..2774c4d --- /dev/null +++ b/src/main/java/moe/ymc/acron/config/ConfigReloadCmd.java @@ -0,0 +1,41 @@ +package moe.ymc.acron.config; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import moe.ymc.acron.config.json.ConfigDeserializationException; +import moe.ymc.acron.config.json.ConfigDeserializer; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.LiteralText; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.nio.file.Path; + +public class ConfigReloadCmd implements Command { + private static final Logger LOGGER = LogManager.getLogger(); + + @Override + public int run(CommandContext context) throws CommandSyntaxException { + LOGGER.info("Reloading rules."); + try { + final Path config = FabricLoader + .getInstance().getConfigDir() + .resolve("acron.json"); + if (!config.toFile().exists()) { + throw new IllegalStateException("Cannot find config/acron.json."); + } + final Config cfg = ConfigDeserializer.deserialize(config.toFile(), false); + Config.setGlobalConfig(cfg); + context.getSource().sendFeedback(new LiteralText("Rules reloaded."), true); + return 0; + } catch (IOException | ConfigDeserializationException e) { + LOGGER.error("Cannot reload config.", e); + context.getSource().sendError(new LiteralText("Cannot reload rules: " + + e.getMessage())); + return 1; + } + } +} diff --git a/src/main/java/moe/ymc/acron/config/json/Client.java b/src/main/java/moe/ymc/acron/config/json/Client.java new file mode 100644 index 0000000..4d31308 --- /dev/null +++ b/src/main/java/moe/ymc/acron/config/json/Client.java @@ -0,0 +1,45 @@ +package moe.ymc.acron.config.json; + +import com.google.gson.annotations.SerializedName; +import moe.ymc.acron.auth.Action; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +class Client implements ConfigJsonObject { + @SerializedName("id") + private final String id; + + @SerializedName("token") + private final String token; + + @SerializedName("policy_mode") + private final Action policyMode; + + @SerializedName("rules") + private final List rules; + + private Client(String id, + String token, + Action policyMode, + List rules) { + this.id = id; + this.token = token; + this.policyMode = policyMode; + this.rules = rules; + } + + @Override + public @NotNull moe.ymc.acron.auth.Client create(boolean startup) throws ConfigDeserializationException { + if (id == null || id.trim().equals("") || + token == null || token.trim().equals("")) { + throw new ConfigDeserializationException(".clients[].id or .clients[].token is not supplied."); + } + return new moe.ymc.acron.auth.Client(id, + token, + policyMode == null ? Action.DENY : policyMode, + rules == null ? new moe.ymc.acron.auth.Rule[]{} : + rules.stream().map(rule -> rule.create(startup)).toList() + .toArray(new moe.ymc.acron.auth.Rule[rules.size()])); + } +} diff --git a/src/main/java/moe/ymc/acron/config/json/Config.java b/src/main/java/moe/ymc/acron/config/json/Config.java new file mode 100644 index 0000000..e8c5a83 --- /dev/null +++ b/src/main/java/moe/ymc/acron/config/json/Config.java @@ -0,0 +1,103 @@ +package moe.ymc.acron.config.json; + +import com.google.gson.annotations.SerializedName; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +class Config implements ConfigJsonObject { + private static final Logger LOGGER = LogManager.getLogger(); + + @SerializedName("listen") + private final String listen; + + @SerializedName("port") + private final Integer port; + + @SerializedName("native_transport") + private final boolean nativeTransport; + + @SerializedName("clients") + private final List clients; + + private Config(String listen, + Integer port, + boolean nativeTransport, + List clients) { + this.listen = listen; + this.port = port; + this.nativeTransport = nativeTransport; + this.clients = clients; + } + + @Override + public @NotNull moe.ymc.acron.config.Config create(boolean startup) throws ConfigDeserializationException { + final InetAddress address; + final int p; + final boolean nt; + if (!startup) { + address = moe.ymc.acron.config.Config.getGlobalConfig().address(); + p = moe.ymc.acron.config.Config.getGlobalConfig().port(); + nt = moe.ymc.acron.config.Config.getGlobalConfig().useNativeTransport(); + } else { + if (listen == null || listen.trim().equals("")) { + address = InetAddress.getLoopbackAddress(); + } else { + try { + address = InetAddress.getByName(listen); + } catch (UnknownHostException e) { + throw new ConfigDeserializationException("Cannot parse address: " + e.getMessage(), + true); + } + } + if (port == null) { + p = 25575; + } else { + if (port < 0 || port > 65535) { + throw new ConfigDeserializationException("The port is out of range.", true); + } + p = port; + } + nt = nativeTransport; + } + + + Map map; + try { + if (clients != null) { + map = clients.stream() + .collect(Collectors. + toMap(client -> client.create(startup).id(), + client -> client.create(startup))); + } else { + map = new HashMap<>(0); + } + } catch (IllegalStateException e) { + // Collision. + LOGGER.error("Duplicate clients with the same ID in the Acron configuration. All clients are ignored. " + + "Fix the configuration and reload.", e); + if (!startup) { + throw new ConfigDeserializationException("Duplicate clients with the same ID: " + e.getMessage()); + } + map = new HashMap<>(0); + } catch (ConfigDeserializationException e) { + LOGGER.error("Cannot parse the Acron configuration. All clients are ignored. " + + "Fix the configuration and reload.", e); + if (!startup) { + throw e; + } + map = new HashMap<>(0); + } + return new moe.ymc.acron.config.Config(address, + p, + nt, + map); + } +} diff --git a/src/main/java/moe/ymc/acron/config/json/ConfigDeserializationException.java b/src/main/java/moe/ymc/acron/config/json/ConfigDeserializationException.java new file mode 100644 index 0000000..baf5b35 --- /dev/null +++ b/src/main/java/moe/ymc/acron/config/json/ConfigDeserializationException.java @@ -0,0 +1,18 @@ +package moe.ymc.acron.config.json; + +public class ConfigDeserializationException extends RuntimeException { + private final boolean fetal; + + public ConfigDeserializationException(String message) { + this(message, false); + } + + public ConfigDeserializationException(String message, boolean fetal) { + super(message); + this.fetal = fetal; + } + + public boolean isFetal() { + return fetal; + } +} diff --git a/src/main/java/moe/ymc/acron/config/json/ConfigDeserializer.java b/src/main/java/moe/ymc/acron/config/json/ConfigDeserializer.java new file mode 100644 index 0000000..e91b355 --- /dev/null +++ b/src/main/java/moe/ymc/acron/config/json/ConfigDeserializer.java @@ -0,0 +1,27 @@ +package moe.ymc.acron.config.json; + +import com.google.gson.Gson; +import moe.ymc.acron.config.Config; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; + +public class ConfigDeserializer { + public static @NotNull Config deserialize(@NotNull File file, boolean startup) + throws ConfigDeserializationException, IOException { + final Reader reader = new FileReader(file); + final moe.ymc.acron.config.json.Config config; + try { + config = new Gson() + .fromJson(reader, moe.ymc.acron.config.json.Config.class); + } catch (Throwable e) { + throw new ConfigDeserializationException("Cannot parse JSON: " + e.getMessage(), + true); + } + reader.close(); + return config.create(startup); + } +} diff --git a/src/main/java/moe/ymc/acron/config/json/ConfigJsonObject.java b/src/main/java/moe/ymc/acron/config/json/ConfigJsonObject.java new file mode 100644 index 0000000..0efd9a9 --- /dev/null +++ b/src/main/java/moe/ymc/acron/config/json/ConfigJsonObject.java @@ -0,0 +1,7 @@ +package moe.ymc.acron.config.json; + +import org.jetbrains.annotations.NotNull; + +public interface ConfigJsonObject { + @NotNull T create(boolean startup) throws ConfigDeserializationException; +} diff --git a/src/main/java/moe/ymc/acron/config/json/Rule.java b/src/main/java/moe/ymc/acron/config/json/Rule.java new file mode 100644 index 0000000..114e17d --- /dev/null +++ b/src/main/java/moe/ymc/acron/config/json/Rule.java @@ -0,0 +1,35 @@ +package moe.ymc.acron.config.json; + +import com.google.gson.annotations.SerializedName; +import moe.ymc.acron.auth.Action; +import org.jetbrains.annotations.NotNull; + +import java.util.regex.Pattern; + +class Rule implements ConfigJsonObject { + @SerializedName("regex") + private final String regex; + + @SerializedName("action") + private final Action action; + + @SerializedName("display") + private final boolean display; + + private Rule(String regex, + Action action, + boolean display) { + this.regex = regex; + this.action = action; + this.display = display; + } + + public @NotNull moe.ymc.acron.auth.Rule create(boolean startup) throws ConfigDeserializationException { + if (regex == null || regex.trim().equals("") || + action == null) throw new ConfigDeserializationException(".clients.[]rules.regex or .clients.[]rules.action is" + + "not specified."); + return new moe.ymc.acron.auth.Rule(Pattern.compile(regex), + action, + display); + } +} -- cgit v1.2.3