From 64f6cd9af4f4d1a5b313699eaba1552af4b8e214 Mon Sep 17 00:00:00 2001 From: gbrodman Date: Thu, 8 Jan 2026 17:00:39 -0500 Subject: [PATCH] Only include fee 1.0 extension in nonprod envs (#2927) We need to have this enabled in sandbox, but we wish to wait to enable it for production to make sure that the implementation is correct and that clients can use it. Soon we'll want to do something similar (but the opposite) with the old fee extensions, where we **only** serve them in production (or maybe unit test as well). That will allow us to pass the RST tests that depend on only having the fee extension 1.0. --- .../model/eppcommon/EppXmlTransformer.java | 22 ++++++- .../model/eppcommon/ProtocolDefinition.java | 64 +++++++++++++------ .../google/registry/xml/XmlTransformer.java | 5 +- .../registry/flows/EppLoggedOutTest.java | 3 +- .../eppcommon/EppXmlTransformerTest.java | 23 +++++++ .../google/registry/flows/greeting.xml | 1 + .../registry/flows/session/greeting.xml | 1 + 7 files changed, 92 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/google/registry/model/eppcommon/EppXmlTransformer.java b/core/src/main/java/google/registry/model/eppcommon/EppXmlTransformer.java index 91c64d8e7..165046612 100644 --- a/core/src/main/java/google/registry/model/eppcommon/EppXmlTransformer.java +++ b/core/src/main/java/google/registry/model/eppcommon/EppXmlTransformer.java @@ -14,13 +14,16 @@ package google.registry.model.eppcommon; +import static com.google.common.collect.ImmutableList.toImmutableList; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import google.registry.model.ImmutableObject; import google.registry.model.eppinput.EppInput; import google.registry.model.eppoutput.EppOutput; +import google.registry.util.RegistryEnvironment; import google.registry.xml.ValidationMode; import google.registry.xml.XmlException; import google.registry.xml.XmlTransformer; @@ -31,7 +34,7 @@ import java.io.ByteArrayOutputStream; public class EppXmlTransformer { // Hardcoded XML schemas, ordered with respect to dependency. - private static final ImmutableList SCHEMAS = + private static final ImmutableList ALL_SCHEMAS = ImmutableList.of( "eppcom.xsd", "epp.xsd", @@ -54,11 +57,24 @@ public class EppXmlTransformer { "allocationToken-1.0.xsd", "bulkToken.xsd"); + // XML schemas that should not be used in production (yet) + private static final ImmutableSet NON_PROD_SCHEMAS = ImmutableSet.of("fee-std-v1.xsd"); + private static final XmlTransformer INPUT_TRANSFORMER = - new XmlTransformer(SCHEMAS, EppInput.class); + new XmlTransformer(getSchemas(), EppInput.class); private static final XmlTransformer OUTPUT_TRANSFORMER = - new XmlTransformer(SCHEMAS, EppOutput.class); + new XmlTransformer(getSchemas(), EppOutput.class); + + @VisibleForTesting + public static ImmutableList getSchemas() { + if (RegistryEnvironment.get().equals(RegistryEnvironment.PRODUCTION)) { + return ALL_SCHEMAS.stream() + .filter(s -> !NON_PROD_SCHEMAS.contains(s)) + .collect(toImmutableList()); + } + return ALL_SCHEMAS; + } public static void validateOutput(String xml) throws XmlException { OUTPUT_TRANSFORMER.validate(xml); diff --git a/core/src/main/java/google/registry/model/eppcommon/ProtocolDefinition.java b/core/src/main/java/google/registry/model/eppcommon/ProtocolDefinition.java index ccff560ed..7eeea88e0 100644 --- a/core/src/main/java/google/registry/model/eppcommon/ProtocolDefinition.java +++ b/core/src/main/java/google/registry/model/eppcommon/ProtocolDefinition.java @@ -33,6 +33,7 @@ import google.registry.model.domain.rgp.RgpUpdateExtension; import google.registry.model.domain.secdns.SecDnsCreateExtension; import google.registry.model.eppinput.EppInput.CommandExtension; import google.registry.model.eppoutput.EppResponse.ResponseExtension; +import google.registry.util.RegistryEnvironment; import jakarta.xml.bind.annotation.XmlSchema; import java.util.EnumSet; @@ -48,30 +49,50 @@ public class ProtocolDefinition { "urn:ietf:params:xml:ns:domain-1.0", "urn:ietf:params:xml:ns:contact-1.0"); - /** Enums repesenting valid service extensions that are recognized by the server. */ + /** Enum representing which environments should have which service extensions enabled. */ + private enum ServiceExtensionVisibility { + ALL, + ONLY_IN_PRODUCTION, + ONLY_IN_NON_PRODUCTION, + NONE + } + + /** Enum representing valid service extensions that are recognized by the server. */ public enum ServiceExtension { - LAUNCH_EXTENSION_1_0(LaunchCreateExtension.class, null, true), - REDEMPTION_GRACE_PERIOD_1_0(RgpUpdateExtension.class, null, true), - SECURE_DNS_1_1(SecDnsCreateExtension.class, null, true), - FEE_0_6(FeeCheckCommandExtensionV06.class, FeeCheckResponseExtensionV06.class, true), - FEE_0_11(FeeCheckCommandExtensionV11.class, FeeCheckResponseExtensionV11.class, true), - FEE_0_12(FeeCheckCommandExtensionV12.class, FeeCheckResponseExtensionV12.class, true), - FEE_1_00(FeeCheckCommandExtensionStdV1.class, FeeCheckResponseExtensionStdV1.class, false), - METADATA_1_0(MetadataExtension.class, null, false); + LAUNCH_EXTENSION_1_0(LaunchCreateExtension.class, null, ServiceExtensionVisibility.ALL), + REDEMPTION_GRACE_PERIOD_1_0(RgpUpdateExtension.class, null, ServiceExtensionVisibility.ALL), + SECURE_DNS_1_1(SecDnsCreateExtension.class, null, ServiceExtensionVisibility.ALL), + FEE_0_6( + FeeCheckCommandExtensionV06.class, + FeeCheckResponseExtensionV06.class, + ServiceExtensionVisibility.ALL), + FEE_0_11( + FeeCheckCommandExtensionV11.class, + FeeCheckResponseExtensionV11.class, + ServiceExtensionVisibility.ALL), + FEE_0_12( + FeeCheckCommandExtensionV12.class, + FeeCheckResponseExtensionV12.class, + ServiceExtensionVisibility.ALL), + FEE_1_00( + FeeCheckCommandExtensionStdV1.class, + FeeCheckResponseExtensionStdV1.class, + ServiceExtensionVisibility.ONLY_IN_NON_PRODUCTION), + METADATA_1_0(MetadataExtension.class, null, ServiceExtensionVisibility.NONE); private final Class commandExtensionClass; private final Class responseExtensionClass; private final String uri; - private final boolean visible; + private final ServiceExtensionVisibility visibility; ServiceExtension( Class commandExtensionClass, Class responseExtensionClass, - boolean visible) { + ServiceExtensionVisibility visibility) { this.commandExtensionClass = commandExtensionClass; this.responseExtensionClass = responseExtensionClass; this.uri = getCommandExtensionUri(commandExtensionClass); - this.visible = visible; + this.visibility = visibility; } public Class getCommandExtensionClass() { @@ -86,14 +107,20 @@ public class ProtocolDefinition { return uri; } - public boolean getVisible() { - return visible; - } - /** Returns the namespace URI of the command extension class. */ public static String getCommandExtensionUri(Class clazz) { return clazz.getPackage().getAnnotation(XmlSchema.class).namespace(); } + + private boolean isVisible() { + return switch (visibility) { + case ALL -> true; + case ONLY_IN_PRODUCTION -> RegistryEnvironment.get().equals(RegistryEnvironment.PRODUCTION); + case ONLY_IN_NON_PRODUCTION -> + !RegistryEnvironment.get().equals(RegistryEnvironment.PRODUCTION); + case NONE -> false; + }; + } } /** @@ -111,9 +138,8 @@ public class ProtocolDefinition { /** A set of all the visible extension URIs. */ private static final ImmutableSet visibleServiceExtensionUris = - EnumSet.allOf(ServiceExtension.class) - .stream() - .filter(ServiceExtension::getVisible) + EnumSet.allOf(ServiceExtension.class).stream() + .filter(ServiceExtension::isVisible) .map(ServiceExtension::getUri) .collect(toImmutableSet()); diff --git a/core/src/main/java/google/registry/xml/XmlTransformer.java b/core/src/main/java/google/registry/xml/XmlTransformer.java index 7a04f109f..be2ef1201 100644 --- a/core/src/main/java/google/registry/xml/XmlTransformer.java +++ b/core/src/main/java/google/registry/xml/XmlTransformer.java @@ -38,7 +38,6 @@ import java.io.StringWriter; import java.io.Writer; import java.nio.charset.Charset; import java.util.Collection; -import java.util.List; import java.util.Map; import javax.annotation.Nullable; import javax.xml.XMLConstants; @@ -82,7 +81,7 @@ public class XmlTransformer { * @param schemaFilenames schema files, used only for validating, and relative to this package. * @param recognizedClasses the classes that can be used to marshal to and from */ - public XmlTransformer(List schemaFilenames, Class... recognizedClasses) { + public XmlTransformer(ImmutableList schemaFilenames, Class... recognizedClasses) { try { this.jaxbContext = JAXBContext.newInstance(recognizedClasses); this.schema = loadXmlSchemas(schemaFilenames); @@ -251,7 +250,7 @@ public class XmlTransformer { } /** Creates a single {@link Schema} from multiple {@code .xsd} files. */ - public static Schema loadXmlSchemas(List schemaFilenames) { + public static Schema loadXmlSchemas(ImmutableList schemaFilenames) { try (Closer closer = Closer.create()) { StreamSource[] sources = new StreamSource[schemaFilenames.size()]; for (int i = 0; i < schemaFilenames.size(); ++i) { diff --git a/core/src/test/java/google/registry/flows/EppLoggedOutTest.java b/core/src/test/java/google/registry/flows/EppLoggedOutTest.java index 962e46fcb..119ac1ccf 100644 --- a/core/src/test/java/google/registry/flows/EppLoggedOutTest.java +++ b/core/src/test/java/google/registry/flows/EppLoggedOutTest.java @@ -14,7 +14,6 @@ package google.registry.flows; -import static org.joda.time.DateTimeZone.UTC; import static org.joda.time.format.ISODateTimeFormat.dateTimeNoMillis; import com.google.common.collect.ImmutableMap; @@ -26,7 +25,7 @@ class EppLoggedOutTest extends EppTestCase { @Test void testHello() throws Exception { - DateTime now = DateTime.now(UTC); + DateTime now = clock.nowUtc(); assertThatCommand("hello.xml", null) .atTime(now) .hasResponse("greeting.xml", ImmutableMap.of("DATE", now.toString(dateTimeNoMillis()))); diff --git a/core/src/test/java/google/registry/model/eppcommon/EppXmlTransformerTest.java b/core/src/test/java/google/registry/model/eppcommon/EppXmlTransformerTest.java index 5e10fb7d5..5d77124b7 100644 --- a/core/src/test/java/google/registry/model/eppcommon/EppXmlTransformerTest.java +++ b/core/src/test/java/google/registry/model/eppcommon/EppXmlTransformerTest.java @@ -21,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import google.registry.model.eppinput.EppInput; import google.registry.model.eppoutput.EppOutput; +import google.registry.util.RegistryEnvironment; import org.junit.jupiter.api.Test; /** Tests for {@link EppXmlTransformer}. */ @@ -38,4 +39,26 @@ class EppXmlTransformerTest { ClassCastException.class, () -> unmarshal(EppOutput.class, loadBytes(getClass(), "contact_info.xml").read())); } + + @Test + void testSchemas_inNonProduction_includesFee1Point0() { + var currentEnv = RegistryEnvironment.get(); + try { + RegistryEnvironment.SANDBOX.setup(); + assertThat(EppXmlTransformer.getSchemas()).contains("fee-std-v1.xsd"); + } finally { + currentEnv.setup(); + } + } + + @Test + void testSchemas_inProduction_skipsFee1Point0() { + var currentEnv = RegistryEnvironment.get(); + try { + RegistryEnvironment.PRODUCTION.setup(); + assertThat(EppXmlTransformer.getSchemas()).doesNotContain("fee-std-v1.xsd"); + } finally { + currentEnv.setup(); + } + } } diff --git a/core/src/test/resources/google/registry/flows/greeting.xml b/core/src/test/resources/google/registry/flows/greeting.xml index 3e4d3a54d..f039bfca7 100644 --- a/core/src/test/resources/google/registry/flows/greeting.xml +++ b/core/src/test/resources/google/registry/flows/greeting.xml @@ -15,6 +15,7 @@ urn:ietf:params:xml:ns:fee-0.6 urn:ietf:params:xml:ns:fee-0.11 urn:ietf:params:xml:ns:fee-0.12 + urn:ietf:params:xml:ns:epp:fee-1.0 diff --git a/core/src/test/resources/google/registry/flows/session/greeting.xml b/core/src/test/resources/google/registry/flows/session/greeting.xml index 3e4d3a54d..f039bfca7 100644 --- a/core/src/test/resources/google/registry/flows/session/greeting.xml +++ b/core/src/test/resources/google/registry/flows/session/greeting.xml @@ -15,6 +15,7 @@ urn:ietf:params:xml:ns:fee-0.6 urn:ietf:params:xml:ns:fee-0.11 urn:ietf:params:xml:ns:fee-0.12 + urn:ietf:params:xml:ns:epp:fee-1.0