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