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
This commit is contained in:
cgoldfeder 2016-09-14 11:16:12 -07:00 committed by Ben McIlwain
parent 3978b4f169
commit 1ee02108ae
15 changed files with 194 additions and 157 deletions

View file

@ -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<AuthInfo> provideAuthInfo(ResourceCommand resourceCommand) {
return Optional.fromNullable(((SingleResourceCommand) resourceCommand).getAuthInfo());
}
/**
* Provides a partially filled in {@link HistoryEntry} builder.
*

View file

@ -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> 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 {

View file

@ -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()));
}
}

View file

@ -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());

View file

@ -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> 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().<Object>entities(newResource, historyBuilder.build());
return createOutput(SuccessWithActionPending, null, null);
return createOutput(SuccessWithActionPending);
}
}

View file

@ -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() {

View file

@ -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> 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));
}
}

View file

@ -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> 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().<Object>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()));
}
}

View file

@ -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> 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().<Object>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()));
}
}

View file

@ -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> 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()));
}
}

View file

@ -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().<Object>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()));
}
}

View file

@ -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> 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()));
}
}

View file

@ -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> 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).

View file

@ -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.
*
* <p>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();

View file

@ -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);
}
}