From 237019fbf23516a523b595577b2fdc22b2742dbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Br=C3=A4mer?= <22003767+robinbraemer@users.noreply.github.com> Date: Wed, 2 Sep 2026 09:21:20 +0200 Subject: [PATCH] fix(config): default legacy bedrock identity safely --- .../minekube/connect/config/ConfigLoader.java | 19 ++++++- .../connect/config/ConnectConfig.java | 8 +++ .../connect/config/ConfigLoaderTest.java | 50 +++++++++++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/minekube/connect/config/ConfigLoader.java b/core/src/main/java/com/minekube/connect/config/ConfigLoader.java index b53a778bb..d62d7e510 100644 --- a/core/src/main/java/com/minekube/connect/config/ConfigLoader.java +++ b/core/src/main/java/com/minekube/connect/config/ConfigLoader.java @@ -31,11 +31,13 @@ import com.minekube.connect.util.Utils; import java.io.File; import java.io.IOException; +import java.io.Reader; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.time.Duration; +import java.util.Map; import java.util.UUID; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -48,6 +50,8 @@ import org.geysermc.configutils.file.codec.PathFileCodec; import org.geysermc.configutils.file.template.ResourceTemplateReader; import org.geysermc.configutils.updater.change.Changes; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.SafeConstructor; @Getter @RequiredArgsConstructor @@ -65,6 +69,13 @@ private static void fixPlaceholderIssue(Path path) throws IOException { Files.write(path, content.getBytes(charset)); } + private static boolean hasBedrockIdentitySection(Path path) throws IOException { + try (Reader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { + Object document = new Yaml(new SafeConstructor()).load(reader); + return document instanceof Map map && map.containsKey("bedrock-identity"); + } + } + @SuppressWarnings("unchecked") public T load() { String templateFile = "config.yml"; @@ -96,6 +107,8 @@ public T load() { try { // temporary placeholder fix File config = new File(dataFolder.toFile(), "config.yml"); + boolean useMinekubeBedrockIdentityDefaults = config.exists() + && !hasBedrockIdentitySection(config.toPath()); if (config.exists()) { fixPlaceholderIssue(config.toPath()); } else { @@ -104,7 +117,11 @@ public T load() { fixPlaceholderIssue(config.toPath()); // apply fix - return (T) utilities.executeOn(configClass); + T loaded = (T) utilities.executeOn(configClass); + if (useMinekubeBedrockIdentityDefaults) { + loaded.getBedrockIdentity().useMinekubeDefaults(); + } + return loaded; } catch (Throwable throwable) { throw new RuntimeException( "Failed to load the config! Try to delete the config file if this error persists", diff --git a/core/src/main/java/com/minekube/connect/config/ConnectConfig.java b/core/src/main/java/com/minekube/connect/config/ConnectConfig.java index b7631b406..95fa4db7a 100644 --- a/core/src/main/java/com/minekube/connect/config/ConnectConfig.java +++ b/core/src/main/java/com/minekube/connect/config/ConnectConfig.java @@ -100,6 +100,9 @@ public static class MetricsConfig { @Getter public static class BedrockIdentityConfig { + private static final String MINEKUBE_METADATA_URL = + "https://watch-connect.minekube.net/.well-known/minekube-connect/bedrock-identity-keys.json"; + /** * Exact enforcement mode: disabled, warn, or require. Any other value is invalid. */ @@ -133,6 +136,11 @@ public static class BedrockIdentityConfig { * Required exact policy: linked_java_only or trusted_bedrock_xuid. */ private String expectedPolicy = "trusted_bedrock_xuid"; + + void useMinekubeDefaults() { + enforcement = "warn"; + metadataUrl = MINEKUBE_METADATA_URL; + } } @Getter diff --git a/core/src/test/java/com/minekube/connect/config/ConfigLoaderTest.java b/core/src/test/java/com/minekube/connect/config/ConfigLoaderTest.java index daf5aa9c3..e3943390d 100644 --- a/core/src/test/java/com/minekube/connect/config/ConfigLoaderTest.java +++ b/core/src/test/java/com/minekube/connect/config/ConfigLoaderTest.java @@ -74,6 +74,56 @@ void loadsBedrockIdentityEnforcementConfig() throws Exception { assertEquals("trusted_bedrock_xuid", config.getBedrockIdentity().getExpectedPolicy()); } + @Test + void preservesExplicitQuotedBedrockIdentitySection() throws Exception { + Files.writeString(tempDir.resolve("config.yml"), String.join("\n", + "endpoint: codexp2p3", + "allow-offline-mode-players: false", + "\"bedrock-identity\":", + " enforcement: require", + " metadata-url: https://operator.example/bedrock-identity-keys.json", + "metrics:", + " disabled: true", + " uuid: 00000000-0000-0000-0000-000000000000", + "config-version: 1", + "")); + + ConnectConfig config = load(ConnectConfig.class); + + assertEquals("require", config.getBedrockIdentity().getEnforcement()); + assertEquals( + "https://operator.example/bedrock-identity-keys.json", + config.getBedrockIdentity().getMetadataUrl()); + } + + @Test + void legacyConfigWithoutBedrockSectionUsesSafeMinekubeIdentityDefaults() throws Exception { + Files.writeString(tempDir.resolve("config.yml"), String.join("\n", + "endpoint: codexp2p3", + "allow-offline-mode-players: false", + "metrics:", + " disabled: true", + " uuid: 00000000-0000-0000-0000-000000000000", + "config-version: 1", + "")); + + ConnectConfig config = load(ConnectConfig.class); + + assertEquals("warn", config.getBedrockIdentity().getEnforcement()); + assertEquals( + "https://watch-connect.minekube.net/.well-known/minekube-connect/bedrock-identity-keys.json", + config.getBedrockIdentity().getMetadataUrl()); + assertEquals("minekube-connect", config.getBedrockIdentity().getExpectedIssuer()); + assertEquals("trusted_bedrock_xuid", config.getBedrockIdentity().getExpectedPolicy()); + + ConnectConfig reloaded = load(ConnectConfig.class); + assertEquals("warn", reloaded.getBedrockIdentity().getEnforcement(), + "an automatically rewritten legacy config must keep the safe default after restart"); + assertEquals( + "https://watch-connect.minekube.net/.well-known/minekube-connect/bedrock-identity-keys.json", + reloaded.getBedrockIdentity().getMetadataUrl()); + } + /** * The login re-assert is on by default and its full-profile half is off by default, including * for a proxy config written before either key existed.