diff --git a/java/google/registry/flows/domain/BaseDomainCreateFlow.java b/java/google/registry/flows/domain/BaseDomainCreateFlow.java index 6eb8926c4..07c08b344 100644 --- a/java/google/registry/flows/domain/BaseDomainCreateFlow.java +++ b/java/google/registry/flows/domain/BaseDomainCreateFlow.java @@ -21,7 +21,7 @@ import static google.registry.flows.domain.DomainFlowUtils.validateDomainName; import static google.registry.flows.domain.DomainFlowUtils.validateDomainNameWithIdnTables; import static google.registry.flows.domain.DomainFlowUtils.validateDsData; import static google.registry.flows.domain.DomainFlowUtils.validateNameserversAllowedOnTld; -import static google.registry.flows.domain.DomainFlowUtils.validateNameserversCount; +import static google.registry.flows.domain.DomainFlowUtils.validateNameserversCountForTld; import static google.registry.flows.domain.DomainFlowUtils.validateNoDuplicateContacts; import static google.registry.flows.domain.DomainFlowUtils.validateRegistrantAllowedOnTld; import static google.registry.flows.domain.DomainFlowUtils.validateRequiredContactsPresent; @@ -215,7 +215,7 @@ public abstract class BaseDomainCreateFlow fullyQualifiedHostNames = nullToEmpty(command.getNameserverFullyQualifiedHostNames()); - validateNameserversCount(fullyQualifiedHostNames.size()); + validateNameserversCountForTld(tld, fullyQualifiedHostNames.size()); validateNameserversAllowedOnTld(tld, fullyQualifiedHostNames); validateLaunchCreateExtension(); // If a signed mark was provided, then it must match the desired domain label. diff --git a/java/google/registry/flows/domain/BaseDomainUpdateFlow.java b/java/google/registry/flows/domain/BaseDomainUpdateFlow.java index b7e59e3a2..7c7716219 100644 --- a/java/google/registry/flows/domain/BaseDomainUpdateFlow.java +++ b/java/google/registry/flows/domain/BaseDomainUpdateFlow.java @@ -21,13 +21,14 @@ import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReference import static google.registry.flows.domain.DomainFlowUtils.validateContactsHaveTypes; import static google.registry.flows.domain.DomainFlowUtils.validateDsData; import static google.registry.flows.domain.DomainFlowUtils.validateNameserversAllowedOnTld; -import static google.registry.flows.domain.DomainFlowUtils.validateNameserversCount; +import static google.registry.flows.domain.DomainFlowUtils.validateNameserversCountForTld; import static google.registry.flows.domain.DomainFlowUtils.validateNoDuplicateContacts; import static google.registry.flows.domain.DomainFlowUtils.validateRegistrantAllowedOnTld; import static google.registry.flows.domain.DomainFlowUtils.validateRequiredContactsPresent; import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete; import com.google.common.collect.ImmutableSet; + import google.registry.flows.EppException; import google.registry.flows.EppException.ParameterValuePolicyErrorException; import google.registry.flows.EppException.RequiredParameterMissingException; @@ -40,6 +41,7 @@ import google.registry.model.domain.secdns.DelegationSignerData; import google.registry.model.domain.secdns.SecDnsUpdateExtension; import google.registry.model.domain.secdns.SecDnsUpdateExtension.Add; import google.registry.model.domain.secdns.SecDnsUpdateExtension.Remove; + import java.util.Set; /** @@ -126,7 +128,7 @@ public abstract class BaseDomainUpdateFlow whitelist = Registry.get(tld).getAllowedFullyQualifiedHostNames(); + // For TLDs with a nameserver whitelist, all domains must have at least 1 nameserver. + if (!whitelist.isEmpty() && count == 0) { + throw new NameserversNotSpecifiedException(); + } + if (count > MAX_NAMESERVERS_PER_DOMAIN) { throw new TooManyNameserversException(String.format( "Only %d nameservers are allowed per domain", MAX_NAMESERVERS_PER_DOMAIN)); } + } static void validateNoDuplicateContacts(Set contacts) @@ -313,7 +320,7 @@ public class DomainFlowUtils { static void validateRegistrantAllowedOnTld(String tld, String registrantContactId) throws RegistrantNotAllowedException { ImmutableSet whitelist = Registry.get(tld).getAllowedRegistrantContactIds(); - // Empty whitelists or null registrantContactId are ignored. + // Empty whitelist or null registrantContactId are ignored. if (registrantContactId != null && !whitelist.isEmpty() && !whitelist.contains(registrantContactId)) { throw new RegistrantNotAllowedException(registrantContactId); @@ -323,12 +330,12 @@ public class DomainFlowUtils { static void validateNameserversAllowedOnTld(String tld, Set fullyQualifiedHostNames) throws EppException { ImmutableSet whitelist = Registry.get(tld).getAllowedFullyQualifiedHostNames(); - if (whitelist.isEmpty()) { // Empty whitelists are ignored. - return; - } - Set disallowedNameservers = difference(nullToEmpty(fullyQualifiedHostNames), whitelist); - if (!disallowedNameservers.isEmpty()) { - throw new NameserversNotAllowedException(disallowedNameservers); + Set hostnames = nullToEmpty(fullyQualifiedHostNames); + if (!whitelist.isEmpty()) { // Empty whitelist is ignored. + Set disallowedNameservers = difference(hostnames, whitelist); + if (!disallowedNameservers.isEmpty()) { + throw new NameserversNotAllowedException(disallowedNameservers); + } } } @@ -998,4 +1005,12 @@ public class DomainFlowUtils { Joiner.on(',').join(fullyQualifiedHostNames))); } } + + /** Nameservers not specified for this TLD with whitelist. */ + public static class NameserversNotSpecifiedException extends StatusProhibitsOperationException { + public NameserversNotSpecifiedException() { + super("At least one nameserver must be specified for this TLD"); + } + } + } diff --git a/java/google/registry/flows/domain/DomainUpdateFlow.java b/java/google/registry/flows/domain/DomainUpdateFlow.java index 1efc2f66d..d21ca541a 100644 --- a/java/google/registry/flows/domain/DomainUpdateFlow.java +++ b/java/google/registry/flows/domain/DomainUpdateFlow.java @@ -59,6 +59,7 @@ import org.joda.time.DateTime; * @error {@link DomainFlowUtils.MissingContactTypeException} * @error {@link DomainFlowUtils.MissingTechnicalContactException} * @error {@link DomainFlowUtils.NameserversNotAllowedException} + * @error {@link DomainFlowUtils.NameserversNotSpecifiedException} * @error {@link DomainFlowUtils.RegistrantNotAllowedException} * @error {@link DomainFlowUtils.TooManyDsRecordsException} * @error {@link DomainFlowUtils.TooManyNameserversException} diff --git a/javatests/google/registry/flows/domain/DomainApplicationCreateFlowTest.java b/javatests/google/registry/flows/domain/DomainApplicationCreateFlowTest.java index 49b5f653d..de44bc026 100644 --- a/javatests/google/registry/flows/domain/DomainApplicationCreateFlowTest.java +++ b/javatests/google/registry/flows/domain/DomainApplicationCreateFlowTest.java @@ -77,6 +77,7 @@ import google.registry.flows.domain.DomainFlowUtils.LeadingDashException; import google.registry.flows.domain.DomainFlowUtils.LinkedResourcesDoNotExistException; import google.registry.flows.domain.DomainFlowUtils.MissingContactTypeException; import google.registry.flows.domain.DomainFlowUtils.NameserversNotAllowedException; +import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedException; import google.registry.flows.domain.DomainFlowUtils.NoMarksFoundMatchingDomainException; import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException; import google.registry.flows.domain.DomainFlowUtils.PremiumNameBlockedException; @@ -1205,7 +1206,7 @@ public class DomainApplicationCreateFlowTest } @Test - public void testSuccess_emptyNameserversPassesWhitelist() throws Exception { + public void testFailure_emptyNameserverFailsWhitelist() throws Exception { setEppInput("domain_create_sunrise_encoded_signed_mark_no_hosts.xml"); persistResource(Registry.get("tld").asBuilder() .setAllowedRegistrantContactIds(ImmutableSet.of("jd1234")) @@ -1213,9 +1214,8 @@ public class DomainApplicationCreateFlowTest .build()); persistContactsAndHosts(); clock.advanceOneMilli(); - doSuccessfulTest("domain_create_sunrise_encoded_signed_mark_response.xml", true); - assertAboutApplications().that(getOnlyGlobalResource(DomainApplication.class)) - .hasApplicationStatus(ApplicationStatus.VALIDATED); + thrown.expect(NameserversNotSpecifiedException.class); + runFlow(); } /** diff --git a/javatests/google/registry/flows/domain/DomainCreateFlowTest.java b/javatests/google/registry/flows/domain/DomainCreateFlowTest.java index d7467bcc3..45a776d83 100644 --- a/javatests/google/registry/flows/domain/DomainCreateFlowTest.java +++ b/javatests/google/registry/flows/domain/DomainCreateFlowTest.java @@ -87,6 +87,7 @@ import google.registry.flows.domain.DomainFlowUtils.MissingContactTypeException; import google.registry.flows.domain.DomainFlowUtils.MissingRegistrantException; import google.registry.flows.domain.DomainFlowUtils.MissingTechnicalContactException; import google.registry.flows.domain.DomainFlowUtils.NameserversNotAllowedException; +import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedException; import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException; import google.registry.flows.domain.DomainFlowUtils.PremiumNameBlockedException; import google.registry.flows.domain.DomainFlowUtils.RegistrantNotAllowedException; @@ -1261,13 +1262,14 @@ public class DomainCreateFlowTest extends ResourceFlowTestCase + + + + example.tld + + + + sh8013 + + + + ABC-12345 + + diff --git a/javatests/google/registry/flows/domain/testdata/domain_update_remove_nameserver.xml b/javatests/google/registry/flows/domain/testdata/domain_update_remove_nameserver.xml new file mode 100644 index 000000000..bbf5548ea --- /dev/null +++ b/javatests/google/registry/flows/domain/testdata/domain_update_remove_nameserver.xml @@ -0,0 +1,16 @@ + + + + + example.tld + + + ns1.example.foo + + + + + ABC-12345 + +