diff --git a/core/src/main/java/com/minekube/connect/bedrock/BedrockPrincipalConsumer.java b/core/src/main/java/com/minekube/connect/bedrock/BedrockPrincipalConsumer.java index 8f0e82136..35ead0b0f 100644 --- a/core/src/main/java/com/minekube/connect/bedrock/BedrockPrincipalConsumer.java +++ b/core/src/main/java/com/minekube/connect/bedrock/BedrockPrincipalConsumer.java @@ -54,6 +54,12 @@ public Optional verify(Session session) { if (hasInjectedProperty(session)) { throw new BedrockPrincipalAdmissionException(PrincipalError.BINDING_MISMATCH); } + // Bedrock v2 enforcement is protocol-scoped. Java and legacy unmarked sessions do not + // carry a Bedrock principal envelope and must continue through ordinary admission. + if (session.getProtocol() != SessionProtocol.SESSION_PROTOCOL_BEDROCK + && session.getSignedBedrockPrincipalV2().isEmpty()) { + return Optional.empty(); + } ConnectConfig currentConfig; BedrockPrincipalConfiguration principalConfiguration; try { diff --git a/core/src/test/java/com/minekube/connect/bedrock/BedrockPrincipalConsumerTest.java b/core/src/test/java/com/minekube/connect/bedrock/BedrockPrincipalConsumerTest.java index 79b8fee3c..3e153776c 100644 --- a/core/src/test/java/com/minekube/connect/bedrock/BedrockPrincipalConsumerTest.java +++ b/core/src/test/java/com/minekube/connect/bedrock/BedrockPrincipalConsumerTest.java @@ -8,6 +8,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.protobuf.ByteString; +import com.minekube.connect.api.player.bedrock.BedrockIdentityProfiles; import com.minekube.connect.api.player.principal.PrincipalError; import com.minekube.connect.config.ConnectConfig; import java.io.InputStreamReader; @@ -21,6 +22,7 @@ import java.util.UUID; import minekube.connect.v1alpha1.WatchServiceOuterClass.Authentication; import minekube.connect.v1alpha1.WatchServiceOuterClass.GameProfile; +import minekube.connect.v1alpha1.WatchServiceOuterClass.GameProfileProperty; import minekube.connect.v1alpha1.WatchServiceOuterClass.Player; import minekube.connect.v1alpha1.WatchServiceOuterClass.Session; import minekube.connect.v1alpha1.WatchServiceOuterClass.SessionProtocol; @@ -72,7 +74,7 @@ void malformedCompanionBindingFailsBeforeProfileApplication() throws Exception { } @Test - void requireModeRejectsSessionsMissingV2Principal() throws Exception { + void requireModeRejectsBedrockSessionsMissingV2Principal() throws Exception { JsonObject vector = vector("valid-unlinked"); BedrockPrincipalAdmissionException error = assertThrows( BedrockPrincipalAdmissionException.class, @@ -81,6 +83,61 @@ void requireModeRejectsSessionsMissingV2Principal() throws Exception { assertEquals(PrincipalError.READINESS, error.error()); } + @Test + void requireModeAllowsJavaSessionsWithoutV2Principal() throws Exception { + JsonObject vector = vector("valid-unlinked"); + Session javaSession = session(vector).toBuilder() + .setProtocol(SessionProtocol.SESSION_PROTOCOL_JAVA) + .clearSignedBedrockPrincipalV2() + .build(); + + assertTrue(consumer(vector).verify(javaSession).isEmpty()); + } + + @Test + void requireModeAllowsLegacyUnmarkedSessionsWithoutV2Principal() throws Exception { + JsonObject vector = vector("valid-unlinked"); + Session legacySession = session(vector).toBuilder() + .setProtocol(SessionProtocol.SESSION_PROTOCOL_UNSPECIFIED) + .clearSignedBedrockPrincipalV2() + .build(); + + assertTrue(consumer(vector).verify(legacySession).isEmpty()); + } + + @Test + void javaSessionsCannotCarryV2PrincipalEnvelopes() throws Exception { + JsonObject vector = vector("valid-unlinked"); + Session javaSession = session(vector).toBuilder() + .setProtocol(SessionProtocol.SESSION_PROTOCOL_JAVA) + .build(); + + BedrockPrincipalAdmissionException error = assertThrows( + BedrockPrincipalAdmissionException.class, + () -> consumer(vector).verify(javaSession)); + assertEquals(PrincipalError.BINDING_MISMATCH, error.error()); + } + + @Test + void javaSessionsCannotInjectReservedV2PrincipalProperties() throws Exception { + JsonObject vector = vector("valid-unlinked"); + Session source = session(vector); + Session javaSession = source.toBuilder() + .setProtocol(SessionProtocol.SESSION_PROTOCOL_JAVA) + .clearSignedBedrockPrincipalV2() + .setPlayer(source.getPlayer().toBuilder() + .setProfile(source.getPlayer().getProfile().toBuilder() + .addProperties(GameProfileProperty.newBuilder() + .setName(BedrockIdentityProfiles.PRINCIPAL_V2_PROPERTY_NAME) + .setValue("forged")))) + .build(); + + BedrockPrincipalAdmissionException error = assertThrows( + BedrockPrincipalAdmissionException.class, + () -> consumer(vector).verify(javaSession)); + assertEquals(PrincipalError.BINDING_MISMATCH, error.error()); + } + @Test void oversizedV2EnvelopeIsRejected() throws Exception { JsonObject vector = vector("valid-unlinked");