Change domain update commands to be varipotent by status (#2930)

This means that attempting to add a status that is already present will now
fail, and attempting to remove a status that is not present will also now fail.

This also refactors the existing checks into a single verify method, rather than
having to call three separate methods from every callsite.

BUG= http://b/474645068
This commit is contained in:
Ben McIlwain
2026-01-12 22:12:08 +00:00
committed by GitHub
parent 5725eb95e0
commit d6e0a7b979
15 changed files with 144 additions and 103 deletions
@@ -63,8 +63,6 @@ import google.registry.config.RegistryConfig;
import google.registry.flows.EppException;
import google.registry.flows.EppException.UnimplementedExtensionException;
import google.registry.flows.EppRequestSource;
import google.registry.flows.FlowTestCase.CommitMode;
import google.registry.flows.FlowTestCase.UserPrivileges;
import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.ResourceFlowTestCase;
import google.registry.flows.ResourceFlowUtils.AddExistingValueException;
@@ -454,6 +452,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
@Test
void testSuccess_removeClientUpdateProhibited() throws Exception {
setEppInput("domain_update_remove_client_update_prohibited.xml");
persistReferencedEntities();
persistResource(
persistDomain()
@@ -549,21 +548,26 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
}
@Test
void testSuccess_secDnsAddSameDoesNothing() throws Exception {
doSecDnsSuccessfulTest(
"domain_update_dsdata_add.xml",
ImmutableSet.of(SOME_DSDATA),
ImmutableSet.of(SOME_DSDATA),
ImmutableMap.of(
"KEY_TAG",
"1",
"ALG",
"2",
"DIGEST_TYPE",
"2",
"DIGEST",
"9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08"),
false);
void testFailure_secDnsAddSame_throwsException() throws Exception {
EppException thrown =
assertThrows(
AddExistingValueException.class,
() ->
doSecDnsSuccessfulTest(
"domain_update_dsdata_add.xml",
ImmutableSet.of(SOME_DSDATA),
ImmutableSet.of(SOME_DSDATA),
ImmutableMap.of(
"KEY_TAG",
"1",
"ALG",
"2",
"DIGEST_TYPE",
"2",
"DIGEST",
"9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08"),
false));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
@@ -681,7 +685,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
2,
2,
base16()
.decode("9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08"))),
.decode("0F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08"))),
ImmutableMap.of(
"KEY_TAG",
"1",
@@ -690,8 +694,8 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
"DIGEST_TYPE",
"2",
"DIGEST",
"9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08"),
false);
"0F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08"),
true);
}
@Test
@@ -799,29 +803,43 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
}
@Test
void testSuccess_secDnsAddRemoveSame() throws Exception {
// Adding and removing the same dsData is a no-op because removes are processed first.
doSecDnsSuccessfulTest(
"domain_update_dsdata_add_rem_same.xml",
ImmutableSet.of(
SOME_DSDATA,
DomainDsData.create(
12345, 3, 1, base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))),
ImmutableSet.of(
SOME_DSDATA,
DomainDsData.create(
12345, 3, 1, base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))),
false);
void testFailure_secDnsAddRemoveSame_throwsException() throws Exception {
EppException thrown =
assertThrows(
AddRemoveSameValueException.class,
() ->
doSecDnsSuccessfulTest(
"domain_update_dsdata_add_rem_same.xml",
ImmutableSet.of(
SOME_DSDATA,
DomainDsData.create(
12345,
3,
1,
base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))),
ImmutableSet.of(
SOME_DSDATA,
DomainDsData.create(
12345,
3,
1,
base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))),
false));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testSuccess_secDnsRemoveAlreadyNotThere() throws Exception {
// Removing a dsData that isn't there is a no-op.
doSecDnsSuccessfulTest(
"domain_update_dsdata_rem.xml",
ImmutableSet.of(SOME_DSDATA),
ImmutableSet.of(SOME_DSDATA),
false);
void testFailure_secDnsRemoveAlreadyNotThere_throwsException() throws Exception {
EppException thrown =
assertThrows(
RemoveNonexistentValueException.class,
() ->
doSecDnsSuccessfulTest(
"domain_update_dsdata_rem.xml",
ImmutableSet.of(SOME_DSDATA),
ImmutableSet.of(SOME_DSDATA),
false));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
void doServerStatusBillingTest(String xmlFilename, boolean isBillable) throws Exception {
@@ -856,14 +874,6 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
doServerStatusBillingTest("domain_update_add_server_status.xml", true);
}
@Test
void testSuccess_noBillingOnPreExistingServerStatus() throws Exception {
eppRequestSource = EppRequestSource.TOOL;
Domain addStatusDomain = persistActiveDomain(getUniqueIdFromCommand());
persistResource(addStatusDomain.asBuilder().addStatusValue(SERVER_RENEW_PROHIBITED).build());
doServerStatusBillingTest("domain_update_add_server_status.xml", false);
}
@Test
void testSuccess_removeServerStatusBillingEvent() throws Exception {
eppRequestSource = EppRequestSource.TOOL;
@@ -1390,7 +1400,20 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
}
@Test
void testFailure_removeNonexistentValue() throws Exception {
void testFailure_addExistingStatusValue() throws Exception {
persistResource(
DatabaseHelper.newDomain(getUniqueIdFromCommand())
.asBuilder()
.setStatusValues(ImmutableSet.of(CLIENT_RENEW_PROHIBITED))
.build());
setEppInput("domain_update_add_non_server_status.xml");
assertAboutEppExceptions()
.that(assertThrows(AddExistingValueException.class, this::runFlow))
.marshalsToXml();
}
@Test
void testFailure_removeNonexistentNameserver() throws Exception {
persistActiveDomain(getUniqueIdFromCommand());
persistActiveHost("ns1.example.foo");
setEppInput("domain_update_remove_nameserver.xml");
@@ -1399,6 +1422,15 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
.marshalsToXml();
}
@Test
void testFailure_removeNonexistentStatusValue() throws Exception {
persistActiveDomain(getUniqueIdFromCommand());
setEppInput("domain_update_remove_client_update_prohibited.xml");
assertAboutEppExceptions()
.that(assertThrows(RemoveNonexistentValueException.class, this::runFlow))
.marshalsToXml();
}
@Test
void testFailure_minimumDataset_addingNewRegistrantFails() throws Exception {
persistReferencedEntities();
@@ -15,7 +15,6 @@
<domain:ns>
<domain:hostObj>ns1.example.foo</domain:hostObj>
</domain:ns>
<domain:status s="clientUpdateProhibited"/>
</domain:rem>
<domain:chg>
<domain:authInfo>
@@ -17,7 +17,6 @@
<domain:hostObj>ns1.example.foo</domain:hostObj>
</domain:ns>
<domain:contact type="tech">sh8013</domain:contact>
<domain:status s="clientUpdateProhibited"/>
</domain:rem>
<domain:chg>
<domain:registrant/>
@@ -15,7 +15,6 @@
<domain:ns>
<domain:hostObj>ns1.example.foo</domain:hostObj>
</domain:ns>
<domain:status s="clientUpdateProhibited"/>
</domain:rem>
</domain:update>
</update>
@@ -15,7 +15,6 @@
<domain:ns>
<domain:hostObj>ns1.example.foo</domain:hostObj>
</domain:ns>
<domain:status s="clientUpdateProhibited"/>
</domain:rem>
<domain:chg>
<domain:authInfo>
@@ -15,7 +15,6 @@
<domain:ns>
<domain:hostObj>ns1.example.foo</domain:hostObj>
</domain:ns>
<domain:status s="clientUpdateProhibited"/>
</domain:rem>
<domain:chg>
<domain:authInfo>
@@ -0,0 +1,16 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<update>
<domain:update
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example.tld</domain:name>
<domain:add />
<domain:rem>
<domain:status s="clientUpdateProhibited"/>
</domain:rem>
<domain:chg />
</domain:update>
</update>
<clTRID>ABC-12345</clTRID>
</command>
</epp>
@@ -15,7 +15,6 @@
<domain:ns>
<domain:hostObj>ns1.example.foo</domain:hostObj>
</domain:ns>
<domain:status s="clientUpdateProhibited"/>
</domain:rem>
<domain:chg>
<domain:authInfo>
@@ -16,7 +16,7 @@
<secDNS:keyTag>12346</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>38EC35D5B3A34B44C39B</secDNS:digest>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
</secDNS:dsData>
</secDNS:rem>
</secDNS:update>