Skip to content

Commit 6747cc8

Browse files
authored
Activate Fee tag normalization in Non-Prod (#2963)
For all flows that use Fee extensions, normalize the fee tags in all non-prod environments. For flows that do not use fee extensions but with fee tags in the header, e.g., HostInfo flows, normalization is not performed.
1 parent e4c4149 commit 6747cc8

File tree

4 files changed

+103
-1
lines changed

4 files changed

+103
-1
lines changed

core/src/main/java/google/registry/model/eppcommon/EppXmlTransformer.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,14 @@
2020
import com.google.common.annotations.VisibleForTesting;
2121
import com.google.common.collect.ImmutableList;
2222
import com.google.common.collect.ImmutableSet;
23+
import google.registry.flows.FeeExtensionXmlTagNormalizer;
2324
import google.registry.model.ImmutableObject;
25+
import google.registry.model.domain.fee.FeeCheckResponseExtension;
26+
import google.registry.model.domain.fee.FeeTransformResponseExtension;
27+
import google.registry.model.domain.fee06.FeeInfoResponseExtensionV06;
2428
import google.registry.model.eppinput.EppInput;
2529
import google.registry.model.eppoutput.EppOutput;
30+
import google.registry.model.eppoutput.EppResponse;
2631
import google.registry.util.RegistryEnvironment;
2732
import google.registry.xml.ValidationMode;
2833
import google.registry.xml.XmlException;
@@ -98,8 +103,31 @@ private static byte[] marshal(
98103
return byteArrayOutputStream.toByteArray();
99104
}
100105

106+
private static boolean hasFeeExtension(EppOutput eppOutput) {
107+
if (!eppOutput.isResponse()) {
108+
return false;
109+
}
110+
return eppOutput.getResponse().getExtensions().stream()
111+
.map(EppResponse.ResponseExtension::getClass)
112+
.filter(EppXmlTransformer::isFeeExtension)
113+
.findAny()
114+
.isPresent();
115+
}
116+
117+
@VisibleForTesting
118+
static boolean isFeeExtension(Class<?> clazz) {
119+
return FeeCheckResponseExtension.class.isAssignableFrom(clazz)
120+
|| FeeTransformResponseExtension.class.isAssignableFrom(clazz)
121+
|| FeeInfoResponseExtensionV06.class.isAssignableFrom(clazz);
122+
}
123+
101124
public static byte[] marshal(EppOutput root, ValidationMode validation) throws XmlException {
102-
return marshal(OUTPUT_TRANSFORMER, root, validation);
125+
byte[] bytes = marshal(OUTPUT_TRANSFORMER, root, validation);
126+
if (!RegistryEnvironment.PRODUCTION.equals(RegistryEnvironment.get())
127+
&& hasFeeExtension(root)) {
128+
return FeeExtensionXmlTagNormalizer.normalize(new String(bytes, UTF_8)).getBytes(UTF_8);
129+
}
130+
return bytes;
103131
}
104132

105133
@VisibleForTesting

core/src/test/java/google/registry/flows/FeeExtensionXmlTagNormalizerTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
import static com.google.common.truth.Truth.assertThat;
1818
import static google.registry.flows.FeeExtensionXmlTagNormalizer.feeExtensionInUseRegex;
1919
import static google.registry.flows.FeeExtensionXmlTagNormalizer.normalize;
20+
import static google.registry.flows.FlowTestCase.verifyFeeTagNormalized;
2021
import static google.registry.model.eppcommon.EppXmlTransformer.validateOutput;
2122
import static google.registry.testing.TestDataHelper.loadFile;
23+
import static org.junit.jupiter.api.Assertions.assertThrows;
2224

2325
import java.util.stream.Stream;
2426
import org.junit.jupiter.api.Test;
@@ -41,6 +43,13 @@ void normalize_noFeeExtensions() throws Exception {
4143
assertThat(normalized).isEqualTo(xml);
4244
}
4345

46+
@Test
47+
void normalize_greetingUnchanged() throws Exception {
48+
String xml = loadFile(getClass(), "greeting.xml");
49+
String normalized = normalize(xml);
50+
assertThat(normalized).isEqualTo(xml);
51+
}
52+
4453
@ParameterizedTest(name = "normalize_withFeeExtension-{0}")
4554
@MethodSource("provideTestCombinations")
4655
@SuppressWarnings("unused") // Parameter 'name' is part of test case name
@@ -55,6 +64,28 @@ void normalize_withFeeExtension(String name, String inputXmlFilename, String exp
5564
assertThat(normalized).isEqualTo(expected);
5665
}
5766

67+
// Piggyback tests for FlowTestCase.verifyFeeTagNormalized here.
68+
@ParameterizedTest(name = "verifyFeeTagNormalized-{0}")
69+
@MethodSource("provideTestCombinations")
70+
@SuppressWarnings("unused") // Parameter 'name' is part of test case name
71+
void verifyFeeTagNormalized_success(
72+
String name, String inputXmlFilename, String expectedXmlFilename) throws Exception {
73+
String original = loadFile(getClass(), inputXmlFilename);
74+
String expected = loadFile(getClass(), expectedXmlFilename);
75+
76+
if (name.equals("v06")) {
77+
// Fee-06 already uses 'fee'. Non-normalized tags only appear in header.
78+
verifyFeeTagNormalized(original);
79+
} else {
80+
assertThrows(
81+
AssertionError.class,
82+
() -> {
83+
verifyFeeTagNormalized(original);
84+
});
85+
}
86+
verifyFeeTagNormalized(expected);
87+
}
88+
5889
@SuppressWarnings("unused")
5990
static Stream<Arguments> provideTestCombinations() {
6091
return Stream.of(

core/src/test/java/google/registry/flows/FlowTestCase.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import static com.google.common.collect.Sets.difference;
2020
import static com.google.common.truth.Truth.assertThat;
2121
import static com.google.common.truth.Truth.assertWithMessage;
22+
import static google.registry.flows.FlowUtils.marshalWithLenientRetry;
2223
import static google.registry.model.eppcommon.EppXmlTransformer.marshal;
2324
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
2425
import static google.registry.testing.DatabaseHelper.stripBillingEventId;
@@ -55,6 +56,7 @@
5556
import java.util.Arrays;
5657
import java.util.List;
5758
import java.util.Map;
59+
import java.util.regex.Pattern;
5860
import javax.annotation.Nullable;
5961
import org.joda.time.DateTime;
6062
import org.junit.jupiter.api.BeforeEach;
@@ -284,6 +286,7 @@ public EppOutput runFlowAssertResponse(
284286
Arrays.toString(marshal(output, ValidationMode.LENIENT))),
285287
e);
286288
}
289+
verifyFeeTagNormalized(new String(marshalWithLenientRetry(output), UTF_8));
287290
return output;
288291
}
289292

@@ -298,4 +301,14 @@ public EppOutput dryRunFlowAssertResponse(String xml, String... ignoredPaths) th
298301
public EppOutput runFlowAssertResponse(String xml, String... ignoredPaths) throws Exception {
299302
return runFlowAssertResponse(CommitMode.LIVE, UserPrivileges.NORMAL, xml, ignoredPaths);
300303
}
304+
305+
// Pattern for non-normalized tags in use. Occurrences in namespace declarations ignored.
306+
private static final Pattern NON_NORMALIZED_FEE_TAGS =
307+
Pattern.compile("\\bfee11:|\\bfee12:|\\bfee_1_00:");
308+
309+
static void verifyFeeTagNormalized(String xml) {
310+
assertWithMessage("Unexpected un-normalized Fee tags found in message.")
311+
.that(NON_NORMALIZED_FEE_TAGS.matcher(xml).find())
312+
.isFalse();
313+
}
301314
}

core/src/test/java/google/registry/model/eppcommon/EppXmlTransformerTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,48 @@
1515
package google.registry.model.eppcommon;
1616

1717
import static com.google.common.truth.Truth.assertThat;
18+
import static com.google.common.truth.Truth.assertWithMessage;
19+
import static google.registry.model.eppcommon.EppXmlTransformer.isFeeExtension;
1820
import static google.registry.model.eppcommon.EppXmlTransformer.unmarshal;
1921
import static google.registry.testing.TestDataHelper.loadBytes;
2022
import static org.junit.jupiter.api.Assertions.assertThrows;
2123

24+
import com.google.common.collect.ImmutableSet;
25+
import google.registry.model.domain.bulktoken.BulkTokenResponseExtension;
26+
import google.registry.model.domain.launch.LaunchCheckResponseExtension;
27+
import google.registry.model.domain.rgp.RgpInfoExtension;
28+
import google.registry.model.domain.secdns.SecDnsInfoExtension;
2229
import google.registry.model.eppinput.EppInput;
2330
import google.registry.model.eppoutput.EppOutput;
31+
import google.registry.model.eppoutput.EppResponse;
2432
import google.registry.util.RegistryEnvironment;
33+
import jakarta.xml.bind.annotation.XmlElementRef;
34+
import jakarta.xml.bind.annotation.XmlElementRefs;
35+
import java.util.Arrays;
2536
import org.junit.jupiter.api.Test;
2637

2738
/** Tests for {@link EppXmlTransformer}. */
2839
class EppXmlTransformerTest {
2940

41+
// Non-fee extensions allowed in {@code Response.extensions}.
42+
private static final ImmutableSet<Class<?>> NON_FEE_EXTENSIONS =
43+
ImmutableSet.of(
44+
BulkTokenResponseExtension.class,
45+
LaunchCheckResponseExtension.class,
46+
RgpInfoExtension.class,
47+
SecDnsInfoExtension.class);
48+
49+
@Test
50+
void isFeeExtension_eppResponse() throws Exception {
51+
var xmlRefs =
52+
EppResponse.class.getDeclaredField("extensions").getAnnotation(XmlElementRefs.class);
53+
Arrays.stream(xmlRefs.value())
54+
.map(XmlElementRef::type)
55+
.filter(type -> !NON_FEE_EXTENSIONS.contains(type))
56+
.forEach(
57+
type -> assertWithMessage(type.getSimpleName()).that(isFeeExtension(type)).isTrue());
58+
}
59+
3060
@Test
3161
void testUnmarshalingEppInput() throws Exception {
3262
EppInput input = unmarshal(EppInput.class, loadBytes(getClass(), "contact_info.xml").read());

0 commit comments

Comments
 (0)