From 199e3fc72f331b7cb6f7ec6e330ceb5e61f54255 Mon Sep 17 00:00:00 2001 From: Gus Brodman Date: Thu, 8 Jan 2026 14:58:19 -0500 Subject: [PATCH] Only include fee 1.0 extension in nonprod envs 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 91c64d8e747..165046612df 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 @@ 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 ccff560ed6a..7eeea88e0c2 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.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 String getUri() { 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 static ServiceExtension getServiceExtensionFromUri(String uri) { /** 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 7a04f109fa7..be2ef120164 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.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 XmlFragmentMarshaller createFragmentMarshaller() { } /** 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 962e46fcb42..119ac1ccf49 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 5e10fb7d5f9..5d77124b7c3 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 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 @@ void testUnmarshalingWrongClassThrows() { 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 3e4d3a54d68..f039bfca7bf 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 3e4d3a54d68..f039bfca7bf 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