mirror of
https://github.com/google/nomulus.git
synced 2025-04-30 03:57:51 +02:00
Implement EPP Allocation Token Extension for domain:renew (#1693)
* Implement EPP Allocation Token Extension for domain:renew * Add more tests * Fix AllocationTokenFlowUtilsTest * Combine loadTokenAndValidateDomain with verifyAllocationTokenIfPresent * Change to Optional.empty * Remove unused variable
This commit is contained in:
parent
badfb29ce1
commit
68f975451e
8 changed files with 481 additions and 83 deletions
|
@ -262,7 +262,12 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||||
}
|
}
|
||||||
boolean isSunriseCreate = hasSignedMarks && (tldState == START_DATE_SUNRISE);
|
boolean isSunriseCreate = hasSignedMarks && (tldState == START_DATE_SUNRISE);
|
||||||
Optional<AllocationToken> allocationToken =
|
Optional<AllocationToken> allocationToken =
|
||||||
verifyAllocationTokenIfPresent(command, registry, registrarId, now);
|
allocationTokenFlowUtils.verifyAllocationTokenCreateIfPresent(
|
||||||
|
command,
|
||||||
|
registry,
|
||||||
|
registrarId,
|
||||||
|
now,
|
||||||
|
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||||
boolean isAnchorTenant =
|
boolean isAnchorTenant =
|
||||||
isAnchorTenant(
|
isAnchorTenant(
|
||||||
domainName, allocationToken, eppInput.getSingleExtension(MetadataExtension.class));
|
domainName, allocationToken, eppInput.getSingleExtension(MetadataExtension.class));
|
||||||
|
@ -485,19 +490,6 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||||
throw new NoGeneralRegistrationsInCurrentPhaseException();
|
throw new NoGeneralRegistrationsInCurrentPhaseException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Verifies and returns the allocation token if one is specified, otherwise does nothing. */
|
|
||||||
private Optional<AllocationToken> verifyAllocationTokenIfPresent(
|
|
||||||
DomainCommand.Create command, Registry registry, String registrarId, DateTime now)
|
|
||||||
throws EppException {
|
|
||||||
Optional<AllocationTokenExtension> extension =
|
|
||||||
eppInput.getSingleExtension(AllocationTokenExtension.class);
|
|
||||||
return Optional.ofNullable(
|
|
||||||
extension.isPresent()
|
|
||||||
? allocationTokenFlowUtils.loadTokenAndValidateDomainCreate(
|
|
||||||
command, extension.get().getAllocationToken(), registry, registrarId, now)
|
|
||||||
: null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private DomainHistory buildDomainHistory(
|
private DomainHistory buildDomainHistory(
|
||||||
DomainBase domain, Registry registry, DateTime now, Period period, Duration addGracePeriod) {
|
DomainBase domain, Registry registry, DateTime now, Period period, Duration addGracePeriod) {
|
||||||
// We ignore prober transactions
|
// We ignore prober transactions
|
||||||
|
|
|
@ -51,6 +51,8 @@ import google.registry.flows.custom.DomainRenewFlowCustomLogic.BeforeResponsePar
|
||||||
import google.registry.flows.custom.DomainRenewFlowCustomLogic.BeforeResponseReturnData;
|
import google.registry.flows.custom.DomainRenewFlowCustomLogic.BeforeResponseReturnData;
|
||||||
import google.registry.flows.custom.DomainRenewFlowCustomLogic.BeforeSaveParameters;
|
import google.registry.flows.custom.DomainRenewFlowCustomLogic.BeforeSaveParameters;
|
||||||
import google.registry.flows.custom.EntityChanges;
|
import google.registry.flows.custom.EntityChanges;
|
||||||
|
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
|
||||||
|
import google.registry.model.ImmutableObject;
|
||||||
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;
|
||||||
import google.registry.model.billing.BillingEvent.Reason;
|
import google.registry.model.billing.BillingEvent.Reason;
|
||||||
|
@ -68,6 +70,9 @@ import google.registry.model.domain.fee.FeeRenewCommandExtension;
|
||||||
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.rgp.GracePeriodStatus;
|
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||||
|
import google.registry.model.domain.token.AllocationToken;
|
||||||
|
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||||
|
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.eppinput.EppInput;
|
import google.registry.model.eppinput.EppInput;
|
||||||
|
@ -113,6 +118,18 @@ import org.joda.time.Duration;
|
||||||
* @error {@link DomainFlowUtils.RegistrarMustBeActiveForThisOperationException}
|
* @error {@link DomainFlowUtils.RegistrarMustBeActiveForThisOperationException}
|
||||||
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
|
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
|
||||||
* @error {@link DomainRenewFlow.IncorrectCurrentExpirationDateException}
|
* @error {@link DomainRenewFlow.IncorrectCurrentExpirationDateException}
|
||||||
|
* @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_RENEW)
|
@ReportingSpec(ActivityReportField.DOMAIN_RENEW)
|
||||||
public final class DomainRenewFlow implements TransactionalFlow {
|
public final class DomainRenewFlow implements TransactionalFlow {
|
||||||
|
@ -133,13 +150,15 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||||
@Inject @Superuser boolean isSuperuser;
|
@Inject @Superuser boolean isSuperuser;
|
||||||
@Inject DomainHistory.Builder historyBuilder;
|
@Inject DomainHistory.Builder historyBuilder;
|
||||||
@Inject EppResponse.Builder responseBuilder;
|
@Inject EppResponse.Builder responseBuilder;
|
||||||
|
@Inject AllocationTokenFlowUtils allocationTokenFlowUtils;
|
||||||
@Inject DomainRenewFlowCustomLogic flowCustomLogic;
|
@Inject DomainRenewFlowCustomLogic flowCustomLogic;
|
||||||
@Inject DomainPricingLogic pricingLogic;
|
@Inject DomainPricingLogic pricingLogic;
|
||||||
@Inject DomainRenewFlow() {}
|
@Inject DomainRenewFlow() {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EppResponse run() throws EppException {
|
public EppResponse run() throws EppException {
|
||||||
extensionManager.register(FeeRenewCommandExtension.class, MetadataExtension.class);
|
extensionManager.register(
|
||||||
|
FeeRenewCommandExtension.class, MetadataExtension.class, AllocationTokenExtension.class);
|
||||||
flowCustomLogic.beforeValidation();
|
flowCustomLogic.beforeValidation();
|
||||||
validateRegistrarIsLoggedIn(registrarId);
|
validateRegistrarIsLoggedIn(registrarId);
|
||||||
verifyRegistrarIsActive(registrarId);
|
verifyRegistrarIsActive(registrarId);
|
||||||
|
@ -148,6 +167,13 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||||
Renew command = (Renew) resourceCommand;
|
Renew command = (Renew) resourceCommand;
|
||||||
// Loads the target resource if it exists
|
// Loads the target resource if it exists
|
||||||
DomainBase existingDomain = loadAndVerifyExistence(DomainBase.class, targetId, now);
|
DomainBase existingDomain = loadAndVerifyExistence(DomainBase.class, targetId, now);
|
||||||
|
Optional<AllocationToken> allocationToken =
|
||||||
|
allocationTokenFlowUtils.verifyAllocationTokenIfPresent(
|
||||||
|
existingDomain,
|
||||||
|
Registry.get(existingDomain.getTld()),
|
||||||
|
registrarId,
|
||||||
|
now,
|
||||||
|
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||||
verifyRenewAllowed(authInfo, existingDomain, command);
|
verifyRenewAllowed(authInfo, existingDomain, command);
|
||||||
int years = command.getPeriod().getValue();
|
int years = command.getPeriod().getValue();
|
||||||
DateTime newExpirationTime =
|
DateTime newExpirationTime =
|
||||||
|
@ -176,7 +202,8 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||||
String tld = existingDomain.getTld();
|
String tld = existingDomain.getTld();
|
||||||
// Bill for this explicit renew itself.
|
// Bill for this explicit renew itself.
|
||||||
BillingEvent.OneTime explicitRenewEvent =
|
BillingEvent.OneTime explicitRenewEvent =
|
||||||
createRenewBillingEvent(tld, feesAndCredits.getTotalCost(), years, domainHistoryKey, now);
|
createRenewBillingEvent(
|
||||||
|
tld, feesAndCredits.getTotalCost(), years, domainHistoryKey, allocationToken, now);
|
||||||
// Create a new autorenew billing event and poll message starting at the new expiration time.
|
// Create a new autorenew billing event and poll message starting at the new expiration time.
|
||||||
BillingEvent.Recurring newAutorenewEvent =
|
BillingEvent.Recurring newAutorenewEvent =
|
||||||
newAutorenewBillingEvent(existingDomain)
|
newAutorenewBillingEvent(existingDomain)
|
||||||
|
@ -212,6 +239,14 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||||
DomainHistory domainHistory =
|
DomainHistory domainHistory =
|
||||||
buildDomainHistory(
|
buildDomainHistory(
|
||||||
newDomain, now, command.getPeriod(), registry.getRenewGracePeriodLength());
|
newDomain, now, command.getPeriod(), registry.getRenewGracePeriodLength());
|
||||||
|
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
|
||||||
|
entitiesToSave.add(
|
||||||
|
newDomain, domainHistory, explicitRenewEvent, newAutorenewEvent, newAutorenewPollMessage);
|
||||||
|
if (allocationToken.isPresent()
|
||||||
|
&& TokenType.SINGLE_USE.equals(allocationToken.get().getTokenType())) {
|
||||||
|
entitiesToSave.add(
|
||||||
|
allocationTokenFlowUtils.redeemToken(allocationToken.get(), domainHistory.createVKey()));
|
||||||
|
}
|
||||||
EntityChanges entityChanges =
|
EntityChanges entityChanges =
|
||||||
flowCustomLogic.beforeSave(
|
flowCustomLogic.beforeSave(
|
||||||
BeforeSaveParameters.newBuilder()
|
BeforeSaveParameters.newBuilder()
|
||||||
|
@ -221,15 +256,7 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||||
.setYears(years)
|
.setYears(years)
|
||||||
.setHistoryEntry(domainHistory)
|
.setHistoryEntry(domainHistory)
|
||||||
.setEntityChanges(
|
.setEntityChanges(
|
||||||
EntityChanges.newBuilder()
|
EntityChanges.newBuilder().setSaves(entitiesToSave.build()).build())
|
||||||
.setSaves(
|
|
||||||
ImmutableSet.of(
|
|
||||||
newDomain,
|
|
||||||
domainHistory,
|
|
||||||
explicitRenewEvent,
|
|
||||||
newAutorenewEvent,
|
|
||||||
newAutorenewPollMessage))
|
|
||||||
.build())
|
|
||||||
.build());
|
.build());
|
||||||
BeforeResponseReturnData responseData =
|
BeforeResponseReturnData responseData =
|
||||||
flowCustomLogic.beforeResponse(
|
flowCustomLogic.beforeResponse(
|
||||||
|
@ -290,7 +317,12 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||||
}
|
}
|
||||||
|
|
||||||
private OneTime createRenewBillingEvent(
|
private OneTime createRenewBillingEvent(
|
||||||
String tld, Money renewCost, int years, Key<DomainHistory> domainHistoryKey, DateTime now) {
|
String tld,
|
||||||
|
Money renewCost,
|
||||||
|
int years,
|
||||||
|
Key<DomainHistory> domainHistoryKey,
|
||||||
|
Optional<AllocationToken> allocationToken,
|
||||||
|
DateTime now) {
|
||||||
return new BillingEvent.OneTime.Builder()
|
return new BillingEvent.OneTime.Builder()
|
||||||
.setReason(Reason.RENEW)
|
.setReason(Reason.RENEW)
|
||||||
.setTargetId(targetId)
|
.setTargetId(targetId)
|
||||||
|
@ -298,6 +330,7 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||||
.setPeriodYears(years)
|
.setPeriodYears(years)
|
||||||
.setCost(renewCost)
|
.setCost(renewCost)
|
||||||
.setEventTime(now)
|
.setEventTime(now)
|
||||||
|
.setAllocationToken(allocationToken.map(AllocationToken::createVKey).orElse(null))
|
||||||
.setBillingTime(now.plus(Registry.get(tld).getRenewGracePeriodLength()))
|
.setBillingTime(now.plus(Registry.get(tld).getRenewGracePeriodLength()))
|
||||||
.setParent(domainHistoryKey)
|
.setParent(domainHistoryKey)
|
||||||
.build();
|
.build();
|
||||||
|
|
|
@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.net.InternetDomainName;
|
import com.google.common.net.InternetDomainName;
|
||||||
import google.registry.flows.EppException;
|
import google.registry.flows.EppException;
|
||||||
|
import google.registry.model.domain.DomainBase;
|
||||||
import google.registry.model.domain.DomainCommand;
|
import google.registry.model.domain.DomainCommand;
|
||||||
import google.registry.model.domain.token.AllocationToken;
|
import google.registry.model.domain.token.AllocationToken;
|
||||||
import google.registry.model.tld.Registry;
|
import google.registry.model.tld.Registry;
|
||||||
|
@ -31,7 +32,7 @@ import org.joda.time.DateTime;
|
||||||
*/
|
*/
|
||||||
public class AllocationTokenCustomLogic {
|
public class AllocationTokenCustomLogic {
|
||||||
|
|
||||||
/** Performs additional custom logic for validating a token. */
|
/** Performs additional custom logic for validating a token on a domain create. */
|
||||||
public AllocationToken validateToken(
|
public AllocationToken validateToken(
|
||||||
DomainCommand.Create command,
|
DomainCommand.Create command,
|
||||||
AllocationToken token,
|
AllocationToken token,
|
||||||
|
@ -43,6 +44,14 @@ public class AllocationTokenCustomLogic {
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Performs additional custom logic for validating a token on an existing domain. */
|
||||||
|
public AllocationToken validateToken(
|
||||||
|
DomainBase domain, AllocationToken token, Registry registry, String registrarId, DateTime now)
|
||||||
|
throws EppException {
|
||||||
|
// Do nothing.
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
/** Performs additional custom logic for performing domain checks using a token. */
|
/** Performs additional custom logic for performing domain checks using a token. */
|
||||||
public ImmutableMap<InternetDomainName, String> checkDomainsWithToken(
|
public ImmutableMap<InternetDomainName, String> checkDomainsWithToken(
|
||||||
ImmutableList<InternetDomainName> domainNames,
|
ImmutableList<InternetDomainName> domainNames,
|
||||||
|
|
|
@ -26,10 +26,12 @@ import google.registry.flows.EppException;
|
||||||
import google.registry.flows.EppException.AssociationProhibitsOperationException;
|
import google.registry.flows.EppException.AssociationProhibitsOperationException;
|
||||||
import google.registry.flows.EppException.AuthorizationErrorException;
|
import google.registry.flows.EppException.AuthorizationErrorException;
|
||||||
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||||
|
import google.registry.model.domain.DomainBase;
|
||||||
import google.registry.model.domain.DomainCommand;
|
import google.registry.model.domain.DomainCommand;
|
||||||
import google.registry.model.domain.token.AllocationToken;
|
import google.registry.model.domain.token.AllocationToken;
|
||||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||||
|
import google.registry.model.domain.token.AllocationTokenExtension;
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
import google.registry.model.tld.Registry;
|
import google.registry.model.tld.Registry;
|
||||||
import google.registry.persistence.VKey;
|
import google.registry.persistence.VKey;
|
||||||
|
@ -48,30 +50,6 @@ public class AllocationTokenFlowUtils {
|
||||||
this.tokenCustomLogic = tokenCustomLogic;
|
this.tokenCustomLogic = tokenCustomLogic;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads an allocation token given a string and verifies that the token is valid for the domain
|
|
||||||
* create request.
|
|
||||||
*
|
|
||||||
* @return the loaded {@link AllocationToken} for that string.
|
|
||||||
* @throws EppException if the token doesn't exist, is already redeemed, or is otherwise invalid
|
|
||||||
* for this request.
|
|
||||||
*/
|
|
||||||
public AllocationToken loadTokenAndValidateDomainCreate(
|
|
||||||
DomainCommand.Create command,
|
|
||||||
String token,
|
|
||||||
Registry registry,
|
|
||||||
String registrarId,
|
|
||||||
DateTime now)
|
|
||||||
throws EppException {
|
|
||||||
AllocationToken tokenEntity = loadToken(token);
|
|
||||||
validateToken(
|
|
||||||
InternetDomainName.from(command.getFullyQualifiedDomainName()),
|
|
||||||
tokenEntity,
|
|
||||||
registrarId,
|
|
||||||
now);
|
|
||||||
return tokenCustomLogic.validateToken(command, tokenEntity, registry, registrarId, now);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the allocation token applies to the given domain names, used for domain checks.
|
* Checks if the allocation token applies to the given domain names, used for domain checks.
|
||||||
*
|
*
|
||||||
|
@ -170,6 +148,45 @@ public class AllocationTokenFlowUtils {
|
||||||
return maybeTokenEntity.get();
|
return maybeTokenEntity.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Verifies and returns the allocation token if one is specified, otherwise does nothing. */
|
||||||
|
public Optional<AllocationToken> verifyAllocationTokenCreateIfPresent(
|
||||||
|
DomainCommand.Create command,
|
||||||
|
Registry registry,
|
||||||
|
String registrarId,
|
||||||
|
DateTime now,
|
||||||
|
Optional<AllocationTokenExtension> extension)
|
||||||
|
throws EppException {
|
||||||
|
if (!extension.isPresent()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
AllocationToken tokenEntity = loadToken(extension.get().getAllocationToken());
|
||||||
|
validateToken(
|
||||||
|
InternetDomainName.from(command.getFullyQualifiedDomainName()),
|
||||||
|
tokenEntity,
|
||||||
|
registrarId,
|
||||||
|
now);
|
||||||
|
return Optional.of(
|
||||||
|
tokenCustomLogic.validateToken(command, tokenEntity, registry, registrarId, now));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Verifies and returns the allocation token if one is specified, otherwise does nothing. */
|
||||||
|
public Optional<AllocationToken> verifyAllocationTokenIfPresent(
|
||||||
|
DomainBase existingDomain,
|
||||||
|
Registry registry,
|
||||||
|
String registrarId,
|
||||||
|
DateTime now,
|
||||||
|
Optional<AllocationTokenExtension> extension)
|
||||||
|
throws EppException {
|
||||||
|
if (!extension.isPresent()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
AllocationToken tokenEntity = loadToken(extension.get().getAllocationToken());
|
||||||
|
validateToken(
|
||||||
|
InternetDomainName.from(existingDomain.getDomainName()), tokenEntity, registrarId, now);
|
||||||
|
return Optional.of(
|
||||||
|
tokenCustomLogic.validateToken(existingDomain, tokenEntity, registry, registrarId, now));
|
||||||
|
}
|
||||||
|
|
||||||
// Note: exception messages should be <= 32 characters long for domain check results
|
// Note: exception messages should be <= 32 characters long for domain check results
|
||||||
|
|
||||||
/** The allocation token is not currently valid. */
|
/** The allocation token is not currently valid. */
|
||||||
|
|
|
@ -15,10 +15,13 @@
|
||||||
package google.registry.flows.domain;
|
package google.registry.flows.domain;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static com.google.common.truth.Truth8.assertThat;
|
||||||
import static google.registry.flows.domain.DomainTransferFlowTestCase.persistWithPendingTransfer;
|
import static google.registry.flows.domain.DomainTransferFlowTestCase.persistWithPendingTransfer;
|
||||||
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.DEFAULT;
|
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.DEFAULT;
|
||||||
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.NONPREMIUM;
|
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.NONPREMIUM;
|
||||||
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.SPECIFIED;
|
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.SPECIFIED;
|
||||||
|
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.persistence.transaction.TransactionManagerFactory.tm;
|
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||||
import static google.registry.testing.DatabaseHelper.assertBillingEvents;
|
import static google.registry.testing.DatabaseHelper.assertBillingEvents;
|
||||||
import static google.registry.testing.DatabaseHelper.assertPollMessages;
|
import static google.registry.testing.DatabaseHelper.assertPollMessages;
|
||||||
|
@ -46,6 +49,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.ImmutableSortedMap;
|
import com.google.common.collect.ImmutableSortedMap;
|
||||||
|
import com.googlecode.objectify.Key;
|
||||||
import google.registry.flows.EppException;
|
import google.registry.flows.EppException;
|
||||||
import google.registry.flows.EppRequestSource;
|
import google.registry.flows.EppRequestSource;
|
||||||
import google.registry.flows.FlowUtils.NotLoggedInException;
|
import google.registry.flows.FlowUtils.NotLoggedInException;
|
||||||
|
@ -64,6 +68,12 @@ import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException
|
||||||
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.DomainRenewFlow.IncorrectCurrentExpirationDateException;
|
import google.registry.flows.domain.DomainRenewFlow.IncorrectCurrentExpirationDateException;
|
||||||
|
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.ResourceStatusProhibitsOperationException;
|
import google.registry.flows.exceptions.ResourceStatusProhibitsOperationException;
|
||||||
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;
|
||||||
|
@ -73,6 +83,8 @@ import google.registry.model.domain.DomainBase;
|
||||||
import google.registry.model.domain.DomainHistory;
|
import google.registry.model.domain.DomainHistory;
|
||||||
import google.registry.model.domain.GracePeriod;
|
import google.registry.model.domain.GracePeriod;
|
||||||
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.StatusValue;
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
import google.registry.model.poll.PollMessage;
|
import google.registry.model.poll.PollMessage;
|
||||||
import google.registry.model.registrar.Registrar;
|
import google.registry.model.registrar.Registrar;
|
||||||
|
@ -81,6 +93,8 @@ import google.registry.model.reporting.DomainTransactionRecord;
|
||||||
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
|
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
import google.registry.model.tld.Registry;
|
import google.registry.model.tld.Registry;
|
||||||
|
import google.registry.persistence.VKey;
|
||||||
|
import google.registry.testing.DatabaseHelper;
|
||||||
import google.registry.testing.SetClockExtension;
|
import google.registry.testing.SetClockExtension;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
@ -321,6 +335,13 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, DomainBa
|
||||||
renewBillingEvent));
|
renewBillingEvent));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
private void assertAllocationTokenWasNotRedeemed(String token) {
|
||||||
|
AllocationToken reloadedToken =
|
||||||
|
tm().transact(() -> tm().loadByKey(VKey.create(AllocationToken.class, token)));
|
||||||
|
assertThat(reloadedToken.isRedeemed()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testNotLoggedIn() {
|
void testNotLoggedIn() {
|
||||||
sessionMetadata.setRegistrarId(null);
|
sessionMetadata.setRegistrarId(null);
|
||||||
|
@ -577,6 +598,164 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, DomainBa
|
||||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccess_allocationToken() throws Exception {
|
||||||
|
setEppInput(
|
||||||
|
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
|
||||||
|
persistDomain();
|
||||||
|
AllocationToken allocationToken =
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(SINGLE_USE)
|
||||||
|
.setDomainName("example.tld")
|
||||||
|
.build());
|
||||||
|
runFlowAssertResponse(
|
||||||
|
loadFile(
|
||||||
|
"domain_renew_response.xml",
|
||||||
|
ImmutableMap.of("DOMAIN", "example.tld", "EXDATE", "2002-04-03T22:00:00.0Z")));
|
||||||
|
assertThat(DatabaseHelper.loadByEntity(allocationToken).getRedemptionHistoryEntry())
|
||||||
|
.isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccess_allocationTokenMultiUse() throws Exception {
|
||||||
|
setEppInput(
|
||||||
|
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
|
||||||
|
persistDomain();
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder().setToken("abc123").setTokenType(UNLIMITED_USE).build());
|
||||||
|
runFlowAssertResponse(
|
||||||
|
loadFile(
|
||||||
|
"domain_renew_response.xml",
|
||||||
|
ImmutableMap.of("DOMAIN", "example.tld", "EXDATE", "2002-04-03T22:00:00.0Z")));
|
||||||
|
clock.advanceOneMilli();
|
||||||
|
setEppInput(
|
||||||
|
"domain_renew_allocationtoken.xml",
|
||||||
|
ImmutableMap.of("DOMAIN", "other-example.tld", "YEARS", "2"));
|
||||||
|
persistDomain();
|
||||||
|
runFlowAssertResponse(
|
||||||
|
loadFile(
|
||||||
|
"domain_renew_response.xml",
|
||||||
|
ImmutableMap.of("DOMAIN", "other-example.tld", "EXDATE", "2002-04-03T22:00:00.0Z")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_invalidAllocationToken() throws Exception {
|
||||||
|
setEppInput(
|
||||||
|
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
|
||||||
|
persistDomain();
|
||||||
|
EppException thrown = assertThrows(InvalidAllocationTokenException.class, this::runFlow);
|
||||||
|
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_allocationTokenIsForADifferentDomain() throws Exception {
|
||||||
|
setEppInput(
|
||||||
|
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
|
||||||
|
persistDomain();
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(SINGLE_USE)
|
||||||
|
.setDomainName("otherdomain.tld")
|
||||||
|
.build());
|
||||||
|
clock.advanceOneMilli();
|
||||||
|
EppException thrown =
|
||||||
|
assertThrows(AllocationTokenNotValidForDomainException.class, this::runFlow);
|
||||||
|
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||||
|
assertAllocationTokenWasNotRedeemed("abc123");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_promotionNotActive() throws Exception {
|
||||||
|
setEppInput(
|
||||||
|
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
|
||||||
|
persistDomain();
|
||||||
|
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());
|
||||||
|
assertAboutEppExceptions()
|
||||||
|
.that(assertThrows(AllocationTokenNotInPromotionException.class, this::runFlow))
|
||||||
|
.marshalsToXml();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_promoTokenNotValidForRegistrar() throws Exception {
|
||||||
|
setEppInput(
|
||||||
|
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
|
||||||
|
persistDomain();
|
||||||
|
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());
|
||||||
|
assertAboutEppExceptions()
|
||||||
|
.that(assertThrows(AllocationTokenNotValidForRegistrarException.class, this::runFlow))
|
||||||
|
.marshalsToXml();
|
||||||
|
assertAllocationTokenWasNotRedeemed("abc123");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_promoTokenNotValidForTld() throws Exception {
|
||||||
|
setEppInput(
|
||||||
|
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
|
||||||
|
persistDomain();
|
||||||
|
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());
|
||||||
|
assertAboutEppExceptions()
|
||||||
|
.that(assertThrows(AllocationTokenNotValidForTldException.class, this::runFlow))
|
||||||
|
.marshalsToXml();
|
||||||
|
assertAllocationTokenWasNotRedeemed("abc123");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_alreadyRedemeedAllocationToken() throws Exception {
|
||||||
|
setEppInput(
|
||||||
|
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
|
||||||
|
persistDomain();
|
||||||
|
DomainBase domain = persistActiveDomain("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());
|
||||||
|
clock.advanceOneMilli();
|
||||||
|
EppException thrown =
|
||||||
|
assertThrows(AlreadyRedeemedAllocationTokenException.class, this::runFlow);
|
||||||
|
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testFailure_suspendedRegistrarCantRenewDomain() {
|
void testFailure_suspendedRegistrarCantRenewDomain() {
|
||||||
doFailingTest_invalidRegistrarState(State.SUSPENDED);
|
doFailingTest_invalidRegistrarState(State.SUSPENDED);
|
||||||
|
|
|
@ -22,6 +22,7 @@ import static google.registry.model.domain.token.AllocationToken.TokenStatus.VAL
|
||||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
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.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
||||||
import static google.registry.testing.DatabaseHelper.createTld;
|
import static google.registry.testing.DatabaseHelper.createTld;
|
||||||
|
import static google.registry.testing.DatabaseHelper.newDomainBase;
|
||||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||||
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
|
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
|
||||||
|
@ -47,9 +48,11 @@ import google.registry.model.domain.DomainBase;
|
||||||
import google.registry.model.domain.DomainCommand;
|
import google.registry.model.domain.DomainCommand;
|
||||||
import google.registry.model.domain.token.AllocationToken;
|
import google.registry.model.domain.token.AllocationToken;
|
||||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||||
|
import google.registry.model.domain.token.AllocationTokenExtension;
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
import google.registry.model.tld.Registry;
|
import google.registry.model.tld.Registry;
|
||||||
import google.registry.testing.AppEngineExtension;
|
import google.registry.testing.AppEngineExtension;
|
||||||
|
import java.util.Optional;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -64,98 +67,195 @@ class AllocationTokenFlowUtilsTest {
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
final AppEngineExtension appEngine = AppEngineExtension.builder().withCloudSql().build();
|
final AppEngineExtension appEngine = AppEngineExtension.builder().withCloudSql().build();
|
||||||
|
|
||||||
|
private final AllocationTokenExtension allocationTokenExtension =
|
||||||
|
mock(AllocationTokenExtension.class);
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void beforeEach() {
|
void beforeEach() {
|
||||||
createTld("tld");
|
createTld("tld");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void test_validateToken_successfullyVerifiesValidToken() throws Exception {
|
void test_validateToken_successfullyVerifiesValidTokenOnCreate() throws Exception {
|
||||||
AllocationToken token =
|
AllocationToken token =
|
||||||
persistResource(
|
persistResource(
|
||||||
new AllocationToken.Builder().setToken("tokeN").setTokenType(SINGLE_USE).build());
|
new AllocationToken.Builder().setToken("tokeN").setTokenType(SINGLE_USE).build());
|
||||||
|
when(allocationTokenExtension.getAllocationToken()).thenReturn("tokeN");
|
||||||
assertThat(
|
assertThat(
|
||||||
flowUtils.loadTokenAndValidateDomainCreate(
|
flowUtils
|
||||||
|
.verifyAllocationTokenCreateIfPresent(
|
||||||
createCommand("blah.tld"),
|
createCommand("blah.tld"),
|
||||||
"tokeN",
|
|
||||||
Registry.get("tld"),
|
Registry.get("tld"),
|
||||||
"TheRegistrar",
|
"TheRegistrar",
|
||||||
DateTime.now(UTC)))
|
DateTime.now(UTC),
|
||||||
|
Optional.of(allocationTokenExtension))
|
||||||
|
.get())
|
||||||
.isEqualTo(token);
|
.isEqualTo(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void test_validateToken_failsOnNonexistentToken() {
|
void test_validateToken_successfullyVerifiesValidTokenExistingDomain() throws Exception {
|
||||||
assertValidateThrowsEppException(InvalidAllocationTokenException.class);
|
AllocationToken token =
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder().setToken("tokeN").setTokenType(SINGLE_USE).build());
|
||||||
|
when(allocationTokenExtension.getAllocationToken()).thenReturn("tokeN");
|
||||||
|
assertThat(
|
||||||
|
flowUtils
|
||||||
|
.verifyAllocationTokenIfPresent(
|
||||||
|
newDomainBase("blah.tld"),
|
||||||
|
Registry.get("tld"),
|
||||||
|
"TheRegistrar",
|
||||||
|
DateTime.now(UTC),
|
||||||
|
Optional.of(allocationTokenExtension))
|
||||||
|
.get())
|
||||||
|
.isEqualTo(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void test_validateToken_failsOnNullToken() {
|
void test_validateTokenCreate_failsOnNonexistentToken() {
|
||||||
|
assertValidateCreateThrowsEppException(InvalidAllocationTokenException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_validateTokenExistingDomain_failsOnNonexistentToken() {
|
||||||
|
assertValidateExistingDomainThrowsEppException(InvalidAllocationTokenException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_validateTokenCreate_failsOnNullToken() {
|
||||||
assertAboutEppExceptions()
|
assertAboutEppExceptions()
|
||||||
.that(
|
.that(
|
||||||
assertThrows(
|
assertThrows(
|
||||||
InvalidAllocationTokenException.class,
|
InvalidAllocationTokenException.class,
|
||||||
() ->
|
() ->
|
||||||
flowUtils.loadTokenAndValidateDomainCreate(
|
flowUtils.verifyAllocationTokenCreateIfPresent(
|
||||||
createCommand("blah.tld"),
|
createCommand("blah.tld"),
|
||||||
null,
|
|
||||||
Registry.get("tld"),
|
Registry.get("tld"),
|
||||||
"TheRegistrar",
|
"TheRegistrar",
|
||||||
DateTime.now(UTC))))
|
DateTime.now(UTC),
|
||||||
|
Optional.of(allocationTokenExtension))))
|
||||||
.marshalsToXml();
|
.marshalsToXml();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void test_validateToken_callsCustomLogic() {
|
void test_validateTokenExistingDomain_failsOnNullToken() {
|
||||||
|
assertAboutEppExceptions()
|
||||||
|
.that(
|
||||||
|
assertThrows(
|
||||||
|
InvalidAllocationTokenException.class,
|
||||||
|
() ->
|
||||||
|
flowUtils.verifyAllocationTokenIfPresent(
|
||||||
|
newDomainBase("blah.tld"),
|
||||||
|
Registry.get("tld"),
|
||||||
|
"TheRegistrar",
|
||||||
|
DateTime.now(UTC),
|
||||||
|
Optional.of(allocationTokenExtension))))
|
||||||
|
.marshalsToXml();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_validateTokenCreate_callsCustomLogic() {
|
||||||
AllocationTokenFlowUtils failingFlowUtils =
|
AllocationTokenFlowUtils failingFlowUtils =
|
||||||
new AllocationTokenFlowUtils(new FailingAllocationTokenCustomLogic());
|
new AllocationTokenFlowUtils(new FailingAllocationTokenCustomLogic());
|
||||||
persistResource(
|
persistResource(
|
||||||
new AllocationToken.Builder().setToken("tokeN").setTokenType(SINGLE_USE).build());
|
new AllocationToken.Builder().setToken("tokeN").setTokenType(SINGLE_USE).build());
|
||||||
|
when(allocationTokenExtension.getAllocationToken()).thenReturn("tokeN");
|
||||||
Exception thrown =
|
Exception thrown =
|
||||||
assertThrows(
|
assertThrows(
|
||||||
IllegalStateException.class,
|
IllegalStateException.class,
|
||||||
() ->
|
() ->
|
||||||
failingFlowUtils.loadTokenAndValidateDomainCreate(
|
failingFlowUtils.verifyAllocationTokenCreateIfPresent(
|
||||||
createCommand("blah.tld"),
|
createCommand("blah.tld"),
|
||||||
"tokeN",
|
|
||||||
Registry.get("tld"),
|
Registry.get("tld"),
|
||||||
"TheRegistrar",
|
"TheRegistrar",
|
||||||
DateTime.now(UTC)));
|
DateTime.now(UTC),
|
||||||
|
Optional.of(allocationTokenExtension)));
|
||||||
assertThat(thrown).hasMessageThat().isEqualTo("failed for tests");
|
assertThat(thrown).hasMessageThat().isEqualTo("failed for tests");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void test_validateToken_invalidForClientId() {
|
void test_validateTokenExistingDomain_callsCustomLogic() {
|
||||||
|
AllocationTokenFlowUtils failingFlowUtils =
|
||||||
|
new AllocationTokenFlowUtils(new FailingAllocationTokenCustomLogic());
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder().setToken("tokeN").setTokenType(SINGLE_USE).build());
|
||||||
|
when(allocationTokenExtension.getAllocationToken()).thenReturn("tokeN");
|
||||||
|
Exception thrown =
|
||||||
|
assertThrows(
|
||||||
|
IllegalStateException.class,
|
||||||
|
() ->
|
||||||
|
failingFlowUtils.verifyAllocationTokenIfPresent(
|
||||||
|
newDomainBase("blah.tld"),
|
||||||
|
Registry.get("tld"),
|
||||||
|
"TheRegistrar",
|
||||||
|
DateTime.now(UTC),
|
||||||
|
Optional.of(allocationTokenExtension)));
|
||||||
|
assertThat(thrown).hasMessageThat().isEqualTo("failed for tests");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_validateTokenCreate_invalidForClientId() {
|
||||||
persistResource(
|
persistResource(
|
||||||
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
|
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
|
||||||
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
|
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
|
||||||
.build());
|
.build());
|
||||||
assertValidateThrowsEppException(AllocationTokenNotValidForRegistrarException.class);
|
assertValidateCreateThrowsEppException(AllocationTokenNotValidForRegistrarException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void test_validateToken_invalidForTld() {
|
void test_validateTokenExistingDomain_invalidForClientId() {
|
||||||
|
persistResource(
|
||||||
|
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
|
||||||
|
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
|
||||||
|
.build());
|
||||||
|
assertValidateExistingDomainThrowsEppException(
|
||||||
|
AllocationTokenNotValidForRegistrarException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_validateTokenCreate_invalidForTld() {
|
||||||
persistResource(
|
persistResource(
|
||||||
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
|
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
|
||||||
.setAllowedTlds(ImmutableSet.of("nottld"))
|
.setAllowedTlds(ImmutableSet.of("nottld"))
|
||||||
.build());
|
.build());
|
||||||
assertValidateThrowsEppException(AllocationTokenNotValidForTldException.class);
|
assertValidateCreateThrowsEppException(AllocationTokenNotValidForTldException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void test_validateToken_beforePromoStart() {
|
void test_validateTokenExistingDomain_invalidForTld() {
|
||||||
|
persistResource(
|
||||||
|
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
|
||||||
|
.setAllowedTlds(ImmutableSet.of("nottld"))
|
||||||
|
.build());
|
||||||
|
assertValidateExistingDomainThrowsEppException(AllocationTokenNotValidForTldException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_validateTokenCreate_beforePromoStart() {
|
||||||
persistResource(createOneMonthPromoTokenBuilder(DateTime.now(UTC).plusDays(1)).build());
|
persistResource(createOneMonthPromoTokenBuilder(DateTime.now(UTC).plusDays(1)).build());
|
||||||
assertValidateThrowsEppException(AllocationTokenNotInPromotionException.class);
|
assertValidateCreateThrowsEppException(AllocationTokenNotInPromotionException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void test_validateToken_afterPromoEnd() {
|
void test_validateTokenExistingDomain_beforePromoStart() {
|
||||||
|
persistResource(createOneMonthPromoTokenBuilder(DateTime.now(UTC).plusDays(1)).build());
|
||||||
|
assertValidateExistingDomainThrowsEppException(AllocationTokenNotInPromotionException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_validateTokenCreate_afterPromoEnd() {
|
||||||
persistResource(createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusMonths(2)).build());
|
persistResource(createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusMonths(2)).build());
|
||||||
assertValidateThrowsEppException(AllocationTokenNotInPromotionException.class);
|
assertValidateCreateThrowsEppException(AllocationTokenNotInPromotionException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void test_validateToken_promoCancelled() {
|
void test_validateTokenExistingDomain_afterPromoEnd() {
|
||||||
|
persistResource(createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusMonths(2)).build());
|
||||||
|
assertValidateExistingDomainThrowsEppException(AllocationTokenNotInPromotionException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_validateTokenCreate_promoCancelled() {
|
||||||
// the promo would be valid but it was cancelled 12 hours ago
|
// the promo would be valid but it was cancelled 12 hours ago
|
||||||
persistResource(
|
persistResource(
|
||||||
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
|
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
|
||||||
|
@ -166,7 +266,22 @@ class AllocationTokenFlowUtilsTest {
|
||||||
.put(DateTime.now(UTC).minusHours(12), CANCELLED)
|
.put(DateTime.now(UTC).minusHours(12), CANCELLED)
|
||||||
.build())
|
.build())
|
||||||
.build());
|
.build());
|
||||||
assertValidateThrowsEppException(AllocationTokenNotInPromotionException.class);
|
assertValidateCreateThrowsEppException(AllocationTokenNotInPromotionException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_validateTokenExistingDomain_promoCancelled() {
|
||||||
|
// the promo would be valid but it was cancelled 12 hours ago
|
||||||
|
persistResource(
|
||||||
|
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
|
||||||
|
.setTokenStatusTransitions(
|
||||||
|
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||||
|
.put(START_OF_TIME, NOT_STARTED)
|
||||||
|
.put(DateTime.now(UTC).minusMonths(1), VALID)
|
||||||
|
.put(DateTime.now(UTC).minusHours(12), CANCELLED)
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
assertValidateExistingDomainThrowsEppException(AllocationTokenNotInPromotionException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -259,18 +374,33 @@ class AllocationTokenFlowUtilsTest {
|
||||||
.inOrder();
|
.inOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertValidateThrowsEppException(Class<? extends EppException> clazz) {
|
private void assertValidateCreateThrowsEppException(Class<? extends EppException> clazz) {
|
||||||
assertAboutEppExceptions()
|
assertAboutEppExceptions()
|
||||||
.that(
|
.that(
|
||||||
assertThrows(
|
assertThrows(
|
||||||
clazz,
|
clazz,
|
||||||
() ->
|
() ->
|
||||||
flowUtils.loadTokenAndValidateDomainCreate(
|
flowUtils.verifyAllocationTokenCreateIfPresent(
|
||||||
createCommand("blah.tld"),
|
createCommand("blah.tld"),
|
||||||
"tokeN",
|
|
||||||
Registry.get("tld"),
|
Registry.get("tld"),
|
||||||
"TheRegistrar",
|
"TheRegistrar",
|
||||||
DateTime.now(UTC))))
|
DateTime.now(UTC),
|
||||||
|
Optional.of(allocationTokenExtension))))
|
||||||
|
.marshalsToXml();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertValidateExistingDomainThrowsEppException(Class<? extends EppException> clazz) {
|
||||||
|
assertAboutEppExceptions()
|
||||||
|
.that(
|
||||||
|
assertThrows(
|
||||||
|
clazz,
|
||||||
|
() ->
|
||||||
|
flowUtils.verifyAllocationTokenIfPresent(
|
||||||
|
newDomainBase("blah.tld"),
|
||||||
|
Registry.get("tld"),
|
||||||
|
"TheRegistrar",
|
||||||
|
DateTime.now(UTC),
|
||||||
|
Optional.of(allocationTokenExtension))))
|
||||||
.marshalsToXml();
|
.marshalsToXml();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,7 +410,8 @@ class AllocationTokenFlowUtilsTest {
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AllocationToken.Builder createOneMonthPromoTokenBuilder(DateTime promoStart) {
|
private AllocationToken.Builder createOneMonthPromoTokenBuilder(DateTime promoStart) {
|
||||||
|
when(allocationTokenExtension.getAllocationToken()).thenReturn("tokeN");
|
||||||
return new AllocationToken.Builder()
|
return new AllocationToken.Builder()
|
||||||
.setToken("tokeN")
|
.setToken("tokeN")
|
||||||
.setTokenType(UNLIMITED_USE)
|
.setTokenType(UNLIMITED_USE)
|
||||||
|
@ -305,6 +436,16 @@ class AllocationTokenFlowUtilsTest {
|
||||||
throw new IllegalStateException("failed for tests");
|
throw new IllegalStateException("failed for tests");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AllocationToken validateToken(
|
||||||
|
DomainBase domain,
|
||||||
|
AllocationToken token,
|
||||||
|
Registry registry,
|
||||||
|
String registrarId,
|
||||||
|
DateTime now) {
|
||||||
|
throw new IllegalStateException("failed for tests");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableMap<InternetDomainName, String> checkDomainsWithToken(
|
public ImmutableMap<InternetDomainName, String> checkDomainsWithToken(
|
||||||
ImmutableList<InternetDomainName> domainNames,
|
ImmutableList<InternetDomainName> domainNames,
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
|
<command>
|
||||||
|
<renew>
|
||||||
|
<domain:renew
|
||||||
|
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
|
<domain:name>%DOMAIN%</domain:name>
|
||||||
|
<domain:curExpDate>2000-04-03</domain:curExpDate>
|
||||||
|
<domain:period unit="y">%YEARS%</domain:period>
|
||||||
|
</domain:renew>
|
||||||
|
</renew>
|
||||||
|
<extension>
|
||||||
|
<allocationToken:allocationToken
|
||||||
|
xmlns:allocationToken=
|
||||||
|
"urn:ietf:params:xml:ns:allocationToken-1.0">
|
||||||
|
abc123
|
||||||
|
</allocationToken:allocationToken>
|
||||||
|
</extension>
|
||||||
|
<clTRID>ABC-12345</clTRID>
|
||||||
|
</command>
|
||||||
|
</epp>
|
|
@ -489,10 +489,17 @@ comes in at the exact millisecond that the domain would have expired.
|
||||||
* 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.
|
||||||
* 2303
|
* 2303
|
||||||
* Resource with this id does not exist.
|
* Resource with this id does not exist.
|
||||||
* 2304
|
* 2304
|
||||||
* Resource status prohibits this operation.
|
* Resource status prohibits this operation.
|
||||||
|
* 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
|
||||||
* Periods for domain registrations must be specified in years.
|
* Periods for domain registrations must be specified in years.
|
||||||
* The requested fees cannot be provided in the requested currency.
|
* The requested fees cannot be provided in the requested currency.
|
||||||
|
|
Loading…
Add table
Reference in a new issue