From 9bc8a61e2dd8035e75ca9a0a0ffe8a02af6d8f67 Mon Sep 17 00:00:00 2001 From: Luck Date: Sat, 22 Feb 2025 15:57:44 +0000 Subject: [PATCH] Add file secret config adapter (#3913) --- .../adapter/FileSecretConfigAdapter.java | 90 +++++++++++++++++++ .../plugin/AbstractLuckPermsPlugin.java | 2 + .../config/FileSecretConfigAdapterTest.java | 76 ++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/FileSecretConfigAdapter.java create mode 100644 common/src/test/java/me/lucko/luckperms/common/config/FileSecretConfigAdapterTest.java diff --git a/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/FileSecretConfigAdapter.java b/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/FileSecretConfigAdapter.java new file mode 100644 index 000000000..b73fde0b9 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/FileSecretConfigAdapter.java @@ -0,0 +1,90 @@ +/* + * 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.config.generic.adapter; + +import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Locale; + +public class FileSecretConfigAdapter extends StringBasedConfigurationAdapter { + private static final String PREFIX = "luckperms_"; + + private final LuckPermsPlugin plugin; + private final Path directory; + + public FileSecretConfigAdapter(LuckPermsPlugin plugin, String directory) { + this.plugin = plugin; + this.directory = directory == null ? null : Paths.get(directory); + } + + public FileSecretConfigAdapter(LuckPermsPlugin plugin) { + this(plugin, System.getenv("LUCKPERMS_FILE_SECRET_DIRECTORY")); + } + + @Override + protected @Nullable String resolveValue(String path) { + if (this.directory == null) { + return null; // not configured + } + + // e.g. + // 'server' -> luckperms_server + // 'data.table_prefix' -> luckperms_data_table_prefix + String key = PREFIX + path.toLowerCase(Locale.ROOT) + .replace('-', '_') + .replace('.', '_'); + + Path resolvedFile = this.directory.resolve(key); + + String value; + try { + value = new String(Files.readAllBytes(resolvedFile), StandardCharsets.UTF_8); + } catch (IOException e) { + return null; + } + + String printableValue = ConfigKeys.shouldCensorValue(path) ? "*****" : value; + this.plugin.getLogger().info(String.format("Resolved configuration value from file secret: %s = %s", key, printableValue)); + return value; + } + + @Override + public LuckPermsPlugin getPlugin() { + return this.plugin; + } + + @Override + public void reload() { + // no-op + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java index dd2e25c12..1e5d747ef 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java @@ -33,6 +33,7 @@ import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.config.LuckPermsConfiguration; import me.lucko.luckperms.common.config.generic.adapter.ConfigurationAdapter; import me.lucko.luckperms.common.config.generic.adapter.EnvironmentVariableConfigAdapter; +import me.lucko.luckperms.common.config.generic.adapter.FileSecretConfigAdapter; import me.lucko.luckperms.common.config.generic.adapter.MultiConfigurationAdapter; import me.lucko.luckperms.common.config.generic.adapter.SystemPropertyConfigAdapter; import me.lucko.luckperms.common.context.calculator.ConfigurationContextCalculator; @@ -147,6 +148,7 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin { getLogger().info("Loading configuration..."); ConfigurationAdapter configFileAdapter = provideConfigurationAdapter(); this.configuration = new LuckPermsConfiguration(this, new MultiConfigurationAdapter(this, + new FileSecretConfigAdapter(this), new SystemPropertyConfigAdapter(this), new EnvironmentVariableConfigAdapter(this), configFileAdapter diff --git a/common/src/test/java/me/lucko/luckperms/common/config/FileSecretConfigAdapterTest.java b/common/src/test/java/me/lucko/luckperms/common/config/FileSecretConfigAdapterTest.java new file mode 100644 index 000000000..5cb0677cd --- /dev/null +++ b/common/src/test/java/me/lucko/luckperms/common/config/FileSecretConfigAdapterTest.java @@ -0,0 +1,76 @@ +/* + * 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.config; + +import me.lucko.luckperms.common.config.generic.adapter.FileSecretConfigAdapter; +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.plugin.logging.PluginLogger; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.Mockito.lenient; + +@ExtendWith(MockitoExtension.class) +public class FileSecretConfigAdapterTest { + + @Mock private LuckPermsPlugin plugin; + @Mock private PluginLogger logger; + + @BeforeEach + public void setupMocks() { + lenient().when(this.plugin.getLogger()).thenReturn(this.logger); + } + + @Test + public void testRead(@TempDir Path directory) throws IOException { + Path path = directory.resolve("luckperms_server"); + Files.write(path, "test".getBytes()); + + FileSecretConfigAdapter adapter = new FileSecretConfigAdapter(this.plugin, directory.toString()); + + String server = adapter.getString("server", null); + assertEquals("test", server); + } + + @Test + public void testNoOp() throws IOException { + FileSecretConfigAdapter adapter = new FileSecretConfigAdapter(this.plugin, null); + + String server = adapter.getString("server", null); + assertNull(server); + } + +}