diff --git a/java/google/registry/flows/contact/ContactCheckFlow.java b/java/google/registry/flows/contact/ContactCheckFlow.java index 5c40bd317..1e1346dd3 100644 --- a/java/google/registry/flows/contact/ContactCheckFlow.java +++ b/java/google/registry/flows/contact/ContactCheckFlow.java @@ -15,34 +15,46 @@ package google.registry.flows.contact; import static google.registry.model.EppResourceUtils.checkResourcesExist; +import static google.registry.model.eppoutput.Result.Code.Success; import com.google.common.collect.ImmutableList; -import google.registry.flows.ResourceCheckFlow; +import google.registry.config.ConfigModule.Config; +import google.registry.flows.EppException; +import google.registry.flows.LoggedInFlow; +import google.registry.flows.exceptions.TooManyResourceChecksException; import google.registry.model.contact.ContactCommand.Check; import google.registry.model.contact.ContactResource; -import google.registry.model.eppoutput.CheckData; +import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppoutput.CheckData.ContactCheck; import google.registry.model.eppoutput.CheckData.ContactCheckData; +import google.registry.model.eppoutput.EppOutput; +import java.util.List; import java.util.Set; import javax.inject.Inject; /** * An EPP flow that checks whether a contact can be provisioned. * - * @error {@link google.registry.flows.ResourceCheckFlow.TooManyResourceChecksException} + * @error {@link google.registry.flows.exceptions.TooManyResourceChecksException} */ -public class ContactCheckFlow extends ResourceCheckFlow { +public class ContactCheckFlow extends LoggedInFlow { + @Inject ResourceCommand resourceCommand; + @Inject @Config("maxChecks") int maxChecks; @Inject ContactCheckFlow() {} @Override - protected CheckData getCheckData() { - Set existingIds = checkResourcesExist(resourceClass, targetIds, now); + public final EppOutput run() throws EppException { + List targetIds = ((Check) resourceCommand).getTargetIds(); + if (targetIds.size() > maxChecks) { + throw new TooManyResourceChecksException(maxChecks); + } + Set existingIds = checkResourcesExist(ContactResource.class, targetIds, now); ImmutableList.Builder checks = new ImmutableList.Builder<>(); for (String id : targetIds) { boolean unused = !existingIds.contains(id); checks.add(ContactCheck.create(unused, id, unused ? null : "In use")); } - return ContactCheckData.create(checks.build()); + return createOutput(Success, ContactCheckData.create(checks.build()), null); } } diff --git a/java/google/registry/flows/contact/ContactCreateFlow.java b/java/google/registry/flows/contact/ContactCreateFlow.java index b17ebd383..f034347ae 100644 --- a/java/google/registry/flows/contact/ContactCreateFlow.java +++ b/java/google/registry/flows/contact/ContactCreateFlow.java @@ -17,15 +17,24 @@ package google.registry.flows.contact; import static google.registry.flows.contact.ContactFlowUtils.validateAsciiPostalInfo; import static google.registry.flows.contact.ContactFlowUtils.validateContactAgainstPolicy; import static google.registry.model.EppResourceUtils.createContactHostRoid; +import static google.registry.model.EppResourceUtils.loadByUniqueId; import static google.registry.model.eppoutput.Result.Code.Success; +import static google.registry.model.ofy.ObjectifyService.ofy; +import com.googlecode.objectify.Key; import google.registry.flows.EppException; -import google.registry.flows.ResourceCreateFlow; +import google.registry.flows.LoggedInFlow; +import google.registry.flows.TransactionalFlow; +import google.registry.flows.exceptions.ResourceAlreadyExistsException; import google.registry.model.contact.ContactCommand.Create; import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource.Builder; +import google.registry.model.domain.metadata.MetadataExtension; +import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppoutput.CreateData.ContactCreateData; import google.registry.model.eppoutput.EppOutput; +import google.registry.model.index.EppResourceIndex; +import google.registry.model.index.ForeignKeyIndex; import google.registry.model.ofy.ObjectifyService; import google.registry.model.reporting.HistoryEntry; import javax.inject.Inject; @@ -33,37 +42,46 @@ import javax.inject.Inject; /** * An EPP flow that creates a new contact resource. * - * @error {@link google.registry.flows.ResourceCreateFlow.ResourceAlreadyExistsException} + * @error {@link google.registry.flows.exceptions.ResourceAlreadyExistsException} * @error {@link ContactFlowUtils.BadInternationalizedPostalInfoException} * @error {@link ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException} */ -public class ContactCreateFlow extends ResourceCreateFlow { +public class ContactCreateFlow extends LoggedInFlow implements TransactionalFlow { + @Inject ResourceCommand resourceCommand; + @Inject HistoryEntry.Builder historyBuilder; @Inject ContactCreateFlow() {} @Override - protected EppOutput getOutput() { - return createOutput(Success, ContactCreateData.create(newResource.getContactId(), now)); + protected final void initLoggedInFlow() throws EppException { + registerExtensions(MetadataExtension.class); } @Override - protected String createFlowRepoId() { - return createContactHostRoid(ObjectifyService.allocateId()); - } - - @Override - protected void verifyNewStateIsAllowed() throws EppException { + protected final EppOutput run() throws EppException { + Create command = (Create) resourceCommand; + if (loadByUniqueId(ContactResource.class, command.getTargetId(), now) != null) { + throw new ResourceAlreadyExistsException(command.getTargetId()); + } + Builder builder = new Builder(); + command.applyTo(builder); + ContactResource newResource = builder + .setCreationClientId(getClientId()) + .setCurrentSponsorClientId(getClientId()) + .setRepoId(createContactHostRoid(ObjectifyService.allocateId())) + .build(); validateAsciiPostalInfo(newResource.getInternationalizedPostalInfo()); validateContactAgainstPolicy(newResource); - } - - @Override - protected boolean storeXmlInHistoryEntry() { - return false; - } - - @Override - protected final HistoryEntry.Type getHistoryEntryType() { - return HistoryEntry.Type.CONTACT_CREATE; + historyBuilder + .setType(HistoryEntry.Type.CONTACT_CREATE) + .setModificationTime(now) + .setXmlBytes(null) // We don't want to store contact details in the history entry. + .setParent(Key.create(newResource)); + ofy().save().entities( + newResource, + historyBuilder.build(), + ForeignKeyIndex.create(newResource, newResource.getDeletionTime()), + EppResourceIndex.create(Key.create(newResource))); + return createOutput(Success, ContactCreateData.create(newResource.getContactId(), now)); } } diff --git a/java/google/registry/flows/contact/ContactDeleteFlow.java b/java/google/registry/flows/contact/ContactDeleteFlow.java index f0424df2e..09d2affe3 100644 --- a/java/google/registry/flows/contact/ContactDeleteFlow.java +++ b/java/google/registry/flows/contact/ContactDeleteFlow.java @@ -14,61 +14,87 @@ package google.registry.flows.contact; -import static google.registry.model.EppResourceUtils.queryDomainsUsingResource; +import static google.registry.flows.ResourceFlowUtils.failfastForAsyncDelete; +import static google.registry.flows.ResourceFlowUtils.verifyAuthInfoForResource; +import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses; +import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership; +import static google.registry.model.EppResourceUtils.loadByUniqueId; +import static google.registry.model.eppoutput.Result.Code.SuccessWithActionPending; import static google.registry.model.ofy.ObjectifyService.ofy; -import com.google.common.base.Predicate; +import com.google.common.base.Function; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; +import com.google.common.collect.ImmutableSet; import com.googlecode.objectify.Key; -import google.registry.config.RegistryEnvironment; +import google.registry.config.ConfigModule.Config; import google.registry.flows.EppException; -import google.registry.flows.ResourceAsyncDeleteFlow; +import google.registry.flows.LoggedInFlow; +import google.registry.flows.TransactionalFlow; import google.registry.flows.async.AsyncFlowUtils; import google.registry.flows.async.DeleteContactResourceAction; import google.registry.flows.async.DeleteEppResourceAction; +import google.registry.flows.exceptions.ResourceToMutateDoesNotExistException; import google.registry.model.contact.ContactCommand.Delete; import google.registry.model.contact.ContactResource; -import google.registry.model.contact.ContactResource.Builder; import google.registry.model.domain.DomainBase; +import google.registry.model.domain.metadata.MetadataExtension; +import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppinput.ResourceCommand; +import google.registry.model.eppoutput.EppOutput; import google.registry.model.reporting.HistoryEntry; import javax.inject.Inject; +import org.joda.time.Duration; /** * An EPP flow that deletes a contact resource. * - * @error {@link google.registry.flows.ResourceAsyncDeleteFlow.ResourceToDeleteIsReferencedException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException} - * @error {@link google.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException} - * @error {@link google.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException} + * @error {@link google.registry.flows.exceptions.ResourceStatusProhibitsOperationException} + * @error {@link google.registry.flows.exceptions.ResourceToDeleteIsReferencedException} + * @error {@link google.registry.flows.exceptions.ResourceToMutateDoesNotExistException} */ -public class ContactDeleteFlow extends ResourceAsyncDeleteFlow { +public class ContactDeleteFlow extends LoggedInFlow implements TransactionalFlow { - /** In {@link #isLinkedForFailfast}, check this (arbitrary) number of resources from the query. */ - private static final int FAILFAST_CHECK_COUNT = 5; + private static final ImmutableSet DISALLOWED_STATUSES = ImmutableSet.of( + StatusValue.LINKED, + StatusValue.CLIENT_DELETE_PROHIBITED, + StatusValue.PENDING_DELETE, + StatusValue.SERVER_DELETE_PROHIBITED); + @Inject ResourceCommand resourceCommand; + @Inject @Config("asyncDeleteFlowMapreduceDelay") Duration mapreduceDelay; + @Inject HistoryEntry.Builder historyBuilder; @Inject ContactDeleteFlow() {} @Override - protected boolean isLinkedForFailfast(final Key key) { - // Query for the first few linked domains, and if found, actually load them. The query is - // eventually consistent and so might be very stale, but the direct load will not be stale, - // just non-transactional. If we find at least one actual reference then we can reliably - // fail. If we don't find any, we can't trust the query and need to do the full mapreduce. - return Iterables.any( - ofy().load().keys( - queryDomainsUsingResource( - ContactResource.class, key, now, FAILFAST_CHECK_COUNT)).values(), - new Predicate() { - @Override - public boolean apply(DomainBase domain) { - return domain.getReferencedContacts().contains(key); - }}); + protected final void initLoggedInFlow() throws EppException { + registerExtensions(MetadataExtension.class); } - /** Enqueues a contact resource deletion on the mapreduce queue. */ @Override - protected final void enqueueTasks() throws EppException { + public final EppOutput run() throws EppException { + Delete command = (Delete) resourceCommand; + String targetId = command.getTargetId(); + failfastForAsyncDelete( + targetId, + now, + ContactResource.class, + new Function>() { + @Override + public ImmutableSet apply(DomainBase domain) { + return domain.getReferencedContacts(); + }}); + ContactResource existingResource = loadByUniqueId(ContactResource.class, targetId, now); + if (existingResource == null) { + throw new ResourceToMutateDoesNotExistException(ContactResource.class, targetId); + } + verifyNoDisallowedStatuses(existingResource, DISALLOWED_STATUSES); + if (command.getAuthInfo() != null) { + verifyAuthInfoForResource(command.getAuthInfo(), existingResource); + } + if (!isSuperuser) { + verifyResourceOwnership(getClientId(), existingResource); + } AsyncFlowUtils.enqueueMapreduceAction( DeleteContactResourceAction.class, ImmutableMap.of( @@ -78,11 +104,14 @@ public class ContactDeleteFlow extends ResourceAsyncDeleteFlowentities(newResource, historyBuilder.build()); + return createOutput(SuccessWithActionPending, null, null); } } diff --git a/java/google/registry/flows/contact/ContactInfoFlow.java b/java/google/registry/flows/contact/ContactInfoFlow.java index d935d39ca..350da8f24 100644 --- a/java/google/registry/flows/contact/ContactInfoFlow.java +++ b/java/google/registry/flows/contact/ContactInfoFlow.java @@ -14,17 +14,41 @@ package google.registry.flows.contact; -import google.registry.flows.ResourceInfoFlow; +import static google.registry.flows.ResourceFlowUtils.verifyAuthInfoForResource; +import static google.registry.model.EppResourceUtils.cloneResourceWithLinkedStatus; +import static google.registry.model.EppResourceUtils.loadByUniqueId; +import static google.registry.model.eppoutput.Result.Code.Success; + +import google.registry.flows.EppException; +import google.registry.flows.LoggedInFlow; +import google.registry.flows.exceptions.ResourceToQueryDoesNotExistException; import google.registry.model.contact.ContactCommand.Info; import google.registry.model.contact.ContactResource; +import google.registry.model.eppinput.ResourceCommand; +import google.registry.model.eppoutput.EppOutput; import javax.inject.Inject; /** * An EPP flow that reads a contact. * - * @error {@link google.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException} + * @error {@link google.registry.flows.exceptions.ResourceToQueryDoesNotExistException} */ -public class ContactInfoFlow extends ResourceInfoFlow { - @Inject ContactInfoFlow() {} -} +public class ContactInfoFlow extends LoggedInFlow { + @Inject ResourceCommand resourceCommand; + @Inject ContactInfoFlow() {} + + @Override + public final EppOutput run() throws EppException { + Info command = (Info) resourceCommand; + String targetId = command.getTargetId(); + ContactResource existingResource = loadByUniqueId(ContactResource.class, targetId, now); + if (existingResource == null) { + throw new ResourceToQueryDoesNotExistException(ContactResource.class, targetId); + } + if (command.getAuthInfo() != null) { + verifyAuthInfoForResource(command.getAuthInfo(), existingResource); + } + return createOutput(Success, cloneResourceWithLinkedStatus(existingResource, now), null); + } +} diff --git a/java/google/registry/flows/contact/ContactTransferApproveFlow.java b/java/google/registry/flows/contact/ContactTransferApproveFlow.java index 724b11250..f68f6e27b 100644 --- a/java/google/registry/flows/contact/ContactTransferApproveFlow.java +++ b/java/google/registry/flows/contact/ContactTransferApproveFlow.java @@ -14,11 +14,31 @@ package google.registry.flows.contact; -import google.registry.flows.ResourceTransferApproveFlow; +import static google.registry.flows.ResourceFlowUtils.createPendingTransferNotificationResponse; +import static google.registry.flows.ResourceFlowUtils.createTransferResponse; +import static google.registry.flows.ResourceFlowUtils.verifyAuthInfoForResource; +import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership; +import static google.registry.model.EppResourceUtils.loadByUniqueId; +import static google.registry.model.eppoutput.Result.Code.Success; +import static google.registry.model.ofy.ObjectifyService.ofy; + +import com.google.common.collect.ImmutableList; +import com.googlecode.objectify.Key; +import google.registry.flows.EppException; +import google.registry.flows.LoggedInFlow; +import google.registry.flows.TransactionalFlow; +import google.registry.flows.exceptions.NotPendingTransferException; +import google.registry.flows.exceptions.ResourceToMutateDoesNotExistException; import google.registry.model.contact.ContactCommand.Transfer; import google.registry.model.contact.ContactResource; -import google.registry.model.contact.ContactResource.Builder; +import google.registry.model.domain.metadata.MetadataExtension; +import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppinput.ResourceCommand; +import google.registry.model.eppoutput.EppOutput; +import google.registry.model.poll.PollMessage; import google.registry.model.reporting.HistoryEntry; +import google.registry.model.transfer.TransferData; +import google.registry.model.transfer.TransferStatus; import javax.inject.Inject; /** @@ -26,16 +46,71 @@ import javax.inject.Inject; * * @error {@link google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException} - * @error {@link google.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException} - * @error {@link google.registry.flows.ResourceMutatePendingTransferFlow.NotPendingTransferException} + * @error {@link google.registry.flows.exceptions.NotPendingTransferException} + * @error {@link google.registry.flows.exceptions.ResourceToMutateDoesNotExistException} */ -public class ContactTransferApproveFlow - extends ResourceTransferApproveFlow { +public class ContactTransferApproveFlow extends LoggedInFlow implements TransactionalFlow { + @Inject ResourceCommand resourceCommand; + @Inject HistoryEntry.Builder historyBuilder; @Inject ContactTransferApproveFlow() {} @Override - protected final HistoryEntry.Type getHistoryEntryType() { - return HistoryEntry.Type.CONTACT_TRANSFER_APPROVE; + protected final void initLoggedInFlow() throws EppException { + registerExtensions(MetadataExtension.class); + } + + @Override + public final EppOutput run() throws EppException { + Transfer command = (Transfer) resourceCommand; + String targetId = command.getTargetId(); + ContactResource existingResource = loadByUniqueId(ContactResource.class, targetId, now); + if (existingResource == null) { + throw new ResourceToMutateDoesNotExistException(ContactResource.class, targetId); + } + if (command.getAuthInfo() != null) { + verifyAuthInfoForResource(command.getAuthInfo(), existingResource); + } + if (existingResource.getTransferData().getTransferStatus() != TransferStatus.PENDING) { + throw new NotPendingTransferException(targetId); + } + verifyResourceOwnership(getClientId(), existingResource); + TransferData oldTransferData = existingResource.getTransferData(); + ContactResource newResource = existingResource.asBuilder() + .removeStatusValue(StatusValue.PENDING_TRANSFER) + .setLastTransferTime(now) + .setCurrentSponsorClientId(existingResource.getTransferData().getGainingClientId()) + .setTransferData(oldTransferData.asBuilder() + .setTransferStatus(TransferStatus.CLIENT_APPROVED) + .setPendingTransferExpirationTime(now) + .setExtendedRegistrationYears(null) + .setServerApproveEntities(null) + .setServerApproveBillingEvent(null) + .setServerApproveAutorenewEvent(null) + .setServerApproveAutorenewPollMessage(null) + .build()) + .build(); + HistoryEntry historyEntry = historyBuilder + .setType(HistoryEntry.Type.CONTACT_TRANSFER_APPROVE) + .setModificationTime(now) + .setParent(Key.create(existingResource)) + .build(); + // Create a poll message for the gaining client. + PollMessage gainingPollMessage = new PollMessage.OneTime.Builder() + .setClientId(oldTransferData.getGainingClientId()) + .setEventTime(now) + .setMsg(TransferStatus.CLIENT_APPROVED.getMessage()) + .setResponseData(ImmutableList.of( + createTransferResponse(newResource, newResource.getTransferData(), now), + createPendingTransferNotificationResponse( + existingResource, oldTransferData.getTransferRequestTrid(), true, now))) + .setParent(historyEntry) + .build(); + ofy().save().entities(newResource, historyEntry, gainingPollMessage); + // Delete the billing event and poll messages that were written in case the transfer would have + // been implicitly server approved. + ofy().delete().keys(existingResource.getTransferData().getServerApproveEntities()); + return createOutput( + Success, createTransferResponse(newResource, newResource.getTransferData(), now)); } } diff --git a/java/google/registry/flows/contact/ContactTransferCancelFlow.java b/java/google/registry/flows/contact/ContactTransferCancelFlow.java index 52f786951..689ec4093 100644 --- a/java/google/registry/flows/contact/ContactTransferCancelFlow.java +++ b/java/google/registry/flows/contact/ContactTransferCancelFlow.java @@ -14,28 +14,101 @@ package google.registry.flows.contact; -import google.registry.flows.ResourceTransferCancelFlow; +import static google.registry.flows.ResourceFlowUtils.createTransferResponse; +import static google.registry.flows.ResourceFlowUtils.verifyAuthInfoForResource; +import static google.registry.model.EppResourceUtils.loadByUniqueId; +import static google.registry.model.eppoutput.Result.Code.Success; +import static google.registry.model.ofy.ObjectifyService.ofy; + +import com.google.common.collect.ImmutableList; +import com.googlecode.objectify.Key; +import google.registry.flows.EppException; +import google.registry.flows.LoggedInFlow; +import google.registry.flows.TransactionalFlow; +import google.registry.flows.exceptions.NotPendingTransferException; +import google.registry.flows.exceptions.NotTransferInitiatorException; +import google.registry.flows.exceptions.ResourceToMutateDoesNotExistException; import google.registry.model.contact.ContactCommand.Transfer; import google.registry.model.contact.ContactResource; -import google.registry.model.contact.ContactResource.Builder; +import google.registry.model.domain.metadata.MetadataExtension; +import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppinput.ResourceCommand; +import google.registry.model.eppoutput.EppOutput; +import google.registry.model.poll.PollMessage; import google.registry.model.reporting.HistoryEntry; +import google.registry.model.transfer.TransferStatus; import javax.inject.Inject; /** * An EPP flow that cancels a pending transfer on a {@link ContactResource}. * * @error {@link google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException} - * @error {@link google.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException} - * @error {@link google.registry.flows.ResourceMutatePendingTransferFlow.NotPendingTransferException} - * @error {@link google.registry.flows.ResourceTransferCancelFlow.NotTransferInitiatorException} + * @error {@link google.registry.flows.exceptions.NotPendingTransferException} + * @error {@link google.registry.flows.exceptions.NotTransferInitiatorException} + * @error {@link google.registry.flows.exceptions.ResourceToMutateDoesNotExistException} */ -public class ContactTransferCancelFlow - extends ResourceTransferCancelFlow { +public class ContactTransferCancelFlow extends LoggedInFlow implements TransactionalFlow { + @Inject ResourceCommand resourceCommand; + @Inject HistoryEntry.Builder historyBuilder; @Inject ContactTransferCancelFlow() {} @Override - protected final HistoryEntry.Type getHistoryEntryType() { - return HistoryEntry.Type.CONTACT_TRANSFER_CANCEL; + protected final void initLoggedInFlow() throws EppException { + registerExtensions(MetadataExtension.class); + } + + @Override + protected final EppOutput run() throws EppException { + Transfer command = (Transfer) resourceCommand; + String targetId = command.getTargetId(); + ContactResource existingResource = loadByUniqueId(ContactResource.class, targetId, now); + // Fail if the object doesn't exist or was deleted. + if (existingResource == null) { + throw new ResourceToMutateDoesNotExistException(ContactResource.class, targetId); + } + if (command.getAuthInfo() != null) { + verifyAuthInfoForResource(command.getAuthInfo(), existingResource); + } + // Fail if object doesn't have a pending transfer, or if authinfo doesn't match. */ + if (existingResource.getTransferData().getTransferStatus() != TransferStatus.PENDING) { + throw new NotPendingTransferException(targetId); + } + // TODO(b/18997997): Determine if authInfo is necessary to cancel a transfer. + if (!getClientId().equals(existingResource.getTransferData().getGainingClientId())) { + throw new NotTransferInitiatorException(); + } + ContactResource newResource = existingResource.asBuilder() + .removeStatusValue(StatusValue.PENDING_TRANSFER) + .setTransferData(existingResource.getTransferData().asBuilder() + .setTransferStatus(TransferStatus.CLIENT_CANCELLED) + .setPendingTransferExpirationTime(now) + .setExtendedRegistrationYears(null) + .setServerApproveEntities(null) + .setServerApproveBillingEvent(null) + .setServerApproveAutorenewEvent(null) + .setServerApproveAutorenewPollMessage(null) + .build()) + .build(); + HistoryEntry historyEntry = historyBuilder + .setType(HistoryEntry.Type.CONTACT_TRANSFER_CANCEL) + .setModificationTime(now) + .setParent(Key.create(existingResource)) + .build(); + // Create a poll message for the losing client. + PollMessage losingPollMessage = new PollMessage.OneTime.Builder() + .setClientId(existingResource.getTransferData().getLosingClientId()) + .setEventTime(now) + .setMsg(TransferStatus.CLIENT_CANCELLED.getMessage()) + .setResponseData(ImmutableList.of( + createTransferResponse(newResource, newResource.getTransferData(), now))) + .setParent(historyEntry) + .build(); + ofy().save().entities(newResource, historyEntry, losingPollMessage); + // Delete the billing event and poll messages that were written in case the transfer would have + // been implicitly server approved. + ofy().delete().keys(existingResource.getTransferData().getServerApproveEntities()); + return createOutput( + Success, createTransferResponse(newResource, newResource.getTransferData(), now)); } } diff --git a/java/google/registry/flows/contact/ContactTransferQueryFlow.java b/java/google/registry/flows/contact/ContactTransferQueryFlow.java index 51d514d3a..5d7786dbb 100644 --- a/java/google/registry/flows/contact/ContactTransferQueryFlow.java +++ b/java/google/registry/flows/contact/ContactTransferQueryFlow.java @@ -14,19 +14,59 @@ package google.registry.flows.contact; -import google.registry.flows.ResourceTransferQueryFlow; +import static google.registry.flows.ResourceFlowUtils.createTransferResponse; +import static google.registry.flows.ResourceFlowUtils.verifyAuthInfoForResource; +import static google.registry.model.EppResourceUtils.loadByUniqueId; +import static google.registry.model.eppoutput.Result.Code.Success; + +import google.registry.flows.EppException; +import google.registry.flows.LoggedInFlow; +import google.registry.flows.exceptions.NoTransferHistoryToQueryException; +import google.registry.flows.exceptions.NotAuthorizedToViewTransferException; +import google.registry.flows.exceptions.ResourceToQueryDoesNotExistException; import google.registry.model.contact.ContactCommand.Transfer; import google.registry.model.contact.ContactResource; +import google.registry.model.eppinput.ResourceCommand; +import google.registry.model.eppoutput.EppOutput; import javax.inject.Inject; /** * An EPP flow that queries a pending transfer on a {@link ContactResource}. * * @error {@link google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException} - * @error {@link google.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException} - * @error {@link google.registry.flows.ResourceTransferQueryFlow.NoTransferHistoryToQueryException} - * @error {@link google.registry.flows.ResourceTransferQueryFlow.NotAuthorizedToViewTransferException} + * @error {@link google.registry.flows.exceptions.NoTransferHistoryToQueryException} + * @error {@link google.registry.flows.exceptions.NotAuthorizedToViewTransferException} + * @error {@link google.registry.flows.exceptions.ResourceToQueryDoesNotExistException} */ -public class ContactTransferQueryFlow extends ResourceTransferQueryFlow { +public class ContactTransferQueryFlow extends LoggedInFlow { + + @Inject ResourceCommand resourceCommand; @Inject ContactTransferQueryFlow() {} + + @Override + public final EppOutput run() throws EppException { + Transfer command = (Transfer) resourceCommand; + String targetId = command.getTargetId(); + ContactResource existingResource = loadByUniqueId(ContactResource.class, targetId, now); + if (existingResource == null) { + throw new ResourceToQueryDoesNotExistException(ContactResource.class, targetId); + } + if (command.getAuthInfo() != null) { + verifyAuthInfoForResource(command.getAuthInfo(), existingResource); + } + // Most of the fields on the transfer response are required, so there's no way to return valid + // XML if the object has never been transferred (and hence the fields aren't populated). + if (existingResource.getTransferData().getTransferStatus() == null) { + throw new NoTransferHistoryToQueryException(); + } + // Note that the authorization info on the command (if present) has already been verified by the + // parent class. If it's present, then the other checks are unnecessary. + if (command.getAuthInfo() == null + && !getClientId().equals(existingResource.getTransferData().getGainingClientId()) + && !getClientId().equals(existingResource.getTransferData().getLosingClientId())) { + throw new NotAuthorizedToViewTransferException(); + } + return createOutput( + Success, createTransferResponse(existingResource, existingResource.getTransferData(), now)); + } } diff --git a/java/google/registry/flows/contact/ContactTransferRejectFlow.java b/java/google/registry/flows/contact/ContactTransferRejectFlow.java index 78a93c8f7..b564399e4 100644 --- a/java/google/registry/flows/contact/ContactTransferRejectFlow.java +++ b/java/google/registry/flows/contact/ContactTransferRejectFlow.java @@ -14,11 +14,31 @@ package google.registry.flows.contact; -import google.registry.flows.ResourceTransferRejectFlow; +import static google.registry.flows.ResourceFlowUtils.createPendingTransferNotificationResponse; +import static google.registry.flows.ResourceFlowUtils.createTransferResponse; +import static google.registry.flows.ResourceFlowUtils.verifyAuthInfoForResource; +import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership; +import static google.registry.model.EppResourceUtils.loadByUniqueId; +import static google.registry.model.eppoutput.Result.Code.Success; +import static google.registry.model.ofy.ObjectifyService.ofy; + +import com.google.common.collect.ImmutableList; +import com.googlecode.objectify.Key; +import google.registry.flows.EppException; +import google.registry.flows.LoggedInFlow; +import google.registry.flows.TransactionalFlow; +import google.registry.flows.exceptions.NotPendingTransferException; +import google.registry.flows.exceptions.ResourceToMutateDoesNotExistException; import google.registry.model.contact.ContactCommand.Transfer; import google.registry.model.contact.ContactResource; -import google.registry.model.contact.ContactResource.Builder; +import google.registry.model.domain.metadata.MetadataExtension; +import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppinput.ResourceCommand; +import google.registry.model.eppoutput.EppOutput; +import google.registry.model.poll.PollMessage; import google.registry.model.reporting.HistoryEntry; +import google.registry.model.transfer.TransferData; +import google.registry.model.transfer.TransferStatus; import javax.inject.Inject; /** @@ -26,16 +46,68 @@ import javax.inject.Inject; * * @error {@link google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException} - * @error {@link google.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException} - * @error {@link google.registry.flows.ResourceMutatePendingTransferFlow.NotPendingTransferException} + * @error {@link google.registry.flows.exceptions.NotPendingTransferException} + * @error {@link google.registry.flows.exceptions.ResourceToMutateDoesNotExistException} */ -public class ContactTransferRejectFlow - extends ResourceTransferRejectFlow { +public class ContactTransferRejectFlow extends LoggedInFlow implements TransactionalFlow { + @Inject ResourceCommand resourceCommand; + @Inject HistoryEntry.Builder historyBuilder; @Inject ContactTransferRejectFlow() {} @Override - protected final HistoryEntry.Type getHistoryEntryType() { - return HistoryEntry.Type.CONTACT_TRANSFER_REJECT; + protected final void initLoggedInFlow() throws EppException { + registerExtensions(MetadataExtension.class); + } + + @Override + protected final EppOutput run() throws EppException { + Transfer command = (Transfer) resourceCommand; + String targetId = command.getTargetId(); + ContactResource existingResource = loadByUniqueId(ContactResource.class, targetId, now); + if (existingResource == null) { + throw new ResourceToMutateDoesNotExistException(ContactResource.class, targetId); + } + if (command.getAuthInfo() != null) { + verifyAuthInfoForResource(command.getAuthInfo(), existingResource); + } + if (existingResource.getTransferData().getTransferStatus() != TransferStatus.PENDING) { + throw new NotPendingTransferException(targetId); + } + verifyResourceOwnership(getClientId(), existingResource); + TransferData oldTransferData = existingResource.getTransferData(); + ContactResource newResource = existingResource.asBuilder() + .removeStatusValue(StatusValue.PENDING_TRANSFER) + .setTransferData(oldTransferData.asBuilder() + .setTransferStatus(TransferStatus.CLIENT_REJECTED) + .setPendingTransferExpirationTime(now) + .setExtendedRegistrationYears(null) + .setServerApproveEntities(null) + .setServerApproveBillingEvent(null) + .setServerApproveAutorenewEvent(null) + .setServerApproveAutorenewPollMessage(null) + .build()) + .build(); + HistoryEntry historyEntry = historyBuilder + .setType(HistoryEntry.Type.CONTACT_TRANSFER_REJECT) + .setModificationTime(now) + .setParent(Key.create(existingResource)) + .build(); + PollMessage.OneTime gainingPollMessage = new PollMessage.OneTime.Builder() + .setClientId(oldTransferData.getGainingClientId()) + .setEventTime(now) + .setMsg(TransferStatus.CLIENT_REJECTED.getMessage()) + .setResponseData(ImmutableList.of( + createTransferResponse(newResource, newResource.getTransferData(), now), + createPendingTransferNotificationResponse( + existingResource, oldTransferData.getTransferRequestTrid(), false, now))) + .setParent(historyEntry) + .build(); + ofy().save().entities(newResource, historyEntry, gainingPollMessage); + // Delete the billing event and poll messages that were written in case the transfer would have + // been implicitly server approved. + ofy().delete().keys(existingResource.getTransferData().getServerApproveEntities()); + return createOutput( + Success, createTransferResponse(newResource, newResource.getTransferData(), now)); } } diff --git a/java/google/registry/flows/contact/ContactTransferRequestFlow.java b/java/google/registry/flows/contact/ContactTransferRequestFlow.java index 546619bbe..357b81ccc 100644 --- a/java/google/registry/flows/contact/ContactTransferRequestFlow.java +++ b/java/google/registry/flows/contact/ContactTransferRequestFlow.java @@ -14,35 +14,151 @@ package google.registry.flows.contact; -import google.registry.config.RegistryEnvironment; -import google.registry.flows.ResourceTransferRequestFlow; +import static google.registry.flows.ResourceFlowUtils.createPendingTransferNotificationResponse; +import static google.registry.flows.ResourceFlowUtils.createTransferResponse; +import static google.registry.flows.ResourceFlowUtils.verifyAuthInfoForResource; +import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses; +import static google.registry.model.EppResourceUtils.loadByUniqueId; +import static google.registry.model.eppoutput.Result.Code.SuccessWithActionPending; +import static google.registry.model.ofy.ObjectifyService.ofy; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.googlecode.objectify.Key; +import google.registry.config.ConfigModule.Config; +import google.registry.flows.EppException; +import google.registry.flows.LoggedInFlow; +import google.registry.flows.TransactionalFlow; +import google.registry.flows.exceptions.AlreadyPendingTransferException; +import google.registry.flows.exceptions.MissingTransferRequestAuthInfoException; +import google.registry.flows.exceptions.ObjectAlreadySponsoredException; +import google.registry.flows.exceptions.ResourceToMutateDoesNotExistException; import google.registry.model.contact.ContactCommand.Transfer; import google.registry.model.contact.ContactResource; +import google.registry.model.domain.metadata.MetadataExtension; +import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppinput.ResourceCommand; +import google.registry.model.eppoutput.EppOutput; +import google.registry.model.poll.PollMessage; import google.registry.model.reporting.HistoryEntry; +import google.registry.model.transfer.TransferData; +import google.registry.model.transfer.TransferStatus; import javax.inject.Inject; +import org.joda.time.DateTime; import org.joda.time.Duration; /** * An EPP flow that requests a transfer on a {@link ContactResource}. * * @error {@link google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException} - * @error {@link google.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException} - * @error {@link google.registry.flows.ResourceTransferRequestFlow.AlreadyPendingTransferException} - * @error {@link google.registry.flows.ResourceTransferRequestFlow.MissingTransferRequestAuthInfoException} - * @error {@link google.registry.flows.ResourceTransferRequestFlow.ObjectAlreadySponsoredException} + * @error {@link google.registry.flows.exceptions.ResourceToMutateDoesNotExistException} + * @error {@link google.registry.flows.exceptions.AlreadyPendingTransferException} + * @error {@link google.registry.flows.exceptions.MissingTransferRequestAuthInfoException} + * @error {@link google.registry.flows.exceptions.ObjectAlreadySponsoredException} */ -public class ContactTransferRequestFlow - extends ResourceTransferRequestFlow { +public class ContactTransferRequestFlow extends LoggedInFlow implements TransactionalFlow { + private static final ImmutableSet DISALLOWED_STATUSES = ImmutableSet.of( + StatusValue.CLIENT_TRANSFER_PROHIBITED, + StatusValue.PENDING_DELETE, + StatusValue.SERVER_TRANSFER_PROHIBITED); + + @Inject ResourceCommand resourceCommand; + @Inject @Config("contactAutomaticTransferLength") Duration automaticTransferLength; + @Inject HistoryEntry.Builder historyBuilder; @Inject ContactTransferRequestFlow() {} @Override - protected final HistoryEntry.Type getHistoryEntryType() { - return HistoryEntry.Type.CONTACT_TRANSFER_REQUEST; + protected final void initLoggedInFlow() throws EppException { + registerExtensions(MetadataExtension.class); } @Override - protected Duration getAutomaticTransferLength() { - return RegistryEnvironment.get().config().getContactAutomaticTransferLength(); + protected final EppOutput run() throws EppException { + Transfer command = (Transfer) resourceCommand; + String targetId = command.getTargetId(); + ContactResource existingResource = loadByUniqueId(ContactResource.class, targetId, now); + if (existingResource == null) { + throw new ResourceToMutateDoesNotExistException(ContactResource.class, targetId); + } + if (command.getAuthInfo() == null) { + throw new MissingTransferRequestAuthInfoException(); + } + verifyAuthInfoForResource(command.getAuthInfo(), existingResource); + // Verify that the resource does not already have a pending transfer. + if (TransferStatus.PENDING.equals(existingResource.getTransferData().getTransferStatus())) { + throw new AlreadyPendingTransferException(targetId); + } + String gainingClientId = getClientId(); + String losingClientId = existingResource.getCurrentSponsorClientId(); + // Verify that this client doesn't already sponsor this resource. + if (gainingClientId.equals(losingClientId)) { + throw new ObjectAlreadySponsoredException(); + } + verifyAuthInfoForResource(command.getAuthInfo(), existingResource); + verifyNoDisallowedStatuses(existingResource, DISALLOWED_STATUSES); + HistoryEntry historyEntry = historyBuilder + .setType(HistoryEntry.Type.CONTACT_TRANSFER_REQUEST) + .setModificationTime(now) + .setParent(Key.create(existingResource)) + .build(); + DateTime transferExpirationTime = now.plus(automaticTransferLength); + TransferData serverApproveTransferData = new TransferData.Builder() + .setTransferRequestTime(now) + .setTransferRequestTrid(trid) + .setGainingClientId(gainingClientId) + .setLosingClientId(losingClientId) + .setPendingTransferExpirationTime(transferExpirationTime) + .setTransferStatus(TransferStatus.SERVER_APPROVED) + .build(); + // If the transfer is server approved, this message will be sent to the losing registrar. */ + PollMessage.OneTime serverApproveLosingPollMessage = new PollMessage.OneTime.Builder() + .setClientId(losingClientId) + .setMsg(TransferStatus.SERVER_APPROVED.getMessage()) + .setParent(historyEntry) + .setEventTime(transferExpirationTime) + .setResponseData(ImmutableList.of( + createTransferResponse(existingResource, serverApproveTransferData, now))) + .build(); + // If the transfer is server approved, this message will be sent to the gaining registrar. */ + PollMessage.OneTime serverApproveGainingPollMessage = new PollMessage.OneTime.Builder() + .setClientId(gainingClientId) + .setMsg(TransferStatus.SERVER_APPROVED.getMessage()) + .setParent(historyEntry) + .setEventTime(transferExpirationTime) + .setResponseData(ImmutableList.of( + createTransferResponse(existingResource, serverApproveTransferData, now), + createPendingTransferNotificationResponse(existingResource, trid, true, now))) + .build(); + TransferData pendingTransferData = serverApproveTransferData.asBuilder() + .setTransferStatus(TransferStatus.PENDING) + .setServerApproveEntities( + ImmutableSet.>of( + Key.create(serverApproveGainingPollMessage), + Key.create(serverApproveLosingPollMessage))) + .build(); + // When a transfer is requested, a poll message is created to notify the losing registrar. + PollMessage.OneTime requestPollMessage = new PollMessage.OneTime.Builder() + .setClientId(losingClientId) + .setMsg(TransferStatus.PENDING.getMessage()) + .setParent(historyEntry) + .setEventTime(now) + .setResponseData(ImmutableList.of( + createTransferResponse(existingResource, pendingTransferData, now))) + .build(); + ContactResource newResource = existingResource.asBuilder() + .setTransferData(pendingTransferData) + .addStatusValue(StatusValue.PENDING_TRANSFER) + .build(); + ofy().save().entities( + newResource, + historyEntry, + requestPollMessage, + serverApproveGainingPollMessage, + serverApproveLosingPollMessage); + return createOutput( + SuccessWithActionPending, + createTransferResponse(newResource, newResource.getTransferData(), now), + null); } } diff --git a/java/google/registry/flows/contact/ContactUpdateFlow.java b/java/google/registry/flows/contact/ContactUpdateFlow.java index de0c854e4..0f0b19fda 100644 --- a/java/google/registry/flows/contact/ContactUpdateFlow.java +++ b/java/google/registry/flows/contact/ContactUpdateFlow.java @@ -14,14 +14,33 @@ package google.registry.flows.contact; +import static google.registry.flows.ResourceFlowUtils.verifyAuthInfoForResource; +import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses; +import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership; import static google.registry.flows.contact.ContactFlowUtils.validateAsciiPostalInfo; import static google.registry.flows.contact.ContactFlowUtils.validateContactAgainstPolicy; +import static google.registry.model.EppResourceUtils.loadByUniqueId; +import static google.registry.model.eppoutput.Result.Code.Success; +import static google.registry.model.ofy.ObjectifyService.ofy; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import com.googlecode.objectify.Key; import google.registry.flows.EppException; -import google.registry.flows.ResourceUpdateFlow; +import google.registry.flows.LoggedInFlow; +import google.registry.flows.TransactionalFlow; +import google.registry.flows.exceptions.AddRemoveSameValueEppException; +import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException; +import google.registry.flows.exceptions.ResourceToMutateDoesNotExistException; +import google.registry.flows.exceptions.StatusNotClientSettableException; import google.registry.model.contact.ContactCommand.Update; import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource.Builder; +import google.registry.model.domain.metadata.MetadataExtension; +import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppinput.ResourceCommand; +import google.registry.model.eppinput.ResourceCommand.AddRemoveSameValueException; +import google.registry.model.eppoutput.EppOutput; import google.registry.model.reporting.HistoryEntry; import javax.inject.Inject; @@ -29,31 +48,81 @@ import javax.inject.Inject; * An EPP flow that updates a contact resource. * * @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException} - * @error {@link google.registry.flows.ResourceUpdateFlow.AddRemoveSameValueEppException} - * @error {@link google.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException} - * @error {@link google.registry.flows.ResourceUpdateFlow.ResourceHasClientUpdateProhibitedException} - * @error {@link google.registry.flows.ResourceUpdateFlow.StatusNotClientSettableException} - * @error {@link google.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException} + * @error {@link google.registry.flows.exceptions.AddRemoveSameValueEppException} + * @error {@link google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException} + * @error {@link google.registry.flows.exceptions.ResourceStatusProhibitsOperationException} + * @error {@link google.registry.flows.exceptions.ResourceToMutateDoesNotExistException} + * @error {@link google.registry.flows.exceptions.StatusNotClientSettableException} * @error {@link ContactFlowUtils.BadInternationalizedPostalInfoException} * @error {@link ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException} */ -public class ContactUpdateFlow extends ResourceUpdateFlow { +public class ContactUpdateFlow extends LoggedInFlow implements TransactionalFlow { + /** + * Note that CLIENT_UPDATE_PROHIBITED is intentionally not in this list. This is because it + * requires special checking, since you must be able to clear the status off the object with an + * update. + */ + private static final ImmutableSet DISALLOWED_STATUSES = ImmutableSet.of( + StatusValue.PENDING_DELETE, + StatusValue.SERVER_UPDATE_PROHIBITED); + + @Inject ResourceCommand resourceCommand; + @Inject HistoryEntry.Builder historyBuilder; @Inject ContactUpdateFlow() {} @Override - protected void verifyNewUpdatedStateIsAllowed() throws EppException { + protected final void initLoggedInFlow() throws EppException { + registerExtensions(MetadataExtension.class); + } + + @Override + public final EppOutput run() throws EppException { + Update command = (Update) resourceCommand; + String targetId = command.getTargetId(); + ContactResource existingResource = loadByUniqueId(ContactResource.class, targetId, now); + if (existingResource == null) { + throw new ResourceToMutateDoesNotExistException(ContactResource.class, targetId); + } + if (command.getAuthInfo() != null) { + verifyAuthInfoForResource(command.getAuthInfo(), existingResource); + } + if (!isSuperuser) { + verifyResourceOwnership(getClientId(), existingResource); + } + for (StatusValue statusValue : Sets.union( + command.getInnerAdd().getStatusValues(), + command.getInnerRemove().getStatusValues())) { + if (!isSuperuser && !statusValue.isClientSettable()) { // The superuser can set any status. + throw new StatusNotClientSettableException(statusValue.getXmlName()); + } + } + verifyNoDisallowedStatuses(existingResource, DISALLOWED_STATUSES); + historyBuilder + .setType(HistoryEntry.Type.CONTACT_UPDATE) + .setModificationTime(now) + .setXmlBytes(null) // We don't want to store contact details in the history entry. + .setParent(Key.create(existingResource)); + Builder builder = existingResource.asBuilder(); + try { + command.applyTo(builder); + } catch (AddRemoveSameValueException e) { + throw new AddRemoveSameValueEppException(); + } + ContactResource newResource = builder + .setLastEppUpdateTime(now) + .setLastEppUpdateClientId(getClientId()) + .build(); + // If the resource is marked with clientUpdateProhibited, and this update did not clear that + // status, then the update must be disallowed (unless a superuser is requesting the change). + if (!isSuperuser + && existingResource.getStatusValues().contains(StatusValue.CLIENT_UPDATE_PROHIBITED) + && newResource.getStatusValues().contains(StatusValue.CLIENT_UPDATE_PROHIBITED)) { + throw new ResourceHasClientUpdateProhibitedException(); + } validateAsciiPostalInfo(newResource.getInternationalizedPostalInfo()); validateContactAgainstPolicy(newResource); - } - - @Override - protected boolean storeXmlInHistoryEntry() { - return false; - } - - @Override - protected final HistoryEntry.Type getHistoryEntryType() { - return HistoryEntry.Type.CONTACT_UPDATE; + ofy().save().entities(newResource, historyBuilder.build()); + return createOutput(Success); } } diff --git a/javatests/google/registry/flows/EppLifecycleContactTest.java b/javatests/google/registry/flows/EppLifecycleContactTest.java index 05f936d52..fd6e272ae 100644 --- a/javatests/google/registry/flows/EppLifecycleContactTest.java +++ b/javatests/google/registry/flows/EppLifecycleContactTest.java @@ -76,7 +76,7 @@ public class EppLifecycleContactTest extends EppTestCase { DateTime.parse("2000-06-08T22:01:00Z")); assertCommandAndResponse( "poll_ack.xml", - ImmutableMap.of("ID", "2-1-ROID-3-4"), + ImmutableMap.of("ID", "2-1-ROID-3-6"), "poll_ack_response_empty.xml", null, DateTime.parse("2000-06-08T22:02:00Z")); diff --git a/javatests/google/registry/flows/contact/ContactCheckFlowTest.java b/javatests/google/registry/flows/contact/ContactCheckFlowTest.java index f028f6478..b951a97c3 100644 --- a/javatests/google/registry/flows/contact/ContactCheckFlowTest.java +++ b/javatests/google/registry/flows/contact/ContactCheckFlowTest.java @@ -18,8 +18,8 @@ import static google.registry.model.eppoutput.CheckData.ContactCheck.create; import static google.registry.testing.DatastoreHelper.persistActiveContact; import static google.registry.testing.DatastoreHelper.persistDeletedContact; -import google.registry.flows.ResourceCheckFlow.TooManyResourceChecksException; import google.registry.flows.ResourceCheckFlowTestCase; +import google.registry.flows.exceptions.TooManyResourceChecksException; import google.registry.model.contact.ContactResource; import org.junit.Test; diff --git a/javatests/google/registry/flows/contact/ContactCreateFlowTest.java b/javatests/google/registry/flows/contact/ContactCreateFlowTest.java index 463d8e4f6..26e1f0435 100644 --- a/javatests/google/registry/flows/contact/ContactCreateFlowTest.java +++ b/javatests/google/registry/flows/contact/ContactCreateFlowTest.java @@ -19,10 +19,10 @@ import static google.registry.testing.DatastoreHelper.assertNoBillingEvents; import static google.registry.testing.DatastoreHelper.persistActiveContact; import static google.registry.testing.DatastoreHelper.persistDeletedContact; -import google.registry.flows.ResourceCreateFlow.ResourceAlreadyExistsException; import google.registry.flows.ResourceFlowTestCase; import google.registry.flows.contact.ContactFlowUtils.BadInternationalizedPostalInfoException; import google.registry.flows.contact.ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException; +import google.registry.flows.exceptions.ResourceAlreadyExistsException; import google.registry.model.contact.ContactResource; import org.joda.time.DateTime; import org.junit.Test; diff --git a/javatests/google/registry/flows/contact/ContactDeleteFlowTest.java b/javatests/google/registry/flows/contact/ContactDeleteFlowTest.java index 2a03454c0..ec2ea08a8 100644 --- a/javatests/google/registry/flows/contact/ContactDeleteFlowTest.java +++ b/javatests/google/registry/flows/contact/ContactDeleteFlowTest.java @@ -28,13 +28,13 @@ import static google.registry.testing.TaskQueueHelper.assertTasksEnqueued; import com.google.common.collect.ImmutableSet; import com.googlecode.objectify.Key; -import google.registry.flows.ResourceAsyncDeleteFlow.ResourceToDeleteIsReferencedException; import google.registry.flows.ResourceFlowTestCase; import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException; -import google.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException; -import google.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException; import google.registry.flows.async.DeleteContactResourceAction; import google.registry.flows.async.DeleteEppResourceAction; +import google.registry.flows.exceptions.ResourceStatusProhibitsOperationException; +import google.registry.flows.exceptions.ResourceToDeleteIsReferencedException; +import google.registry.flows.exceptions.ResourceToMutateDoesNotExistException; import google.registry.model.contact.ContactResource; import google.registry.model.eppcommon.StatusValue; import google.registry.model.reporting.HistoryEntry; diff --git a/javatests/google/registry/flows/contact/ContactInfoFlowTest.java b/javatests/google/registry/flows/contact/ContactInfoFlowTest.java index 9c130fa42..d0c87dd0f 100644 --- a/javatests/google/registry/flows/contact/ContactInfoFlowTest.java +++ b/javatests/google/registry/flows/contact/ContactInfoFlowTest.java @@ -24,7 +24,7 @@ import static google.registry.testing.DatastoreHelper.persistResource; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import google.registry.flows.ResourceFlowTestCase; -import google.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException; +import google.registry.flows.exceptions.ResourceToQueryDoesNotExistException; import google.registry.model.contact.ContactAddress; import google.registry.model.contact.ContactAuthInfo; import google.registry.model.contact.ContactPhoneNumber; diff --git a/javatests/google/registry/flows/contact/ContactTransferApproveFlowTest.java b/javatests/google/registry/flows/contact/ContactTransferApproveFlowTest.java index 964a574f0..3582079c0 100644 --- a/javatests/google/registry/flows/contact/ContactTransferApproveFlowTest.java +++ b/javatests/google/registry/flows/contact/ContactTransferApproveFlowTest.java @@ -27,8 +27,8 @@ import com.google.common.collect.FluentIterable; import com.google.common.collect.Iterables; import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException; import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException; -import google.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException; -import google.registry.flows.ResourceMutatePendingTransferFlow.NotPendingTransferException; +import google.registry.flows.exceptions.NotPendingTransferException; +import google.registry.flows.exceptions.ResourceToMutateDoesNotExistException; import google.registry.model.contact.ContactAuthInfo; import google.registry.model.contact.ContactResource; import google.registry.model.eppcommon.AuthInfo.PasswordAuth; diff --git a/javatests/google/registry/flows/contact/ContactTransferCancelFlowTest.java b/javatests/google/registry/flows/contact/ContactTransferCancelFlowTest.java index aad737471..7e0e306b3 100644 --- a/javatests/google/registry/flows/contact/ContactTransferCancelFlowTest.java +++ b/javatests/google/registry/flows/contact/ContactTransferCancelFlowTest.java @@ -25,9 +25,9 @@ import static google.registry.testing.DatastoreHelper.persistResource; import com.google.common.collect.FluentIterable; import com.google.common.collect.Iterables; import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException; -import google.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException; -import google.registry.flows.ResourceMutatePendingTransferFlow.NotPendingTransferException; -import google.registry.flows.ResourceTransferCancelFlow.NotTransferInitiatorException; +import google.registry.flows.exceptions.NotPendingTransferException; +import google.registry.flows.exceptions.NotTransferInitiatorException; +import google.registry.flows.exceptions.ResourceToMutateDoesNotExistException; import google.registry.model.contact.ContactAuthInfo; import google.registry.model.contact.ContactResource; import google.registry.model.eppcommon.AuthInfo.PasswordAuth; diff --git a/javatests/google/registry/flows/contact/ContactTransferQueryFlowTest.java b/javatests/google/registry/flows/contact/ContactTransferQueryFlowTest.java index d3f28b77d..5302aaf46 100644 --- a/javatests/google/registry/flows/contact/ContactTransferQueryFlowTest.java +++ b/javatests/google/registry/flows/contact/ContactTransferQueryFlowTest.java @@ -20,9 +20,9 @@ import static google.registry.testing.DatastoreHelper.deleteResource; import static google.registry.testing.DatastoreHelper.persistResource; import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException; -import google.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException; -import google.registry.flows.ResourceTransferQueryFlow.NoTransferHistoryToQueryException; -import google.registry.flows.ResourceTransferQueryFlow.NotAuthorizedToViewTransferException; +import google.registry.flows.exceptions.NoTransferHistoryToQueryException; +import google.registry.flows.exceptions.NotAuthorizedToViewTransferException; +import google.registry.flows.exceptions.ResourceToQueryDoesNotExistException; import google.registry.model.contact.ContactAuthInfo; import google.registry.model.contact.ContactResource; import google.registry.model.eppcommon.AuthInfo.PasswordAuth; diff --git a/javatests/google/registry/flows/contact/ContactTransferRejectFlowTest.java b/javatests/google/registry/flows/contact/ContactTransferRejectFlowTest.java index d34f79354..f483a2569 100644 --- a/javatests/google/registry/flows/contact/ContactTransferRejectFlowTest.java +++ b/javatests/google/registry/flows/contact/ContactTransferRejectFlowTest.java @@ -26,8 +26,8 @@ import com.google.common.collect.FluentIterable; import com.google.common.collect.Iterables; import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException; import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException; -import google.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException; -import google.registry.flows.ResourceMutatePendingTransferFlow.NotPendingTransferException; +import google.registry.flows.exceptions.NotPendingTransferException; +import google.registry.flows.exceptions.ResourceToMutateDoesNotExistException; import google.registry.model.contact.ContactAuthInfo; import google.registry.model.contact.ContactResource; import google.registry.model.eppcommon.AuthInfo.PasswordAuth; diff --git a/javatests/google/registry/flows/contact/ContactTransferRequestFlowTest.java b/javatests/google/registry/flows/contact/ContactTransferRequestFlowTest.java index 3b5e80777..ebcfd12b8 100644 --- a/javatests/google/registry/flows/contact/ContactTransferRequestFlowTest.java +++ b/javatests/google/registry/flows/contact/ContactTransferRequestFlowTest.java @@ -24,10 +24,10 @@ import static google.registry.testing.DatastoreHelper.persistResource; import google.registry.config.RegistryEnvironment; import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException; -import google.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException; -import google.registry.flows.ResourceTransferRequestFlow.AlreadyPendingTransferException; -import google.registry.flows.ResourceTransferRequestFlow.MissingTransferRequestAuthInfoException; -import google.registry.flows.ResourceTransferRequestFlow.ObjectAlreadySponsoredException; +import google.registry.flows.exceptions.AlreadyPendingTransferException; +import google.registry.flows.exceptions.MissingTransferRequestAuthInfoException; +import google.registry.flows.exceptions.ObjectAlreadySponsoredException; +import google.registry.flows.exceptions.ResourceToMutateDoesNotExistException; import google.registry.model.contact.ContactAuthInfo; import google.registry.model.contact.ContactResource; import google.registry.model.eppcommon.AuthInfo.PasswordAuth; diff --git a/javatests/google/registry/flows/contact/ContactUpdateFlowTest.java b/javatests/google/registry/flows/contact/ContactUpdateFlowTest.java index b8f5752b0..9cb937c92 100644 --- a/javatests/google/registry/flows/contact/ContactUpdateFlowTest.java +++ b/javatests/google/registry/flows/contact/ContactUpdateFlowTest.java @@ -25,13 +25,13 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import google.registry.flows.ResourceFlowTestCase; import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException; -import google.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException; -import google.registry.flows.ResourceUpdateFlow.AddRemoveSameValueEppException; -import google.registry.flows.ResourceUpdateFlow.ResourceHasClientUpdateProhibitedException; -import google.registry.flows.ResourceUpdateFlow.StatusNotClientSettableException; -import google.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException; import google.registry.flows.contact.ContactFlowUtils.BadInternationalizedPostalInfoException; import google.registry.flows.contact.ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException; +import google.registry.flows.exceptions.AddRemoveSameValueEppException; +import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException; +import google.registry.flows.exceptions.ResourceStatusProhibitsOperationException; +import google.registry.flows.exceptions.ResourceToMutateDoesNotExistException; +import google.registry.flows.exceptions.StatusNotClientSettableException; import google.registry.model.contact.ContactAddress; import google.registry.model.contact.ContactResource; import google.registry.model.contact.PostalInfo; diff --git a/javatests/google/registry/flows/testdata/poll_response_contact_transfer.xml b/javatests/google/registry/flows/testdata/poll_response_contact_transfer.xml index 5c38f5b85..641961da3 100644 --- a/javatests/google/registry/flows/testdata/poll_response_contact_transfer.xml +++ b/javatests/google/registry/flows/testdata/poll_response_contact_transfer.xml @@ -3,7 +3,7 @@ Command completed successfully; ack to dequeue - + 2000-06-08T22:00:00Z Transfer requested.