From 1ee02108ae025f84e968b7f28228c286cdfad60a Mon Sep 17 00:00:00 2001 From: cgoldfeder Date: Wed, 14 Sep 2016 11:16:12 -0700 Subject: [PATCH] Crush out shared code in contact flows, especially transfer Although the delta implies that this is actually adding code, it's better than it looks, because some of the stuff in ContactFlowUtils is duplicating more generic methods in ResourceFlowUtils, which can be deleted when the domain and host flows are cut over. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=133149104 --- java/google/registry/flows/FlowModule.java | 9 ++++ .../registry/flows/ResourceFlowUtils.java | 9 ++++ .../flows/contact/ContactCheckFlow.java | 2 +- .../flows/contact/ContactCreateFlow.java | 6 ++- .../flows/contact/ContactDeleteFlow.java | 17 +++--- .../flows/contact/ContactFlowUtils.java | 53 ++++++++++++++++++- .../flows/contact/ContactInfoFlow.java | 11 ++-- .../contact/ContactTransferApproveFlow.java | 47 +++++----------- .../contact/ContactTransferCancelFlow.java | 42 +++++---------- .../contact/ContactTransferQueryFlow.java | 23 ++++---- .../contact/ContactTransferRejectFlow.java | 38 ++++--------- .../contact/ContactTransferRequestFlow.java | 53 +++++++------------ .../flows/contact/ContactUpdateFlow.java | 15 +++--- java/google/registry/model/EppResource.java | 22 ++++++++ .../model/transfer/TransferStatus.java | 4 ++ 15 files changed, 194 insertions(+), 157 deletions(-) diff --git a/java/google/registry/flows/FlowModule.java b/java/google/registry/flows/FlowModule.java index aab0f04ac..669ca333f 100644 --- a/java/google/registry/flows/FlowModule.java +++ b/java/google/registry/flows/FlowModule.java @@ -16,16 +16,19 @@ package google.registry.flows; import static com.google.common.base.Preconditions.checkState; +import com.google.common.base.Optional; import com.google.common.base.Strings; import dagger.Module; import dagger.Provides; import google.registry.flows.exceptions.OnlyToolCanPassMetadataException; import google.registry.flows.picker.FlowPicker; import google.registry.model.domain.metadata.MetadataExtension; +import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.Trid; import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.EppInput.ResourceCommandWrapper; import google.registry.model.eppinput.ResourceCommand; +import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand; import google.registry.model.reporting.HistoryEntry; import java.lang.annotation.Documented; import javax.inject.Qualifier; @@ -177,6 +180,12 @@ public class FlowModule { .getResourceCommand(); } + @Provides + @FlowScope + static Optional provideAuthInfo(ResourceCommand resourceCommand) { + return Optional.fromNullable(((SingleResourceCommand) resourceCommand).getAuthInfo()); + } + /** * Provides a partially filled in {@link HistoryEntry} builder. * diff --git a/java/google/registry/flows/ResourceFlowUtils.java b/java/google/registry/flows/ResourceFlowUtils.java index 88065d649..6450779d5 100644 --- a/java/google/registry/flows/ResourceFlowUtils.java +++ b/java/google/registry/flows/ResourceFlowUtils.java @@ -20,6 +20,7 @@ import static google.registry.model.domain.DomainResource.extendRegistrationWith import static google.registry.model.ofy.ObjectifyService.ofy; import com.google.common.base.Function; +import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -223,6 +224,14 @@ public class ResourceFlowUtils { } } + /** Check that the given AuthInfo is either missing or else is valid for the given resource. */ + public static void verifyOptionalAuthInfoForResource( + Optional authInfo, EppResource resource) throws EppException { + if (authInfo.isPresent()) { + verifyAuthInfoForResource(authInfo.get(), resource); + } + } + /** Check that the given AuthInfo is valid for the given resource. */ public static void verifyAuthInfoForResource(AuthInfo authInfo, EppResource resource) throws EppException { diff --git a/java/google/registry/flows/contact/ContactCheckFlow.java b/java/google/registry/flows/contact/ContactCheckFlow.java index 1e1346dd3..e8b2b0237 100644 --- a/java/google/registry/flows/contact/ContactCheckFlow.java +++ b/java/google/registry/flows/contact/ContactCheckFlow.java @@ -55,6 +55,6 @@ public class ContactCheckFlow extends LoggedInFlow { boolean unused = !existingIds.contains(id); checks.add(ContactCheck.create(unused, id, unused ? null : "In use")); } - return createOutput(Success, ContactCheckData.create(checks.build()), null); + return createOutput(Success, ContactCheckData.create(checks.build())); } } diff --git a/java/google/registry/flows/contact/ContactCreateFlow.java b/java/google/registry/flows/contact/ContactCreateFlow.java index f034347ae..53d26fcf0 100644 --- a/java/google/registry/flows/contact/ContactCreateFlow.java +++ b/java/google/registry/flows/contact/ContactCreateFlow.java @@ -23,6 +23,7 @@ import static google.registry.model.ofy.ObjectifyService.ofy; import com.googlecode.objectify.Key; import google.registry.flows.EppException; +import google.registry.flows.FlowModule.ClientId; import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.exceptions.ResourceAlreadyExistsException; @@ -49,6 +50,7 @@ import javax.inject.Inject; public class ContactCreateFlow extends LoggedInFlow implements TransactionalFlow { @Inject ResourceCommand resourceCommand; + @Inject @ClientId String clientId; @Inject HistoryEntry.Builder historyBuilder; @Inject ContactCreateFlow() {} @@ -66,8 +68,8 @@ public class ContactCreateFlow extends LoggedInFlow implements TransactionalFlow Builder builder = new Builder(); command.applyTo(builder); ContactResource newResource = builder - .setCreationClientId(getClientId()) - .setCurrentSponsorClientId(getClientId()) + .setCreationClientId(clientId) + .setCurrentSponsorClientId(clientId) .setRepoId(createContactHostRoid(ObjectifyService.allocateId())) .build(); validateAsciiPostalInfo(newResource.getInternationalizedPostalInfo()); diff --git a/java/google/registry/flows/contact/ContactDeleteFlow.java b/java/google/registry/flows/contact/ContactDeleteFlow.java index 09d2affe3..fa300f68b 100644 --- a/java/google/registry/flows/contact/ContactDeleteFlow.java +++ b/java/google/registry/flows/contact/ContactDeleteFlow.java @@ -15,19 +15,21 @@ package google.registry.flows.contact; 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.verifyOptionalAuthInfoForResource; 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.Function; +import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; 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.FlowModule.ClientId; import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.async.AsyncFlowUtils; @@ -38,6 +40,7 @@ import google.registry.model.contact.ContactCommand.Delete; import google.registry.model.contact.ContactResource; import google.registry.model.domain.DomainBase; import google.registry.model.domain.metadata.MetadataExtension; +import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppoutput.EppOutput; @@ -62,6 +65,8 @@ public class ContactDeleteFlow extends LoggedInFlow implements TransactionalFlow StatusValue.SERVER_DELETE_PROHIBITED); @Inject ResourceCommand resourceCommand; + @Inject @ClientId String clientId; + @Inject Optional authInfo; @Inject @Config("asyncDeleteFlowMapreduceDelay") Duration mapreduceDelay; @Inject HistoryEntry.Builder historyBuilder; @Inject ContactDeleteFlow() {} @@ -89,11 +94,9 @@ public class ContactDeleteFlow extends LoggedInFlow implements TransactionalFlow throw new ResourceToMutateDoesNotExistException(ContactResource.class, targetId); } verifyNoDisallowedStatuses(existingResource, DISALLOWED_STATUSES); - if (command.getAuthInfo() != null) { - verifyAuthInfoForResource(command.getAuthInfo(), existingResource); - } + verifyOptionalAuthInfoForResource(authInfo, existingResource); if (!isSuperuser) { - verifyResourceOwnership(getClientId(), existingResource); + verifyResourceOwnership(clientId, existingResource); } AsyncFlowUtils.enqueueMapreduceAction( DeleteContactResourceAction.class, @@ -101,7 +104,7 @@ public class ContactDeleteFlow extends LoggedInFlow implements TransactionalFlow DeleteEppResourceAction.PARAM_RESOURCE_KEY, Key.create(existingResource).getString(), DeleteEppResourceAction.PARAM_REQUESTING_CLIENT_ID, - getClientId(), + clientId, DeleteEppResourceAction.PARAM_IS_SUPERUSER, Boolean.toString(isSuperuser)), mapreduceDelay); @@ -112,6 +115,6 @@ public class ContactDeleteFlow extends LoggedInFlow implements TransactionalFlow .setModificationTime(now) .setParent(Key.create(existingResource)); ofy().save().entities(newResource, historyBuilder.build()); - return createOutput(SuccessWithActionPending, null, null); + return createOutput(SuccessWithActionPending); } } diff --git a/java/google/registry/flows/contact/ContactFlowUtils.java b/java/google/registry/flows/contact/ContactFlowUtils.java index a812fe46e..661f4d4ba 100644 --- a/java/google/registry/flows/contact/ContactFlowUtils.java +++ b/java/google/registry/flows/contact/ContactFlowUtils.java @@ -18,6 +18,7 @@ import static google.registry.model.contact.PostalInfo.Type.INTERNATIONALIZED; import com.google.common.base.CharMatcher; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import google.registry.flows.EppException; import google.registry.flows.EppException.ParameterValuePolicyErrorException; @@ -25,6 +26,11 @@ import google.registry.flows.EppException.ParameterValueSyntaxErrorException; import google.registry.model.contact.ContactAddress; import google.registry.model.contact.ContactResource; import google.registry.model.contact.PostalInfo; +import google.registry.model.poll.PendingActionNotificationResponse.ContactPendingActionNotificationResponse; +import google.registry.model.poll.PollMessage; +import google.registry.model.reporting.HistoryEntry; +import google.registry.model.transfer.TransferData; +import google.registry.model.transfer.TransferResponse.ContactTransferResponse; import java.util.Set; import javax.annotation.Nullable; @@ -50,7 +56,7 @@ public class ContactFlowUtils { } } } - + /** Check contact's state against server policy. */ static void validateContactAgainstPolicy(ContactResource contact) throws EppException { if (contact.getDisclose() != null && !contact.getDisclose().getFlag()) { @@ -58,6 +64,49 @@ public class ContactFlowUtils { } } + /** Create a poll message for the gaining client in a transfer. */ + static PollMessage createGainingTransferPollMessage( + String targetId, TransferData transferData, HistoryEntry historyEntry) { + return new PollMessage.OneTime.Builder() + .setClientId(transferData.getGainingClientId()) + .setEventTime(transferData.getPendingTransferExpirationTime()) + .setMsg(transferData.getTransferStatus().getMessage()) + .setResponseData(ImmutableList.of( + createTransferResponse(targetId, transferData), + ContactPendingActionNotificationResponse.create( + targetId, + transferData.getTransferStatus().isApproved(), + transferData.getTransferRequestTrid(), + historyEntry.getModificationTime()))) + .setParent(historyEntry) + .build(); + } + + /** Create a poll message for the losing client in a transfer. */ + static PollMessage createLosingTransferPollMessage( + String targetId, TransferData transferData, HistoryEntry historyEntry) { + return new PollMessage.OneTime.Builder() + .setClientId(transferData.getLosingClientId()) + .setEventTime(transferData.getPendingTransferExpirationTime()) + .setMsg(transferData.getTransferStatus().getMessage()) + .setResponseData(ImmutableList.of(createTransferResponse(targetId, transferData))) + .setParent(historyEntry) + .build(); + } + + /** Create a {@link ContactTransferResponse} off of the info in a {@link TransferData}. */ + static ContactTransferResponse createTransferResponse( + String targetId, TransferData transferData) { + return new ContactTransferResponse.Builder() + .setContactId(targetId) + .setGainingClientId(transferData.getGainingClientId()) + .setLosingClientId(transferData.getLosingClientId()) + .setPendingTransferExpirationTime(transferData.getPendingTransferExpirationTime()) + .setTransferRequestTime(transferData.getTransferRequestTime()) + .setTransferStatus(transferData.getTransferStatus()) + .build(); + } + /** Declining contact disclosure is disallowed by server policy. */ static class DeclineContactDisclosureFieldDisallowedPolicyException extends ParameterValuePolicyErrorException { @@ -65,7 +114,7 @@ public class ContactFlowUtils { super("Declining contact disclosure is disallowed by server policy."); } } - + /** Internationalized postal infos can only contain ASCII characters. */ static class BadInternationalizedPostalInfoException extends ParameterValueSyntaxErrorException { public BadInternationalizedPostalInfoException() { diff --git a/java/google/registry/flows/contact/ContactInfoFlow.java b/java/google/registry/flows/contact/ContactInfoFlow.java index 350da8f24..9adcad333 100644 --- a/java/google/registry/flows/contact/ContactInfoFlow.java +++ b/java/google/registry/flows/contact/ContactInfoFlow.java @@ -14,16 +14,18 @@ package google.registry.flows.contact; -import static google.registry.flows.ResourceFlowUtils.verifyAuthInfoForResource; +import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; import static google.registry.model.EppResourceUtils.cloneResourceWithLinkedStatus; import static google.registry.model.EppResourceUtils.loadByUniqueId; import static google.registry.model.eppoutput.Result.Code.Success; +import com.google.common.base.Optional; 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.eppcommon.AuthInfo; import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppoutput.EppOutput; import javax.inject.Inject; @@ -36,6 +38,7 @@ import javax.inject.Inject; public class ContactInfoFlow extends LoggedInFlow { @Inject ResourceCommand resourceCommand; + @Inject Optional authInfo; @Inject ContactInfoFlow() {} @Override @@ -46,9 +49,7 @@ public class ContactInfoFlow extends LoggedInFlow { if (existingResource == null) { throw new ResourceToQueryDoesNotExistException(ContactResource.class, targetId); } - if (command.getAuthInfo() != null) { - verifyAuthInfoForResource(command.getAuthInfo(), existingResource); - } - return createOutput(Success, cloneResourceWithLinkedStatus(existingResource, now), null); + verifyOptionalAuthInfoForResource(authInfo, existingResource); + return createOutput(Success, cloneResourceWithLinkedStatus(existingResource, now)); } } diff --git a/java/google/registry/flows/contact/ContactTransferApproveFlow.java b/java/google/registry/flows/contact/ContactTransferApproveFlow.java index f68f6e27b..b3e4dbc90 100644 --- a/java/google/registry/flows/contact/ContactTransferApproveFlow.java +++ b/java/google/registry/flows/contact/ContactTransferApproveFlow.java @@ -14,17 +14,18 @@ package google.registry.flows.contact; -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.verifyOptionalAuthInfoForResource; import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership; +import static google.registry.flows.contact.ContactFlowUtils.createGainingTransferPollMessage; +import static google.registry.flows.contact.ContactFlowUtils.createTransferResponse; 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.google.common.base.Optional; import com.googlecode.objectify.Key; import google.registry.flows.EppException; +import google.registry.flows.FlowModule.ClientId; import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.exceptions.NotPendingTransferException; @@ -32,12 +33,11 @@ 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.eppcommon.AuthInfo; 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; @@ -52,6 +52,8 @@ import javax.inject.Inject; public class ContactTransferApproveFlow extends LoggedInFlow implements TransactionalFlow { @Inject ResourceCommand resourceCommand; + @Inject @ClientId String clientId; + @Inject Optional authInfo; @Inject HistoryEntry.Builder historyBuilder; @Inject ContactTransferApproveFlow() {} @@ -68,27 +70,15 @@ public class ContactTransferApproveFlow extends LoggedInFlow implements Transact if (existingResource == null) { throw new ResourceToMutateDoesNotExistException(ContactResource.class, targetId); } - if (command.getAuthInfo() != null) { - verifyAuthInfoForResource(command.getAuthInfo(), existingResource); - } + verifyOptionalAuthInfoForResource(authInfo, existingResource); if (existingResource.getTransferData().getTransferStatus() != TransferStatus.PENDING) { throw new NotPendingTransferException(targetId); } - verifyResourceOwnership(getClientId(), existingResource); - TransferData oldTransferData = existingResource.getTransferData(); + verifyResourceOwnership(clientId, existingResource); ContactResource newResource = existingResource.asBuilder() - .removeStatusValue(StatusValue.PENDING_TRANSFER) + .clearPendingTransfer(TransferStatus.CLIENT_APPROVED, now) .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) @@ -96,21 +86,12 @@ public class ContactTransferApproveFlow extends LoggedInFlow implements Transact .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(); + PollMessage gainingPollMessage = + createGainingTransferPollMessage(targetId, newResource.getTransferData(), historyEntry); 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)); + return createOutput(Success, createTransferResponse(targetId, newResource.getTransferData())); } } diff --git a/java/google/registry/flows/contact/ContactTransferCancelFlow.java b/java/google/registry/flows/contact/ContactTransferCancelFlow.java index 689ec4093..665064e27 100644 --- a/java/google/registry/flows/contact/ContactTransferCancelFlow.java +++ b/java/google/registry/flows/contact/ContactTransferCancelFlow.java @@ -14,15 +14,17 @@ package google.registry.flows.contact; -import static google.registry.flows.ResourceFlowUtils.createTransferResponse; -import static google.registry.flows.ResourceFlowUtils.verifyAuthInfoForResource; +import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; +import static google.registry.flows.contact.ContactFlowUtils.createLosingTransferPollMessage; +import static google.registry.flows.contact.ContactFlowUtils.createTransferResponse; 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.google.common.base.Optional; import com.googlecode.objectify.Key; import google.registry.flows.EppException; +import google.registry.flows.FlowModule.ClientId; import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.exceptions.NotPendingTransferException; @@ -31,7 +33,7 @@ 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.eppcommon.AuthInfo; import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppoutput.EppOutput; import google.registry.model.poll.PollMessage; @@ -50,6 +52,8 @@ import javax.inject.Inject; public class ContactTransferCancelFlow extends LoggedInFlow implements TransactionalFlow { @Inject ResourceCommand resourceCommand; + @Inject Optional authInfo; + @Inject @ClientId String clientId; @Inject HistoryEntry.Builder historyBuilder; @Inject ContactTransferCancelFlow() {} @@ -67,28 +71,17 @@ public class ContactTransferCancelFlow extends LoggedInFlow implements Transacti if (existingResource == null) { throw new ResourceToMutateDoesNotExistException(ContactResource.class, targetId); } - if (command.getAuthInfo() != null) { - verifyAuthInfoForResource(command.getAuthInfo(), existingResource); - } + verifyOptionalAuthInfoForResource(authInfo, 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())) { + if (!clientId.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()) + .clearPendingTransfer(TransferStatus.CLIENT_CANCELLED, now) .build(); HistoryEntry historyEntry = historyBuilder .setType(HistoryEntry.Type.CONTACT_TRANSFER_CANCEL) @@ -96,19 +89,12 @@ public class ContactTransferCancelFlow extends LoggedInFlow implements Transacti .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(); + PollMessage losingPollMessage = + createLosingTransferPollMessage(targetId, newResource.getTransferData(), historyEntry); 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)); + return createOutput(Success, createTransferResponse(targetId, newResource.getTransferData())); } } diff --git a/java/google/registry/flows/contact/ContactTransferQueryFlow.java b/java/google/registry/flows/contact/ContactTransferQueryFlow.java index 5d7786dbb..56c4052d2 100644 --- a/java/google/registry/flows/contact/ContactTransferQueryFlow.java +++ b/java/google/registry/flows/contact/ContactTransferQueryFlow.java @@ -14,18 +14,21 @@ package google.registry.flows.contact; -import static google.registry.flows.ResourceFlowUtils.createTransferResponse; -import static google.registry.flows.ResourceFlowUtils.verifyAuthInfoForResource; +import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; +import static google.registry.flows.contact.ContactFlowUtils.createTransferResponse; import static google.registry.model.EppResourceUtils.loadByUniqueId; import static google.registry.model.eppoutput.Result.Code.Success; +import com.google.common.base.Optional; import google.registry.flows.EppException; +import google.registry.flows.FlowModule.ClientId; 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.eppcommon.AuthInfo; import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppoutput.EppOutput; import javax.inject.Inject; @@ -41,6 +44,8 @@ import javax.inject.Inject; public class ContactTransferQueryFlow extends LoggedInFlow { @Inject ResourceCommand resourceCommand; + @Inject Optional authInfo; + @Inject @ClientId String clientId; @Inject ContactTransferQueryFlow() {} @Override @@ -51,22 +56,20 @@ public class ContactTransferQueryFlow extends LoggedInFlow { if (existingResource == null) { throw new ResourceToQueryDoesNotExistException(ContactResource.class, targetId); } - if (command.getAuthInfo() != null) { - verifyAuthInfoForResource(command.getAuthInfo(), existingResource); - } + verifyOptionalAuthInfoForResource(authInfo, 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. + // Note that the authorization info on the command (if present) has already been verified. If + // it's present, then the other checks are unnecessary. if (command.getAuthInfo() == null - && !getClientId().equals(existingResource.getTransferData().getGainingClientId()) - && !getClientId().equals(existingResource.getTransferData().getLosingClientId())) { + && !clientId.equals(existingResource.getTransferData().getGainingClientId()) + && !clientId.equals(existingResource.getTransferData().getLosingClientId())) { throw new NotAuthorizedToViewTransferException(); } return createOutput( - Success, createTransferResponse(existingResource, existingResource.getTransferData(), now)); + Success, createTransferResponse(targetId, existingResource.getTransferData())); } } diff --git a/java/google/registry/flows/contact/ContactTransferRejectFlow.java b/java/google/registry/flows/contact/ContactTransferRejectFlow.java index b564399e4..3fd7308b9 100644 --- a/java/google/registry/flows/contact/ContactTransferRejectFlow.java +++ b/java/google/registry/flows/contact/ContactTransferRejectFlow.java @@ -14,17 +14,17 @@ package google.registry.flows.contact; -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.flows.contact.ContactFlowUtils.createGainingTransferPollMessage; +import static google.registry.flows.contact.ContactFlowUtils.createTransferResponse; 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.FlowModule.ClientId; import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.exceptions.NotPendingTransferException; @@ -32,12 +32,10 @@ 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; @@ -52,6 +50,7 @@ import javax.inject.Inject; public class ContactTransferRejectFlow extends LoggedInFlow implements TransactionalFlow { @Inject ResourceCommand resourceCommand; + @Inject @ClientId String clientId; @Inject HistoryEntry.Builder historyBuilder; @Inject ContactTransferRejectFlow() {} @@ -74,40 +73,21 @@ public class ContactTransferRejectFlow extends LoggedInFlow implements Transacti if (existingResource.getTransferData().getTransferStatus() != TransferStatus.PENDING) { throw new NotPendingTransferException(targetId); } - verifyResourceOwnership(getClientId(), existingResource); - TransferData oldTransferData = existingResource.getTransferData(); + verifyResourceOwnership(clientId, existingResource); 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()) + .clearPendingTransfer(TransferStatus.CLIENT_REJECTED, now) .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(); + PollMessage gainingPollMessage = + createGainingTransferPollMessage(targetId, newResource.getTransferData(), historyEntry); 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)); + return createOutput(Success, createTransferResponse(targetId, newResource.getTransferData())); } } diff --git a/java/google/registry/flows/contact/ContactTransferRequestFlow.java b/java/google/registry/flows/contact/ContactTransferRequestFlow.java index 357b81ccc..c40201d3b 100644 --- a/java/google/registry/flows/contact/ContactTransferRequestFlow.java +++ b/java/google/registry/flows/contact/ContactTransferRequestFlow.java @@ -14,19 +14,21 @@ package google.registry.flows.contact; -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.flows.contact.ContactFlowUtils.createGainingTransferPollMessage; +import static google.registry.flows.contact.ContactFlowUtils.createLosingTransferPollMessage; +import static google.registry.flows.contact.ContactFlowUtils.createTransferResponse; 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.base.Optional; 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.FlowModule.ClientId; import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.exceptions.AlreadyPendingTransferException; @@ -36,6 +38,7 @@ 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.AuthInfo; import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppoutput.EppOutput; @@ -64,6 +67,8 @@ public class ContactTransferRequestFlow extends LoggedInFlow implements Transact StatusValue.SERVER_TRANSFER_PROHIBITED); @Inject ResourceCommand resourceCommand; + @Inject Optional authInfo; + @Inject @ClientId String gainingClientId; @Inject @Config("contactAutomaticTransferLength") Duration automaticTransferLength; @Inject HistoryEntry.Builder historyBuilder; @Inject ContactTransferRequestFlow() {} @@ -81,21 +86,19 @@ public class ContactTransferRequestFlow extends LoggedInFlow implements Transact if (existingResource == null) { throw new ResourceToMutateDoesNotExistException(ContactResource.class, targetId); } - if (command.getAuthInfo() == null) { + if (!authInfo.isPresent()) { throw new MissingTransferRequestAuthInfoException(); } - verifyAuthInfoForResource(command.getAuthInfo(), existingResource); + verifyAuthInfoForResource(authInfo.get(), 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) @@ -112,24 +115,11 @@ public class ContactTransferRequestFlow extends LoggedInFlow implements Transact .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(); + PollMessage serverApproveLosingPollMessage = + createLosingTransferPollMessage(targetId, serverApproveTransferData, historyEntry); // 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(); + PollMessage serverApproveGainingPollMessage = + createGainingTransferPollMessage(targetId, serverApproveTransferData, historyEntry); TransferData pendingTransferData = serverApproveTransferData.asBuilder() .setTransferStatus(TransferStatus.PENDING) .setServerApproveEntities( @@ -138,13 +128,9 @@ public class ContactTransferRequestFlow extends LoggedInFlow implements Transact 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))) + PollMessage requestPollMessage = + createLosingTransferPollMessage(targetId, pendingTransferData, historyEntry).asBuilder() + .setEventTime(now) // Unlike the serverApprove messages, this applies immediately. .build(); ContactResource newResource = existingResource.asBuilder() .setTransferData(pendingTransferData) @@ -157,8 +143,7 @@ public class ContactTransferRequestFlow extends LoggedInFlow implements Transact serverApproveGainingPollMessage, serverApproveLosingPollMessage); return createOutput( - SuccessWithActionPending, - createTransferResponse(newResource, newResource.getTransferData(), now), - null); + SuccessWithActionPending, createTransferResponse(targetId, newResource.getTransferData())); } } + diff --git a/java/google/registry/flows/contact/ContactUpdateFlow.java b/java/google/registry/flows/contact/ContactUpdateFlow.java index 0f0b19fda..9052a00c6 100644 --- a/java/google/registry/flows/contact/ContactUpdateFlow.java +++ b/java/google/registry/flows/contact/ContactUpdateFlow.java @@ -14,8 +14,8 @@ 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.verifyOptionalAuthInfoForResource; import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership; import static google.registry.flows.contact.ContactFlowUtils.validateAsciiPostalInfo; import static google.registry.flows.contact.ContactFlowUtils.validateContactAgainstPolicy; @@ -23,10 +23,12 @@ 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.base.Optional; 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.FlowModule.ClientId; import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.exceptions.AddRemoveSameValueEppException; @@ -37,6 +39,7 @@ 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.AuthInfo; import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppinput.ResourceCommand.AddRemoveSameValueException; @@ -68,6 +71,8 @@ public class ContactUpdateFlow extends LoggedInFlow implements TransactionalFlow StatusValue.SERVER_UPDATE_PROHIBITED); @Inject ResourceCommand resourceCommand; + @Inject Optional authInfo; + @Inject @ClientId String clientId; @Inject HistoryEntry.Builder historyBuilder; @Inject ContactUpdateFlow() {} @@ -84,11 +89,9 @@ public class ContactUpdateFlow extends LoggedInFlow implements TransactionalFlow if (existingResource == null) { throw new ResourceToMutateDoesNotExistException(ContactResource.class, targetId); } - if (command.getAuthInfo() != null) { - verifyAuthInfoForResource(command.getAuthInfo(), existingResource); - } + verifyOptionalAuthInfoForResource(authInfo, existingResource); if (!isSuperuser) { - verifyResourceOwnership(getClientId(), existingResource); + verifyResourceOwnership(clientId, existingResource); } for (StatusValue statusValue : Sets.union( command.getInnerAdd().getStatusValues(), @@ -111,7 +114,7 @@ public class ContactUpdateFlow extends LoggedInFlow implements TransactionalFlow } ContactResource newResource = builder .setLastEppUpdateTime(now) - .setLastEppUpdateClientId(getClientId()) + .setLastEppUpdateClientId(clientId) .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). diff --git a/java/google/registry/model/EppResource.java b/java/google/registry/model/EppResource.java index 741653ae6..2d09f2cb9 100644 --- a/java/google/registry/model/EppResource.java +++ b/java/google/registry/model/EppResource.java @@ -32,6 +32,7 @@ import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppoutput.EppResponse.ResponseData; import google.registry.model.ofy.CommitLogManifest; import google.registry.model.transfer.TransferData; +import google.registry.model.transfer.TransferStatus; import java.util.Set; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlTransient; @@ -303,6 +304,27 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable, return thisCastToDerived(); } + /** + * Remove a pending transfer. + * + *

This removes the {@link StatusValue#PENDING_TRANSFER} status, clears all the + * server-approve fields on the {@link TransferData} including the extended registration years + * field, and sets the expiration time of the last pending transfer (i.e. the one being cleared) + * to now. + */ + public B clearPendingTransfer(TransferStatus transferStatus, DateTime now) { + removeStatusValue(StatusValue.PENDING_TRANSFER); + return setTransferData(getInstance().getTransferData().asBuilder() + .setExtendedRegistrationYears(null) + .setServerApproveEntities(null) + .setServerApproveBillingEvent(null) + .setServerApproveAutorenewEvent(null) + .setServerApproveAutorenewPollMessage(null) + .setTransferStatus(transferStatus) + .setPendingTransferExpirationTime(now) + .build()); + } + /** Wipe out any personal information in the resource. */ public B wipeOut() { return thisCastToDerived(); diff --git a/java/google/registry/model/transfer/TransferStatus.java b/java/google/registry/model/transfer/TransferStatus.java index 0b9d985f0..4768c216f 100644 --- a/java/google/registry/model/transfer/TransferStatus.java +++ b/java/google/registry/model/transfer/TransferStatus.java @@ -50,4 +50,8 @@ public enum TransferStatus { public String getXmlName() { return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, toString()); } + + public boolean isApproved() { + return this.equals(CLIENT_APPROVED) || this.equals(SERVER_APPROVED); + } }