mirror of
https://github.com/google/nomulus.git
synced 2025-04-30 12:07:51 +02:00
Add allocation token to transfer command (#1737)
This commit is contained in:
parent
644090450e
commit
3bae99ace6
7 changed files with 362 additions and 2 deletions
|
@ -44,6 +44,7 @@ import google.registry.flows.FlowModule.Superuser;
|
||||||
import google.registry.flows.FlowModule.TargetId;
|
import google.registry.flows.FlowModule.TargetId;
|
||||||
import google.registry.flows.TransactionalFlow;
|
import google.registry.flows.TransactionalFlow;
|
||||||
import google.registry.flows.annotations.ReportingSpec;
|
import google.registry.flows.annotations.ReportingSpec;
|
||||||
|
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
|
||||||
import google.registry.model.ImmutableObject;
|
import google.registry.model.ImmutableObject;
|
||||||
import google.registry.model.billing.BillingEvent;
|
import google.registry.model.billing.BillingEvent;
|
||||||
import google.registry.model.billing.BillingEvent.Flag;
|
import google.registry.model.billing.BillingEvent.Flag;
|
||||||
|
@ -55,7 +56,10 @@ import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
||||||
import google.registry.model.domain.GracePeriod;
|
import google.registry.model.domain.GracePeriod;
|
||||||
import google.registry.model.domain.metadata.MetadataExtension;
|
import google.registry.model.domain.metadata.MetadataExtension;
|
||||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||||
|
import google.registry.model.domain.token.AllocationToken;
|
||||||
|
import google.registry.model.domain.token.AllocationTokenExtension;
|
||||||
import google.registry.model.eppcommon.AuthInfo;
|
import google.registry.model.eppcommon.AuthInfo;
|
||||||
|
import google.registry.model.eppinput.EppInput;
|
||||||
import google.registry.model.eppoutput.EppResponse;
|
import google.registry.model.eppoutput.EppResponse;
|
||||||
import google.registry.model.poll.PollMessage;
|
import google.registry.model.poll.PollMessage;
|
||||||
import google.registry.model.reporting.DomainTransactionRecord;
|
import google.registry.model.reporting.DomainTransactionRecord;
|
||||||
|
@ -85,6 +89,18 @@ import org.joda.time.DateTime;
|
||||||
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
|
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
|
||||||
* @error {@link google.registry.flows.exceptions.NotPendingTransferException}
|
* @error {@link google.registry.flows.exceptions.NotPendingTransferException}
|
||||||
* @error {@link DomainFlowUtils.NotAuthorizedForTldException}
|
* @error {@link DomainFlowUtils.NotAuthorizedForTldException}
|
||||||
|
* @error {@link
|
||||||
|
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException}
|
||||||
|
* @error {@link
|
||||||
|
* google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException}
|
||||||
|
* @error {@link
|
||||||
|
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException}
|
||||||
|
* @error {@link
|
||||||
|
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException}
|
||||||
|
* @error {@link
|
||||||
|
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException}
|
||||||
|
* @error {@link
|
||||||
|
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException}
|
||||||
*/
|
*/
|
||||||
@ReportingSpec(ActivityReportField.DOMAIN_TRANSFER_APPROVE)
|
@ReportingSpec(ActivityReportField.DOMAIN_TRANSFER_APPROVE)
|
||||||
public final class DomainTransferApproveFlow implements TransactionalFlow {
|
public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||||
|
@ -97,6 +113,8 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||||
@Inject DomainHistory.Builder historyBuilder;
|
@Inject DomainHistory.Builder historyBuilder;
|
||||||
@Inject EppResponse.Builder responseBuilder;
|
@Inject EppResponse.Builder responseBuilder;
|
||||||
@Inject DomainPricingLogic pricingLogic;
|
@Inject DomainPricingLogic pricingLogic;
|
||||||
|
@Inject AllocationTokenFlowUtils allocationTokenFlowUtils;
|
||||||
|
@Inject EppInput eppInput;
|
||||||
|
|
||||||
@Inject DomainTransferApproveFlow() {}
|
@Inject DomainTransferApproveFlow() {}
|
||||||
|
|
||||||
|
@ -106,11 +124,20 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public EppResponse run() throws EppException {
|
public EppResponse run() throws EppException {
|
||||||
extensionManager.register(MetadataExtension.class);
|
extensionManager.register(MetadataExtension.class, AllocationTokenExtension.class);
|
||||||
validateRegistrarIsLoggedIn(registrarId);
|
validateRegistrarIsLoggedIn(registrarId);
|
||||||
extensionManager.validate();
|
extensionManager.validate();
|
||||||
DateTime now = tm().getTransactionTime();
|
DateTime now = tm().getTransactionTime();
|
||||||
Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now);
|
Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now);
|
||||||
|
// Currently we do not do anything with this allocation token, but just want it loaded and
|
||||||
|
// available in this flow in case we use it in the future
|
||||||
|
Optional<AllocationToken> allocationToken =
|
||||||
|
allocationTokenFlowUtils.verifyAllocationTokenIfPresent(
|
||||||
|
existingDomain,
|
||||||
|
Registry.get(existingDomain.getTld()),
|
||||||
|
registrarId,
|
||||||
|
now,
|
||||||
|
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||||
verifyOptionalAuthInfo(authInfo, existingDomain);
|
verifyOptionalAuthInfo(authInfo, existingDomain);
|
||||||
verifyHasPendingTransfer(existingDomain);
|
verifyHasPendingTransfer(existingDomain);
|
||||||
verifyResourceOwnership(registrarId, existingDomain);
|
verifyResourceOwnership(registrarId, existingDomain);
|
||||||
|
|
|
@ -47,6 +47,7 @@ import google.registry.flows.FlowModule.Superuser;
|
||||||
import google.registry.flows.FlowModule.TargetId;
|
import google.registry.flows.FlowModule.TargetId;
|
||||||
import google.registry.flows.TransactionalFlow;
|
import google.registry.flows.TransactionalFlow;
|
||||||
import google.registry.flows.annotations.ReportingSpec;
|
import google.registry.flows.annotations.ReportingSpec;
|
||||||
|
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
|
||||||
import google.registry.flows.exceptions.AlreadyPendingTransferException;
|
import google.registry.flows.exceptions.AlreadyPendingTransferException;
|
||||||
import google.registry.flows.exceptions.InvalidTransferPeriodValueException;
|
import google.registry.flows.exceptions.InvalidTransferPeriodValueException;
|
||||||
import google.registry.flows.exceptions.ObjectAlreadySponsoredException;
|
import google.registry.flows.exceptions.ObjectAlreadySponsoredException;
|
||||||
|
@ -61,6 +62,8 @@ import google.registry.model.domain.fee.FeeTransferCommandExtension;
|
||||||
import google.registry.model.domain.fee.FeeTransformResponseExtension;
|
import google.registry.model.domain.fee.FeeTransformResponseExtension;
|
||||||
import google.registry.model.domain.metadata.MetadataExtension;
|
import google.registry.model.domain.metadata.MetadataExtension;
|
||||||
import google.registry.model.domain.superuser.DomainTransferRequestSuperuserExtension;
|
import google.registry.model.domain.superuser.DomainTransferRequestSuperuserExtension;
|
||||||
|
import google.registry.model.domain.token.AllocationToken;
|
||||||
|
import google.registry.model.domain.token.AllocationTokenExtension;
|
||||||
import google.registry.model.eppcommon.AuthInfo;
|
import google.registry.model.eppcommon.AuthInfo;
|
||||||
import google.registry.model.eppcommon.StatusValue;
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
import google.registry.model.eppcommon.Trid;
|
import google.registry.model.eppcommon.Trid;
|
||||||
|
@ -117,6 +120,18 @@ import org.joda.time.DateTime;
|
||||||
* @error {@link DomainFlowUtils.PremiumNameBlockedException}
|
* @error {@link DomainFlowUtils.PremiumNameBlockedException}
|
||||||
* @error {@link DomainFlowUtils.RegistrarMustBeActiveForThisOperationException}
|
* @error {@link DomainFlowUtils.RegistrarMustBeActiveForThisOperationException}
|
||||||
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
|
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
|
||||||
|
* @error {@link
|
||||||
|
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException}
|
||||||
|
* @error {@link
|
||||||
|
* google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException}
|
||||||
|
* @error {@link
|
||||||
|
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException}
|
||||||
|
* @error {@link
|
||||||
|
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException}
|
||||||
|
* @error {@link
|
||||||
|
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException}
|
||||||
|
* @error {@link
|
||||||
|
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException}
|
||||||
*/
|
*/
|
||||||
@ReportingSpec(ActivityReportField.DOMAIN_TRANSFER_REQUEST)
|
@ReportingSpec(ActivityReportField.DOMAIN_TRANSFER_REQUEST)
|
||||||
public final class DomainTransferRequestFlow implements TransactionalFlow {
|
public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||||
|
@ -138,6 +153,8 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||||
@Inject AsyncTaskEnqueuer asyncTaskEnqueuer;
|
@Inject AsyncTaskEnqueuer asyncTaskEnqueuer;
|
||||||
@Inject EppResponse.Builder responseBuilder;
|
@Inject EppResponse.Builder responseBuilder;
|
||||||
@Inject DomainPricingLogic pricingLogic;
|
@Inject DomainPricingLogic pricingLogic;
|
||||||
|
@Inject AllocationTokenFlowUtils allocationTokenFlowUtils;
|
||||||
|
|
||||||
@Inject DomainTransferRequestFlow() {}
|
@Inject DomainTransferRequestFlow() {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -145,12 +162,22 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||||
extensionManager.register(
|
extensionManager.register(
|
||||||
DomainTransferRequestSuperuserExtension.class,
|
DomainTransferRequestSuperuserExtension.class,
|
||||||
FeeTransferCommandExtension.class,
|
FeeTransferCommandExtension.class,
|
||||||
MetadataExtension.class);
|
MetadataExtension.class,
|
||||||
|
AllocationTokenExtension.class);
|
||||||
validateRegistrarIsLoggedIn(gainingClientId);
|
validateRegistrarIsLoggedIn(gainingClientId);
|
||||||
verifyRegistrarIsActive(gainingClientId);
|
verifyRegistrarIsActive(gainingClientId);
|
||||||
extensionManager.validate();
|
extensionManager.validate();
|
||||||
DateTime now = tm().getTransactionTime();
|
DateTime now = tm().getTransactionTime();
|
||||||
Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now);
|
Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now);
|
||||||
|
// Currently we do not do anything with this allocation token, but just want it loaded and
|
||||||
|
// available in this flow in case we use it in the future
|
||||||
|
Optional<AllocationToken> allocationToken =
|
||||||
|
allocationTokenFlowUtils.verifyAllocationTokenIfPresent(
|
||||||
|
existingDomain,
|
||||||
|
Registry.get(existingDomain.getTld()),
|
||||||
|
gainingClientId,
|
||||||
|
now,
|
||||||
|
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||||
Optional<DomainTransferRequestSuperuserExtension> superuserExtension =
|
Optional<DomainTransferRequestSuperuserExtension> superuserExtension =
|
||||||
eppInput.getSingleExtension(DomainTransferRequestSuperuserExtension.class);
|
eppInput.getSingleExtension(DomainTransferRequestSuperuserExtension.class);
|
||||||
Period period =
|
Period period =
|
||||||
|
|
|
@ -16,6 +16,8 @@ package google.registry.flows.domain;
|
||||||
|
|
||||||
import static com.google.common.collect.MoreCollectors.onlyElement;
|
import static com.google.common.collect.MoreCollectors.onlyElement;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||||
|
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
||||||
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.NET_ADDS_4_YR;
|
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.NET_ADDS_4_YR;
|
||||||
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.TRANSFER_SUCCESSFUL;
|
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.TRANSFER_SUCCESSFUL;
|
||||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_CREATE;
|
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_CREATE;
|
||||||
|
@ -43,12 +45,19 @@ import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.ImmutableSortedMap;
|
import com.google.common.collect.ImmutableSortedMap;
|
||||||
import com.google.common.collect.Ordering;
|
import com.google.common.collect.Ordering;
|
||||||
import com.google.common.collect.Streams;
|
import com.google.common.collect.Streams;
|
||||||
|
import com.googlecode.objectify.Key;
|
||||||
import google.registry.flows.EppException;
|
import google.registry.flows.EppException;
|
||||||
import google.registry.flows.FlowUtils.NotLoggedInException;
|
import google.registry.flows.FlowUtils.NotLoggedInException;
|
||||||
import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
|
import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
|
||||||
import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
|
import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
|
||||||
import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
|
import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
|
import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
|
||||||
|
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException;
|
||||||
|
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException;
|
||||||
|
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException;
|
||||||
|
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException;
|
||||||
|
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException;
|
||||||
|
import google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException;
|
||||||
import google.registry.flows.exceptions.NotPendingTransferException;
|
import google.registry.flows.exceptions.NotPendingTransferException;
|
||||||
import google.registry.model.billing.BillingEvent;
|
import google.registry.model.billing.BillingEvent;
|
||||||
import google.registry.model.billing.BillingEvent.OneTime;
|
import google.registry.model.billing.BillingEvent.OneTime;
|
||||||
|
@ -63,6 +72,8 @@ import google.registry.model.domain.GracePeriod;
|
||||||
import google.registry.model.domain.Period;
|
import google.registry.model.domain.Period;
|
||||||
import google.registry.model.domain.Period.Unit;
|
import google.registry.model.domain.Period.Unit;
|
||||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||||
|
import google.registry.model.domain.token.AllocationToken;
|
||||||
|
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||||
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
|
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
|
||||||
import google.registry.model.eppcommon.StatusValue;
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
import google.registry.model.eppcommon.Trid;
|
import google.registry.model.eppcommon.Trid;
|
||||||
|
@ -77,6 +88,7 @@ import google.registry.model.transfer.DomainTransferData;
|
||||||
import google.registry.model.transfer.TransferResponse.DomainTransferResponse;
|
import google.registry.model.transfer.TransferResponse.DomainTransferResponse;
|
||||||
import google.registry.model.transfer.TransferStatus;
|
import google.registry.model.transfer.TransferStatus;
|
||||||
import google.registry.persistence.VKey;
|
import google.registry.persistence.VKey;
|
||||||
|
import google.registry.testing.DatabaseHelper;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
@ -775,4 +787,115 @@ class DomainTransferApproveFlowTest
|
||||||
domain.getRegistrationExpirationTime());
|
domain.getRegistrationExpirationTime());
|
||||||
assertHistoryEntriesDoNotContainTransferBillingEventsOrGracePeriods();
|
assertHistoryEntriesDoNotContainTransferBillingEventsOrGracePeriods();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccess_allocationToken() throws Exception {
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(SINGLE_USE)
|
||||||
|
.setDomainName("example.tld")
|
||||||
|
.build());
|
||||||
|
doSuccessfulTest(
|
||||||
|
"tld",
|
||||||
|
"domain_transfer_approve_allocation_token.xml",
|
||||||
|
"domain_transfer_approve_response.xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_invalidAllocationToken() throws Exception {
|
||||||
|
setEppInput("domain_transfer_approve_allocation_token.xml");
|
||||||
|
EppException thrown = assertThrows(InvalidAllocationTokenException.class, this::runFlow);
|
||||||
|
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_allocationTokenIsForDifferentName() throws Exception {
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(SINGLE_USE)
|
||||||
|
.setDomainName("otherdomain.tld")
|
||||||
|
.build());
|
||||||
|
setEppInput("domain_transfer_approve_allocation_token.xml");
|
||||||
|
EppException thrown =
|
||||||
|
assertThrows(AllocationTokenNotValidForDomainException.class, this::runFlow);
|
||||||
|
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_allocationTokenNotActive() throws Exception {
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(UNLIMITED_USE)
|
||||||
|
.setDiscountFraction(0.5)
|
||||||
|
.setTokenStatusTransitions(
|
||||||
|
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||||
|
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||||
|
.put(clock.nowUtc().plusDays(1), TokenStatus.VALID)
|
||||||
|
.put(clock.nowUtc().plusDays(60), TokenStatus.ENDED)
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
setEppInput("domain_transfer_approve_allocation_token.xml");
|
||||||
|
EppException thrown = assertThrows(AllocationTokenNotInPromotionException.class, this::runFlow);
|
||||||
|
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_allocationTokenNotValidForRegistrar() throws Exception {
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(UNLIMITED_USE)
|
||||||
|
.setAllowedRegistrarIds(ImmutableSet.of("someClientId"))
|
||||||
|
.setDiscountFraction(0.5)
|
||||||
|
.setTokenStatusTransitions(
|
||||||
|
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||||
|
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||||
|
.put(clock.nowUtc().minusDays(1), TokenStatus.VALID)
|
||||||
|
.put(clock.nowUtc().plusDays(1), TokenStatus.ENDED)
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
setEppInput("domain_transfer_approve_allocation_token.xml");
|
||||||
|
EppException thrown =
|
||||||
|
assertThrows(AllocationTokenNotValidForRegistrarException.class, this::runFlow);
|
||||||
|
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_allocationTokenNotValidForTld() throws Exception {
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(UNLIMITED_USE)
|
||||||
|
.setAllowedTlds(ImmutableSet.of("example"))
|
||||||
|
.setDiscountFraction(0.5)
|
||||||
|
.setTokenStatusTransitions(
|
||||||
|
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||||
|
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||||
|
.put(clock.nowUtc().minusDays(1), TokenStatus.VALID)
|
||||||
|
.put(clock.nowUtc().plusDays(1), TokenStatus.ENDED)
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
setEppInput("domain_transfer_approve_allocation_token.xml");
|
||||||
|
EppException thrown = assertThrows(AllocationTokenNotValidForTldException.class, this::runFlow);
|
||||||
|
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_allocationTokenAlreadyRedeemed() throws Exception {
|
||||||
|
Domain domain = DatabaseHelper.newDomain("foo.tld");
|
||||||
|
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 505L);
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(SINGLE_USE)
|
||||||
|
.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey))
|
||||||
|
.build());
|
||||||
|
setEppInput("domain_transfer_approve_allocation_token.xml");
|
||||||
|
EppException thrown =
|
||||||
|
assertThrows(AlreadyRedeemedAllocationTokenException.class, this::runFlow);
|
||||||
|
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ import static com.google.common.truth.Truth8.assertThat;
|
||||||
import static google.registry.batch.AsyncTaskEnqueuer.PARAM_REQUESTED_TIME;
|
import static google.registry.batch.AsyncTaskEnqueuer.PARAM_REQUESTED_TIME;
|
||||||
import static google.registry.batch.AsyncTaskEnqueuer.PARAM_RESOURCE_KEY;
|
import static google.registry.batch.AsyncTaskEnqueuer.PARAM_RESOURCE_KEY;
|
||||||
import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_ACTIONS;
|
import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_ACTIONS;
|
||||||
|
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||||
|
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
||||||
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.TRANSFER_SUCCESSFUL;
|
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.TRANSFER_SUCCESSFUL;
|
||||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_CREATE;
|
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_CREATE;
|
||||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST;
|
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST;
|
||||||
|
@ -59,6 +61,7 @@ import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import com.google.common.collect.Streams;
|
import com.google.common.collect.Streams;
|
||||||
|
import com.googlecode.objectify.Key;
|
||||||
import google.registry.batch.ResaveEntityAction;
|
import google.registry.batch.ResaveEntityAction;
|
||||||
import google.registry.flows.EppException;
|
import google.registry.flows.EppException;
|
||||||
import google.registry.flows.EppRequestSource;
|
import google.registry.flows.EppRequestSource;
|
||||||
|
@ -76,6 +79,12 @@ import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException
|
||||||
import google.registry.flows.domain.DomainFlowUtils.PremiumNameBlockedException;
|
import google.registry.flows.domain.DomainFlowUtils.PremiumNameBlockedException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.RegistrarMustBeActiveForThisOperationException;
|
import google.registry.flows.domain.DomainFlowUtils.RegistrarMustBeActiveForThisOperationException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.UnsupportedFeeAttributeException;
|
import google.registry.flows.domain.DomainFlowUtils.UnsupportedFeeAttributeException;
|
||||||
|
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException;
|
||||||
|
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException;
|
||||||
|
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException;
|
||||||
|
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException;
|
||||||
|
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException;
|
||||||
|
import google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException;
|
||||||
import google.registry.flows.exceptions.AlreadyPendingTransferException;
|
import google.registry.flows.exceptions.AlreadyPendingTransferException;
|
||||||
import google.registry.flows.exceptions.InvalidTransferPeriodValueException;
|
import google.registry.flows.exceptions.InvalidTransferPeriodValueException;
|
||||||
import google.registry.flows.exceptions.MissingTransferRequestAuthInfoException;
|
import google.registry.flows.exceptions.MissingTransferRequestAuthInfoException;
|
||||||
|
@ -94,6 +103,8 @@ import google.registry.model.domain.GracePeriod;
|
||||||
import google.registry.model.domain.Period;
|
import google.registry.model.domain.Period;
|
||||||
import google.registry.model.domain.Period.Unit;
|
import google.registry.model.domain.Period.Unit;
|
||||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||||
|
import google.registry.model.domain.token.AllocationToken;
|
||||||
|
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||||
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
|
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
|
||||||
import google.registry.model.eppcommon.StatusValue;
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
import google.registry.model.eppcommon.Trid;
|
import google.registry.model.eppcommon.Trid;
|
||||||
|
@ -111,6 +122,7 @@ import google.registry.model.transfer.TransferResponse;
|
||||||
import google.registry.model.transfer.TransferStatus;
|
import google.registry.model.transfer.TransferStatus;
|
||||||
import google.registry.persistence.VKey;
|
import google.registry.persistence.VKey;
|
||||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||||
|
import google.registry.testing.DatabaseHelper;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -1677,4 +1689,120 @@ class DomainTransferRequestFlowTest
|
||||||
DomainTransactionRecord.create(
|
DomainTransactionRecord.create(
|
||||||
"tld", clock.nowUtc().plusDays(5), TRANSFER_SUCCESSFUL, 1));
|
"tld", clock.nowUtc().plusDays(5), TRANSFER_SUCCESSFUL, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccess_allocationToken() throws Exception {
|
||||||
|
setupDomain("example", "tld");
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(SINGLE_USE)
|
||||||
|
.setDomainName("example.tld")
|
||||||
|
.build());
|
||||||
|
doSuccessfulTest(
|
||||||
|
"domain_transfer_request_allocation_token.xml", "domain_transfer_request_response.xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_invalidAllocationToken() throws Exception {
|
||||||
|
setupDomain("example", "tld");
|
||||||
|
setEppInput("domain_transfer_request_allocation_token.xml");
|
||||||
|
EppException thrown = assertThrows(InvalidAllocationTokenException.class, this::runFlow);
|
||||||
|
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_allocationTokenIsForDifferentName() throws Exception {
|
||||||
|
setupDomain("example", "tld");
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(SINGLE_USE)
|
||||||
|
.setDomainName("otherdomain.tld")
|
||||||
|
.build());
|
||||||
|
setEppInput("domain_transfer_request_allocation_token.xml");
|
||||||
|
EppException thrown =
|
||||||
|
assertThrows(AllocationTokenNotValidForDomainException.class, this::runFlow);
|
||||||
|
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_allocationTokenNotActive() throws Exception {
|
||||||
|
setupDomain("example", "tld");
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(UNLIMITED_USE)
|
||||||
|
.setDiscountFraction(0.5)
|
||||||
|
.setTokenStatusTransitions(
|
||||||
|
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||||
|
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||||
|
.put(clock.nowUtc().plusDays(1), TokenStatus.VALID)
|
||||||
|
.put(clock.nowUtc().plusDays(60), TokenStatus.ENDED)
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
setEppInput("domain_transfer_request_allocation_token.xml");
|
||||||
|
EppException thrown = assertThrows(AllocationTokenNotInPromotionException.class, this::runFlow);
|
||||||
|
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_allocationTokenNotValidForRegistrar() throws Exception {
|
||||||
|
setupDomain("example", "tld");
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(UNLIMITED_USE)
|
||||||
|
.setAllowedRegistrarIds(ImmutableSet.of("someClientId"))
|
||||||
|
.setDiscountFraction(0.5)
|
||||||
|
.setTokenStatusTransitions(
|
||||||
|
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||||
|
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||||
|
.put(clock.nowUtc().minusDays(1), TokenStatus.VALID)
|
||||||
|
.put(clock.nowUtc().plusDays(1), TokenStatus.ENDED)
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
setEppInput("domain_transfer_request_allocation_token.xml");
|
||||||
|
EppException thrown =
|
||||||
|
assertThrows(AllocationTokenNotValidForRegistrarException.class, this::runFlow);
|
||||||
|
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_allocationTokenNotValidForTld() throws Exception {
|
||||||
|
setupDomain("example", "tld");
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(UNLIMITED_USE)
|
||||||
|
.setAllowedTlds(ImmutableSet.of("example"))
|
||||||
|
.setDiscountFraction(0.5)
|
||||||
|
.setTokenStatusTransitions(
|
||||||
|
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||||
|
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||||
|
.put(clock.nowUtc().minusDays(1), TokenStatus.VALID)
|
||||||
|
.put(clock.nowUtc().plusDays(1), TokenStatus.ENDED)
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
setEppInput("domain_transfer_request_allocation_token.xml");
|
||||||
|
EppException thrown = assertThrows(AllocationTokenNotValidForTldException.class, this::runFlow);
|
||||||
|
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_allocationTokenAlreadyRedeemed() throws Exception {
|
||||||
|
setupDomain("example", "tld");
|
||||||
|
Domain domain = DatabaseHelper.newDomain("foo.tld");
|
||||||
|
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 505L);
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(SINGLE_USE)
|
||||||
|
.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey))
|
||||||
|
.build());
|
||||||
|
setEppInput("domain_transfer_request_allocation_token.xml");
|
||||||
|
EppException thrown =
|
||||||
|
assertThrows(AlreadyRedeemedAllocationTokenException.class, this::runFlow);
|
||||||
|
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
|
<command>
|
||||||
|
<transfer op="approve">
|
||||||
|
<domain:transfer
|
||||||
|
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
|
<domain:name>example.tld</domain:name>
|
||||||
|
</domain:transfer>
|
||||||
|
</transfer>
|
||||||
|
<extension>
|
||||||
|
<allocationToken:allocationToken
|
||||||
|
xmlns:allocationToken=
|
||||||
|
"urn:ietf:params:xml:ns:allocationToken-1.0">
|
||||||
|
abc123
|
||||||
|
</allocationToken:allocationToken>
|
||||||
|
</extension>
|
||||||
|
<clTRID>ABC-12345</clTRID>
|
||||||
|
</command>
|
||||||
|
</epp>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
|
<command>
|
||||||
|
<transfer op="request">
|
||||||
|
<domain:transfer
|
||||||
|
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
|
<domain:name>example.tld</domain:name>
|
||||||
|
<domain:period unit="y">1</domain:period>
|
||||||
|
<domain:authInfo>
|
||||||
|
<domain:pw roid="JD1234-REP">2fooBAR</domain:pw>
|
||||||
|
</domain:authInfo>
|
||||||
|
</domain:transfer>
|
||||||
|
</transfer>
|
||||||
|
<extension>
|
||||||
|
<allocationToken:allocationToken
|
||||||
|
xmlns:allocationToken=
|
||||||
|
"urn:ietf:params:xml:ns:allocationToken-1.0">
|
||||||
|
abc123
|
||||||
|
</allocationToken:allocationToken>
|
||||||
|
</extension>
|
||||||
|
<clTRID>ABC-12345</clTRID>
|
||||||
|
</command>
|
||||||
|
</epp>
|
|
@ -589,12 +589,20 @@ replaced with new ones with the correct approval time.
|
||||||
* 2201
|
* 2201
|
||||||
* The specified resource belongs to another client.
|
* The specified resource belongs to another client.
|
||||||
* Registrar is not authorized to access this TLD.
|
* Registrar is not authorized to access this TLD.
|
||||||
|
* The allocation token is invalid.
|
||||||
* 2202
|
* 2202
|
||||||
* Authorization information for accessing resource is invalid.
|
* Authorization information for accessing resource is invalid.
|
||||||
* 2301
|
* 2301
|
||||||
* The resource does not have a pending transfer.
|
* The resource does not have a pending transfer.
|
||||||
* 2303
|
* 2303
|
||||||
* Resource with this id does not exist.
|
* Resource with this id does not exist.
|
||||||
|
* 2304
|
||||||
|
* The allocation token is not currently valid.
|
||||||
|
* 2305
|
||||||
|
* The allocation token is not valid for this domain.
|
||||||
|
* The allocation token is not valid for this registrar.
|
||||||
|
* The allocation token is not valid for this TLD.
|
||||||
|
* The allocation token was already redeemed.
|
||||||
|
|
||||||
## DomainTransferCancelFlow
|
## DomainTransferCancelFlow
|
||||||
|
|
||||||
|
@ -723,6 +731,7 @@ new ones with the correct approval time).
|
||||||
* Registrar is missing the billing account map for this currency type.
|
* Registrar is missing the billing account map for this currency type.
|
||||||
* Registrar is not authorized to access this TLD.
|
* Registrar is not authorized to access this TLD.
|
||||||
* Registrar must be active in order to perform this operation.
|
* Registrar must be active in order to perform this operation.
|
||||||
|
* The allocation token is invalid.
|
||||||
* 2202
|
* 2202
|
||||||
* Authorization information for accessing resource is invalid.
|
* Authorization information for accessing resource is invalid.
|
||||||
* 2300
|
* 2300
|
||||||
|
@ -735,6 +744,12 @@ new ones with the correct approval time).
|
||||||
extension.
|
extension.
|
||||||
* The requested domain name is on the premium price list, and this
|
* The requested domain name is on the premium price list, and this
|
||||||
registrar has blocked premium registrations.
|
registrar has blocked premium registrations.
|
||||||
|
* The allocation token is not currently valid.
|
||||||
|
* 2305
|
||||||
|
* The allocation token is not valid for this domain.
|
||||||
|
* The allocation token is not valid for this registrar.
|
||||||
|
* The allocation token is not valid for this TLD.
|
||||||
|
* The allocation token was already redeemed.
|
||||||
* 2306
|
* 2306
|
||||||
* Domain transfer period must be one year.
|
* Domain transfer period must be one year.
|
||||||
* Domain transfer period must be zero or one year when using the superuser
|
* Domain transfer period must be zero or one year when using the superuser
|
||||||
|
|
Loading…
Add table
Reference in a new issue