mirror of
https://github.com/google/nomulus.git
synced 2025-05-14 08:27:14 +02:00
Fix bug which allowed creation of hosts with superordinate domains in pending delete state.
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=154716883
This commit is contained in:
parent
386d2bc6be
commit
11e7374c0f
7 changed files with 82 additions and 0 deletions
|
@ -1009,6 +1009,7 @@ are enqueued to update DNS accordingly.
|
||||||
* This resource has clientUpdateProhibited on it, and the update does not
|
* This resource has clientUpdateProhibited on it, and the update does not
|
||||||
clear that status.
|
clear that status.
|
||||||
* Resource status prohibits this operation.
|
* Resource status prohibits this operation.
|
||||||
|
* Superordinate domain for this hostname is in pending delete.
|
||||||
* Cannot remove all IP addresses from a subordinate host.
|
* Cannot remove all IP addresses from a subordinate host.
|
||||||
* Cannot rename an external host.
|
* Cannot rename an external host.
|
||||||
* 2306
|
* 2306
|
||||||
|
@ -1094,6 +1095,8 @@ allows creating a host name, and if necessary enqueues tasks to update DNS.
|
||||||
* Resource with this id already exists.
|
* Resource with this id already exists.
|
||||||
* 2303
|
* 2303
|
||||||
* Superordinate domain for this hostname does not exist.
|
* Superordinate domain for this hostname does not exist.
|
||||||
|
* 2304
|
||||||
|
* Superordinate domain for this hostname is in pending delete.
|
||||||
* 2306
|
* 2306
|
||||||
* Host names must be at least two levels below the public suffix.
|
* Host names must be at least two levels below the public suffix.
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
|
||||||
import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist;
|
import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist;
|
||||||
import static google.registry.flows.host.HostFlowUtils.lookupSuperordinateDomain;
|
import static google.registry.flows.host.HostFlowUtils.lookupSuperordinateDomain;
|
||||||
import static google.registry.flows.host.HostFlowUtils.validateHostName;
|
import static google.registry.flows.host.HostFlowUtils.validateHostName;
|
||||||
|
import static google.registry.flows.host.HostFlowUtils.verifySuperordinateDomainNotInPendingDelete;
|
||||||
import static google.registry.flows.host.HostFlowUtils.verifySuperordinateDomainOwnership;
|
import static google.registry.flows.host.HostFlowUtils.verifySuperordinateDomainOwnership;
|
||||||
import static google.registry.model.EppResourceUtils.createRepoId;
|
import static google.registry.model.EppResourceUtils.createRepoId;
|
||||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||||
|
@ -72,6 +73,7 @@ import org.joda.time.DateTime;
|
||||||
* @error {@link HostFlowUtils.HostNameNotNormalizedException}
|
* @error {@link HostFlowUtils.HostNameNotNormalizedException}
|
||||||
* @error {@link HostFlowUtils.HostNameNotPunyCodedException}
|
* @error {@link HostFlowUtils.HostNameNotPunyCodedException}
|
||||||
* @error {@link HostFlowUtils.SuperordinateDomainDoesNotExistException}
|
* @error {@link HostFlowUtils.SuperordinateDomainDoesNotExistException}
|
||||||
|
* @error {@link HostFlowUtils.SuperordinateDomainInPendingDeleteException}
|
||||||
* @error {@link SubordinateHostMustHaveIpException}
|
* @error {@link SubordinateHostMustHaveIpException}
|
||||||
* @error {@link UnexpectedExternalHostIpException}
|
* @error {@link UnexpectedExternalHostIpException}
|
||||||
*/
|
*/
|
||||||
|
@ -101,6 +103,7 @@ public final class HostCreateFlow implements TransactionalFlow {
|
||||||
// we can detect error conditions earlier.
|
// we can detect error conditions earlier.
|
||||||
Optional<DomainResource> superordinateDomain =
|
Optional<DomainResource> superordinateDomain =
|
||||||
lookupSuperordinateDomain(validateHostName(targetId), now);
|
lookupSuperordinateDomain(validateHostName(targetId), now);
|
||||||
|
verifySuperordinateDomainNotInPendingDelete(superordinateDomain.orNull());
|
||||||
verifySuperordinateDomainOwnership(clientId, superordinateDomain.orNull());
|
verifySuperordinateDomainOwnership(clientId, superordinateDomain.orNull());
|
||||||
boolean willBeSubordinate = superordinateDomain.isPresent();
|
boolean willBeSubordinate = superordinateDomain.isPresent();
|
||||||
boolean hasIpAddresses = !isNullOrEmpty(command.getInetAddresses());
|
boolean hasIpAddresses = !isNullOrEmpty(command.getInetAddresses());
|
||||||
|
|
|
@ -30,7 +30,9 @@ import google.registry.flows.EppException.ObjectDoesNotExistException;
|
||||||
import google.registry.flows.EppException.ParameterValuePolicyErrorException;
|
import google.registry.flows.EppException.ParameterValuePolicyErrorException;
|
||||||
import google.registry.flows.EppException.ParameterValueRangeErrorException;
|
import google.registry.flows.EppException.ParameterValueRangeErrorException;
|
||||||
import google.registry.flows.EppException.ParameterValueSyntaxErrorException;
|
import google.registry.flows.EppException.ParameterValueSyntaxErrorException;
|
||||||
|
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||||
import google.registry.model.domain.DomainResource;
|
import google.registry.model.domain.DomainResource;
|
||||||
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
import google.registry.util.Idn;
|
import google.registry.util.Idn;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
|
@ -127,6 +129,24 @@ public class HostFlowUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Ensure that the superordinate domain is not in pending delete. */
|
||||||
|
static void verifySuperordinateDomainNotInPendingDelete(
|
||||||
|
DomainResource superordinateDomain) throws EppException {
|
||||||
|
if ((superordinateDomain != null)
|
||||||
|
&& superordinateDomain.getStatusValues().contains(StatusValue.PENDING_DELETE)) {
|
||||||
|
throw new SuperordinateDomainInPendingDeleteException(
|
||||||
|
superordinateDomain.getFullyQualifiedDomainName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Superordinate domain for this hostname is in pending delete. */
|
||||||
|
static class SuperordinateDomainInPendingDeleteException
|
||||||
|
extends StatusProhibitsOperationException {
|
||||||
|
public SuperordinateDomainInPendingDeleteException(String domainName) {
|
||||||
|
super(domainName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Host names are limited to 253 characters. */
|
/** Host names are limited to 253 characters. */
|
||||||
static class HostNameTooLongException extends ParameterValueRangeErrorException {
|
static class HostNameTooLongException extends ParameterValueRangeErrorException {
|
||||||
public HostNameTooLongException() {
|
public HostNameTooLongException() {
|
||||||
|
|
|
@ -24,6 +24,7 @@ import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses
|
||||||
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
|
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
|
||||||
import static google.registry.flows.host.HostFlowUtils.lookupSuperordinateDomain;
|
import static google.registry.flows.host.HostFlowUtils.lookupSuperordinateDomain;
|
||||||
import static google.registry.flows.host.HostFlowUtils.validateHostName;
|
import static google.registry.flows.host.HostFlowUtils.validateHostName;
|
||||||
|
import static google.registry.flows.host.HostFlowUtils.verifySuperordinateDomainNotInPendingDelete;
|
||||||
import static google.registry.flows.host.HostFlowUtils.verifySuperordinateDomainOwnership;
|
import static google.registry.flows.host.HostFlowUtils.verifySuperordinateDomainOwnership;
|
||||||
import static google.registry.model.index.ForeignKeyIndex.loadAndGetKey;
|
import static google.registry.model.index.ForeignKeyIndex.loadAndGetKey;
|
||||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||||
|
@ -90,6 +91,7 @@ import org.joda.time.DateTime;
|
||||||
* @error {@link HostFlowUtils.HostNameTooShallowException}
|
* @error {@link HostFlowUtils.HostNameTooShallowException}
|
||||||
* @error {@link HostFlowUtils.InvalidHostNameException}
|
* @error {@link HostFlowUtils.InvalidHostNameException}
|
||||||
* @error {@link HostFlowUtils.SuperordinateDomainDoesNotExistException}
|
* @error {@link HostFlowUtils.SuperordinateDomainDoesNotExistException}
|
||||||
|
* @error {@link HostFlowUtils.SuperordinateDomainInPendingDeleteException}
|
||||||
* @error {@link CannotAddIpToExternalHostException}
|
* @error {@link CannotAddIpToExternalHostException}
|
||||||
* @error {@link CannotRemoveSubordinateHostLastIpException}
|
* @error {@link CannotRemoveSubordinateHostLastIpException}
|
||||||
* @error {@link CannotRenameExternalHostException}
|
* @error {@link CannotRenameExternalHostException}
|
||||||
|
@ -139,6 +141,7 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||||
// Note that lookupSuperordinateDomain calls cloneProjectedAtTime on the domain for us.
|
// Note that lookupSuperordinateDomain calls cloneProjectedAtTime on the domain for us.
|
||||||
Optional<DomainResource> newSuperordinateDomain =
|
Optional<DomainResource> newSuperordinateDomain =
|
||||||
lookupSuperordinateDomain(validateHostName(newHostName), now);
|
lookupSuperordinateDomain(validateHostName(newHostName), now);
|
||||||
|
verifySuperordinateDomainNotInPendingDelete(newSuperordinateDomain.orNull());
|
||||||
EppResource owningResource = firstNonNull(oldSuperordinateDomain, existingHost);
|
EppResource owningResource = firstNonNull(oldSuperordinateDomain, existingHost);
|
||||||
verifyUpdateAllowed(
|
verifyUpdateAllowed(
|
||||||
command, existingHost, newSuperordinateDomain.orNull(), owningResource, isHostRename);
|
command, existingHost, newSuperordinateDomain.orNull(), owningResource, isHostRename);
|
||||||
|
|
|
@ -28,6 +28,7 @@ import com.googlecode.objectify.VoidWork;
|
||||||
import google.registry.config.RegistryConfig.Config;
|
import google.registry.config.RegistryConfig.Config;
|
||||||
import google.registry.mapreduce.MapreduceRunner;
|
import google.registry.mapreduce.MapreduceRunner;
|
||||||
import google.registry.model.domain.DomainResource;
|
import google.registry.model.domain.DomainResource;
|
||||||
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
import google.registry.model.host.HostResource;
|
import google.registry.model.host.HostResource;
|
||||||
import google.registry.request.Action;
|
import google.registry.request.Action;
|
||||||
import google.registry.request.Parameter;
|
import google.registry.request.Parameter;
|
||||||
|
@ -107,6 +108,14 @@ public class RdeHostLinkAction implements Runnable {
|
||||||
logger.infofmt("Host %s is out of zone", xjcHost.getName());
|
logger.infofmt("Host %s is out of zone", xjcHost.getName());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (superordinateDomain.get().getStatusValues().contains(StatusValue.PENDING_DELETE)) {
|
||||||
|
getContext()
|
||||||
|
.incrementCounter(
|
||||||
|
"post-import hosts with superordinate domains in pending delete");
|
||||||
|
logger.infofmt(
|
||||||
|
"Host %s has a superordinate domain in pending delete", xjcHost.getName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
// at this point, the host is definitely in zone and should be linked
|
// at this point, the host is definitely in zone and should be linked
|
||||||
getContext().incrementCounter("post-import hosts in zone");
|
getContext().incrementCounter("post-import hosts in zone");
|
||||||
final Key<DomainResource> superordinateDomainKey = Key.create(superordinateDomain.get());
|
final Key<DomainResource> superordinateDomainKey = Key.create(superordinateDomain.get());
|
||||||
|
|
|
@ -18,15 +18,18 @@ import static com.google.common.truth.Truth.assertThat;
|
||||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||||
import static google.registry.testing.DatastoreHelper.assertNoBillingEvents;
|
import static google.registry.testing.DatastoreHelper.assertNoBillingEvents;
|
||||||
import static google.registry.testing.DatastoreHelper.createTld;
|
import static google.registry.testing.DatastoreHelper.createTld;
|
||||||
|
import static google.registry.testing.DatastoreHelper.newDomainResource;
|
||||||
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
|
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
|
||||||
import static google.registry.testing.DatastoreHelper.persistActiveHost;
|
import static google.registry.testing.DatastoreHelper.persistActiveHost;
|
||||||
import static google.registry.testing.DatastoreHelper.persistDeletedHost;
|
import static google.registry.testing.DatastoreHelper.persistDeletedHost;
|
||||||
|
import static google.registry.testing.DatastoreHelper.persistResource;
|
||||||
import static google.registry.testing.HostResourceSubject.assertAboutHosts;
|
import static google.registry.testing.HostResourceSubject.assertAboutHosts;
|
||||||
import static google.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued;
|
import static google.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued;
|
||||||
import static google.registry.testing.TaskQueueHelper.assertNoDnsTasksEnqueued;
|
import static google.registry.testing.TaskQueueHelper.assertNoDnsTasksEnqueued;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.googlecode.objectify.Key;
|
import com.googlecode.objectify.Key;
|
||||||
import google.registry.flows.EppXmlTransformer.IpAddressVersionMismatchException;
|
import google.registry.flows.EppXmlTransformer.IpAddressVersionMismatchException;
|
||||||
import google.registry.flows.ResourceFlowTestCase;
|
import google.registry.flows.ResourceFlowTestCase;
|
||||||
|
@ -40,7 +43,9 @@ import google.registry.flows.host.HostFlowUtils.HostNameTooLongException;
|
||||||
import google.registry.flows.host.HostFlowUtils.HostNameTooShallowException;
|
import google.registry.flows.host.HostFlowUtils.HostNameTooShallowException;
|
||||||
import google.registry.flows.host.HostFlowUtils.InvalidHostNameException;
|
import google.registry.flows.host.HostFlowUtils.InvalidHostNameException;
|
||||||
import google.registry.flows.host.HostFlowUtils.SuperordinateDomainDoesNotExistException;
|
import google.registry.flows.host.HostFlowUtils.SuperordinateDomainDoesNotExistException;
|
||||||
|
import google.registry.flows.host.HostFlowUtils.SuperordinateDomainInPendingDeleteException;
|
||||||
import google.registry.model.domain.DomainResource;
|
import google.registry.model.domain.DomainResource;
|
||||||
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
import google.registry.model.host.HostResource;
|
import google.registry.model.host.HostResource;
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
@ -167,6 +172,22 @@ public class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, Hos
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFailure_superordinateInPendingDelete() throws Exception {
|
||||||
|
setEppHostCreateInputWithIps("ns1.example.tld");
|
||||||
|
createTld("tld");
|
||||||
|
persistResource(newDomainResource("example.tld")
|
||||||
|
.asBuilder()
|
||||||
|
.setDeletionTime(clock.nowUtc().plusDays(35))
|
||||||
|
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
|
||||||
|
.build());
|
||||||
|
clock.advanceOneMilli();
|
||||||
|
thrown.expect(
|
||||||
|
SuperordinateDomainInPendingDeleteException.class,
|
||||||
|
"example.tld");
|
||||||
|
runFlow();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_alreadyExists() throws Exception {
|
public void testFailure_alreadyExists() throws Exception {
|
||||||
setEppHostCreateInput("ns1.example.tld", null);
|
setEppHostCreateInput("ns1.example.tld", null);
|
||||||
|
|
|
@ -59,6 +59,7 @@ import google.registry.flows.host.HostFlowUtils.HostNameTooLongException;
|
||||||
import google.registry.flows.host.HostFlowUtils.HostNameTooShallowException;
|
import google.registry.flows.host.HostFlowUtils.HostNameTooShallowException;
|
||||||
import google.registry.flows.host.HostFlowUtils.InvalidHostNameException;
|
import google.registry.flows.host.HostFlowUtils.InvalidHostNameException;
|
||||||
import google.registry.flows.host.HostFlowUtils.SuperordinateDomainDoesNotExistException;
|
import google.registry.flows.host.HostFlowUtils.SuperordinateDomainDoesNotExistException;
|
||||||
|
import google.registry.flows.host.HostFlowUtils.SuperordinateDomainInPendingDeleteException;
|
||||||
import google.registry.flows.host.HostUpdateFlow.CannotAddIpToExternalHostException;
|
import google.registry.flows.host.HostUpdateFlow.CannotAddIpToExternalHostException;
|
||||||
import google.registry.flows.host.HostUpdateFlow.CannotRemoveSubordinateHostLastIpException;
|
import google.registry.flows.host.HostUpdateFlow.CannotRemoveSubordinateHostLastIpException;
|
||||||
import google.registry.flows.host.HostUpdateFlow.CannotRenameExternalHostException;
|
import google.registry.flows.host.HostUpdateFlow.CannotRenameExternalHostException;
|
||||||
|
@ -762,6 +763,28 @@ public class HostUpdateFlowTest extends ResourceFlowTestCase<HostUpdateFlow, Hos
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFailure_superordinateInPendingDelete() throws Exception {
|
||||||
|
setEppHostUpdateInput(
|
||||||
|
"ns1.example.tld",
|
||||||
|
"ns2.example.tld",
|
||||||
|
"<host:addr ip=\"v4\">192.0.2.22</host:addr>",
|
||||||
|
"<host:addr ip=\"v6\">1080:0:0:0:8:800:200C:417A</host:addr>");
|
||||||
|
createTld("tld");
|
||||||
|
DomainResource domain = persistResource(newDomainResource("example.tld")
|
||||||
|
.asBuilder()
|
||||||
|
.setSubordinateHosts(ImmutableSet.of(oldHostName()))
|
||||||
|
.setDeletionTime(clock.nowUtc().plusDays(35))
|
||||||
|
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
|
||||||
|
.build());
|
||||||
|
persistActiveSubordinateHost(oldHostName(), domain);
|
||||||
|
clock.advanceOneMilli();
|
||||||
|
thrown.expect(
|
||||||
|
SuperordinateDomainInPendingDeleteException.class,
|
||||||
|
"example.tld");
|
||||||
|
runFlow();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_neverExisted() throws Exception {
|
public void testFailure_neverExisted() throws Exception {
|
||||||
thrown.expect(
|
thrown.expect(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue