Add extensibility framework for allocation tokens

This uses an extensibility mechanism similar to that of WhoisCommandFactory
and CustomLogicFactory, namely, that a fully qualified Java class is
specified in the YAML file for each environment with the allocation token
custom logic to be used.  By default, this points to a no-op base class
that does nothing.  Users that wish to add their own allocation token
custom logic can simply create a new class that extends
AllocationTokenCustomLogic and then configure it in their .yaml config
files.

This also renames the existing *FlowCustomLogic *Flow instance variables
from customLogic to flowCustomLogic, to avoid the potential confusion with
the new AllocationTokenCustomLogic class that also now exists.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=183003112
This commit is contained in:
mcilwain 2018-01-23 15:31:15 -08:00 committed by Ben McIlwain
parent e6a097a590
commit 9d532cb507
17 changed files with 320 additions and 61 deletions

View file

@ -1047,6 +1047,12 @@ public final class RegistryConfig {
return config.registryPolicy.whoisCommandFactoryClass; return config.registryPolicy.whoisCommandFactoryClass;
} }
@Provides
@Config("allocationTokenCustomLogicClass")
public static String provideAllocationTokenCustomLogicClass(RegistryConfigSettings config) {
return config.registryPolicy.allocationTokenCustomLogicClass;
}
/** /**
* Returns the header text at the top of the reserved terms exported list. * Returns the header text at the top of the reserved terms exported list.
* *

View file

@ -70,6 +70,7 @@ public class RegistryConfigSettings {
public String productName; public String productName;
public String customLogicFactoryClass; public String customLogicFactoryClass;
public String whoisCommandFactoryClass; public String whoisCommandFactoryClass;
public String allocationTokenCustomLogicClass;
public int contactAutomaticTransferDays; public int contactAutomaticTransferDays;
public String greetingServerId; public String greetingServerId;
public List<String> registrarChangesNotificationEmailAddresses; public List<String> registrarChangesNotificationEmailAddresses;

View file

@ -41,6 +41,10 @@ registryPolicy:
# See whois/WhoisCommandFactory.java # See whois/WhoisCommandFactory.java
whoisCommandFactoryClass: google.registry.whois.WhoisCommandFactory whoisCommandFactoryClass: google.registry.whois.WhoisCommandFactory
# Custom logic class for handling allocation tokens.
# See flows/domain/token/AllocationTokenCustomLogic.java
allocationTokenCustomLogicClass: google.registry.flows.domain.token.AllocationTokenCustomLogic
# Length of time after which contact transfers automatically conclude. # Length of time after which contact transfers automatically conclude.
contactAutomaticTransferDays: 5 contactAutomaticTransferDays: 5

View file

@ -48,6 +48,7 @@ import google.registry.flows.domain.DomainTransferQueryFlow;
import google.registry.flows.domain.DomainTransferRejectFlow; import google.registry.flows.domain.DomainTransferRejectFlow;
import google.registry.flows.domain.DomainTransferRequestFlow; import google.registry.flows.domain.DomainTransferRequestFlow;
import google.registry.flows.domain.DomainUpdateFlow; import google.registry.flows.domain.DomainUpdateFlow;
import google.registry.flows.domain.token.AllocationTokenModule;
import google.registry.flows.host.HostCheckFlow; import google.registry.flows.host.HostCheckFlow;
import google.registry.flows.host.HostCreateFlow; import google.registry.flows.host.HostCreateFlow;
import google.registry.flows.host.HostDeleteFlow; import google.registry.flows.host.HostDeleteFlow;
@ -63,6 +64,7 @@ import google.registry.model.eppcommon.Trid;
/** Dagger component for flow classes. */ /** Dagger component for flow classes. */
@FlowScope @FlowScope
@Subcomponent(modules = { @Subcomponent(modules = {
AllocationTokenModule.class,
AsyncFlowsModule.class, AsyncFlowsModule.class,
CustomLogicModule.class, CustomLogicModule.class,
DnsModule.class, DnsModule.class,

View file

@ -180,7 +180,7 @@ public final class DomainApplicationCreateFlow implements TransactionalFlow {
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject Trid trid; @Inject Trid trid;
@Inject EppResponse.Builder responseBuilder; @Inject EppResponse.Builder responseBuilder;
@Inject DomainApplicationCreateFlowCustomLogic customLogic; @Inject DomainApplicationCreateFlowCustomLogic flowCustomLogic;
@Inject DomainFlowTmchUtils tmchUtils; @Inject DomainFlowTmchUtils tmchUtils;
@Inject DomainPricingLogic pricingLogic; @Inject DomainPricingLogic pricingLogic;
@Inject DomainApplicationCreateFlow() {} @Inject DomainApplicationCreateFlow() {}
@ -192,7 +192,7 @@ public final class DomainApplicationCreateFlow implements TransactionalFlow {
SecDnsCreateExtension.class, SecDnsCreateExtension.class,
MetadataExtension.class, MetadataExtension.class,
LaunchCreateExtension.class); LaunchCreateExtension.class);
customLogic.beforeValidation(); flowCustomLogic.beforeValidation();
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
verifyRegistrarIsActive(clientId); verifyRegistrarIsActive(clientId);
@ -237,7 +237,7 @@ public final class DomainApplicationCreateFlow implements TransactionalFlow {
validateFeeChallenge(targetId, tld, now, feeCreate, feesAndCredits); validateFeeChallenge(targetId, tld, now, feeCreate, feesAndCredits);
Optional<SecDnsCreateExtension> secDnsCreate = Optional<SecDnsCreateExtension> secDnsCreate =
validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class)); validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class));
customLogic.afterValidation( flowCustomLogic.afterValidation(
AfterValidationParameters.newBuilder() AfterValidationParameters.newBuilder()
.setDomainName(domainName) .setDomainName(domainName)
.setYears(years) .setYears(years)
@ -282,7 +282,7 @@ public final class DomainApplicationCreateFlow implements TransactionalFlow {
prepareMarkedLrpTokenEntity(authInfo.getPw().getValue(), domainName, historyEntry)); prepareMarkedLrpTokenEntity(authInfo.getPw().getValue(), domainName, historyEntry));
} }
EntityChanges entityChanges = EntityChanges entityChanges =
customLogic.beforeSave( flowCustomLogic.beforeSave(
DomainApplicationCreateFlowCustomLogic.BeforeSaveParameters.newBuilder() DomainApplicationCreateFlowCustomLogic.BeforeSaveParameters.newBuilder()
.setNewApplication(newApplication) .setNewApplication(newApplication)
.setHistoryEntry(historyEntry) .setHistoryEntry(historyEntry)
@ -292,7 +292,7 @@ public final class DomainApplicationCreateFlow implements TransactionalFlow {
.build()); .build());
persistEntityChanges(entityChanges); persistEntityChanges(entityChanges);
BeforeResponseReturnData responseData = BeforeResponseReturnData responseData =
customLogic.beforeResponse( flowCustomLogic.beforeResponse(
BeforeResponseParameters.newBuilder() BeforeResponseParameters.newBuilder()
.setResData(DomainCreateData.create(targetId, now, null)) .setResData(DomainCreateData.create(targetId, now, null))
.setResponseExtensions( .setResponseExtensions(

View file

@ -14,9 +14,9 @@
package google.registry.flows.domain; package google.registry.flows.domain;
import static com.google.common.base.Strings.emptyToNull;
import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount; import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount;
import static google.registry.flows.domain.AllocationTokenFlowUtils.checkDomainsWithToken;
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld; import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
import static google.registry.flows.domain.DomainFlowUtils.getReservationTypes; import static google.registry.flows.domain.DomainFlowUtils.getReservationTypes;
import static google.registry.flows.domain.DomainFlowUtils.handleFeeRequest; import static google.registry.flows.domain.DomainFlowUtils.handleFeeRequest;
@ -44,6 +44,7 @@ import google.registry.flows.annotations.ReportingSpec;
import google.registry.flows.custom.DomainCheckFlowCustomLogic; import google.registry.flows.custom.DomainCheckFlowCustomLogic;
import google.registry.flows.custom.DomainCheckFlowCustomLogic.BeforeResponseParameters; import google.registry.flows.custom.DomainCheckFlowCustomLogic.BeforeResponseParameters;
import google.registry.flows.custom.DomainCheckFlowCustomLogic.BeforeResponseReturnData; import google.registry.flows.custom.DomainCheckFlowCustomLogic.BeforeResponseReturnData;
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
import google.registry.model.domain.DomainCommand.Check; import google.registry.model.domain.DomainCommand.Check;
import google.registry.model.domain.DomainResource; import google.registry.model.domain.DomainResource;
import google.registry.model.domain.fee.FeeCheckCommandExtension; import google.registry.model.domain.fee.FeeCheckCommandExtension;
@ -114,7 +115,8 @@ public final class DomainCheckFlow implements Flow {
@Inject @Superuser boolean isSuperuser; @Inject @Superuser boolean isSuperuser;
@Inject Clock clock; @Inject Clock clock;
@Inject EppResponse.Builder responseBuilder; @Inject EppResponse.Builder responseBuilder;
@Inject DomainCheckFlowCustomLogic customLogic; @Inject AllocationTokenFlowUtils allocationTokenFlowUtils;
@Inject DomainCheckFlowCustomLogic flowCustomLogic;
@Inject DomainPricingLogic pricingLogic; @Inject DomainPricingLogic pricingLogic;
@Inject DomainCheckFlow() {} @Inject DomainCheckFlow() {}
@ -122,7 +124,7 @@ public final class DomainCheckFlow implements Flow {
public EppResponse run() throws EppException { public EppResponse run() throws EppException {
extensionManager.register( extensionManager.register(
FeeCheckCommandExtension.class, LaunchCheckExtension.class, AllocationTokenExtension.class); FeeCheckCommandExtension.class, LaunchCheckExtension.class, AllocationTokenExtension.class);
customLogic.beforeValidation(); flowCustomLogic.beforeValidation();
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
List<String> targetIds = ((Check) resourceCommand).getTargetIds(); List<String> targetIds = ((Check) resourceCommand).getTargetIds();
@ -144,7 +146,7 @@ public final class DomainCheckFlow implements Flow {
} }
} }
ImmutableMap<String, InternetDomainName> domainNames = domains.build(); ImmutableMap<String, InternetDomainName> domainNames = domains.build();
customLogic.afterValidation( flowCustomLogic.afterValidation(
DomainCheckFlowCustomLogic.AfterValidationParameters.newBuilder() DomainCheckFlowCustomLogic.AfterValidationParameters.newBuilder()
.setDomainNames(domainNames) .setDomainNames(domainNames)
// TODO: Use as of date from fee extension v0.12 instead of now, if specified. // TODO: Use as of date from fee extension v0.12 instead of now, if specified.
@ -155,7 +157,7 @@ public final class DomainCheckFlow implements Flow {
eppInput.getSingleExtension(AllocationTokenExtension.class); eppInput.getSingleExtension(AllocationTokenExtension.class);
ImmutableMap<String, String> tokenCheckResults = ImmutableMap<String, String> tokenCheckResults =
allocationTokenExtension.isPresent() allocationTokenExtension.isPresent()
? checkDomainsWithToken( ? allocationTokenFlowUtils.checkDomainsWithToken(
targetIds, allocationTokenExtension.get().getAllocationToken(), clientId) targetIds, allocationTokenExtension.get().getAllocationToken(), clientId)
: ImmutableMap.of(); : ImmutableMap.of();
ImmutableList.Builder<DomainCheck> checks = new ImmutableList.Builder<>(); ImmutableList.Builder<DomainCheck> checks = new ImmutableList.Builder<>();
@ -165,7 +167,7 @@ public final class DomainCheckFlow implements Flow {
checks.add(DomainCheck.create(!message.isPresent(), targetId, message.orElse(null))); checks.add(DomainCheck.create(!message.isPresent(), targetId, message.orElse(null)));
} }
BeforeResponseReturnData responseData = BeforeResponseReturnData responseData =
customLogic.beforeResponse( flowCustomLogic.beforeResponse(
BeforeResponseParameters.newBuilder() BeforeResponseParameters.newBuilder()
.setDomainChecks(checks.build()) .setDomainChecks(checks.build())
.setResponseExtensions(getResponseExtensions(domainNames, now)) .setResponseExtensions(getResponseExtensions(domainNames, now))
@ -202,9 +204,7 @@ public final class DomainCheckFlow implements Flow {
if (!reservationTypes.isEmpty()) { if (!reservationTypes.isEmpty()) {
return Optional.of(getTypeOfHighestSeverity(reservationTypes).getMessageForCheck()); return Optional.of(getTypeOfHighestSeverity(reservationTypes).getMessageForCheck());
} }
return tokenCheckResults.containsKey(domainName.toString()) return Optional.ofNullable(emptyToNull(tokenCheckResults.get(domainName.toString())));
? Optional.of(tokenCheckResults.get(domainName.toString()))
: Optional.empty();
} }
/** Handle the fee check extension. */ /** Handle the fee check extension. */

View file

@ -17,8 +17,6 @@ package google.registry.flows.domain;
import static google.registry.flows.FlowUtils.persistEntityChanges; import static google.registry.flows.FlowUtils.persistEntityChanges;
import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist; import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist;
import static google.registry.flows.domain.AllocationTokenFlowUtils.redeemToken;
import static google.registry.flows.domain.AllocationTokenFlowUtils.verifyToken;
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld; import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences; import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences;
import static google.registry.flows.domain.DomainFlowUtils.createFeeCreateResponse; import static google.registry.flows.domain.DomainFlowUtils.createFeeCreateResponse;
@ -67,6 +65,7 @@ import google.registry.flows.custom.DomainCreateFlowCustomLogic;
import google.registry.flows.custom.DomainCreateFlowCustomLogic.BeforeResponseParameters; import google.registry.flows.custom.DomainCreateFlowCustomLogic.BeforeResponseParameters;
import google.registry.flows.custom.DomainCreateFlowCustomLogic.BeforeResponseReturnData; import google.registry.flows.custom.DomainCreateFlowCustomLogic.BeforeResponseReturnData;
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.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;
@ -112,12 +111,12 @@ import org.joda.time.Duration;
/** /**
* An EPP flow that creates a new domain resource. * An EPP flow that creates a new domain resource.
* *
* @error {@link google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException}
* @error {@link google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException}
* @error {@link google.registry.flows.exceptions.OnlyToolCanPassMetadataException} * @error {@link google.registry.flows.exceptions.OnlyToolCanPassMetadataException}
* @error {@link google.registry.flows.exceptions.ResourceAlreadyExistsException} * @error {@link google.registry.flows.exceptions.ResourceAlreadyExistsException}
* @error {@link google.registry.flows.EppException.UnimplementedExtensionException} * @error {@link google.registry.flows.EppException.UnimplementedExtensionException}
* @error {@link google.registry.flows.ExtensionManager.UndeclaredServiceExtensionException} * @error {@link google.registry.flows.ExtensionManager.UndeclaredServiceExtensionException}
* @error {@link AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException}
* @error {@link AllocationTokenFlowUtils.InvalidAllocationTokenException}
* @error {@link DomainCreateFlow.DomainHasOpenApplicationsException} * @error {@link DomainCreateFlow.DomainHasOpenApplicationsException}
* @error {@link DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException} * @error {@link DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException}
* @error {@link DomainFlowUtils.NotAuthorizedForTldException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException}
@ -185,7 +184,8 @@ public class DomainCreateFlow implements TransactionalFlow {
@Inject @Superuser boolean isSuperuser; @Inject @Superuser boolean isSuperuser;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject EppResponse.Builder responseBuilder; @Inject EppResponse.Builder responseBuilder;
@Inject DomainCreateFlowCustomLogic customLogic; @Inject AllocationTokenFlowUtils allocationTokenFlowUtils;
@Inject DomainCreateFlowCustomLogic flowCustomLogic;
@Inject DomainFlowTmchUtils tmchUtils; @Inject DomainFlowTmchUtils tmchUtils;
@Inject DomainPricingLogic pricingLogic; @Inject DomainPricingLogic pricingLogic;
@Inject DnsQueue dnsQueue; @Inject DnsQueue dnsQueue;
@ -199,7 +199,7 @@ public class DomainCreateFlow implements TransactionalFlow {
MetadataExtension.class, MetadataExtension.class,
LaunchCreateExtension.class, LaunchCreateExtension.class,
AllocationTokenExtension.class); AllocationTokenExtension.class);
customLogic.beforeValidation(); flowCustomLogic.beforeValidation();
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
verifyRegistrarIsActive(clientId); verifyRegistrarIsActive(clientId);
@ -262,7 +262,7 @@ public class DomainCreateFlow implements TransactionalFlow {
} }
Optional<AllocationToken> allocationToken = Optional<AllocationToken> allocationToken =
verifyAllocationTokenIfPresent(domainName, registry, clientId); verifyAllocationTokenIfPresent(domainName, registry, clientId);
customLogic.afterValidation( flowCustomLogic.afterValidation(
DomainCreateFlowCustomLogic.AfterValidationParameters.newBuilder() DomainCreateFlowCustomLogic.AfterValidationParameters.newBuilder()
.setDomainName(domainName) .setDomainName(domainName)
.setYears(years) .setYears(years)
@ -325,7 +325,8 @@ public class DomainCreateFlow implements TransactionalFlow {
ForeignKeyIndex.create(newDomain, newDomain.getDeletionTime()), ForeignKeyIndex.create(newDomain, newDomain.getDeletionTime()),
EppResourceIndex.create(Key.create(newDomain))); EppResourceIndex.create(Key.create(newDomain)));
allocationToken.ifPresent(t -> entitiesToSave.add(redeemToken(t, Key.create(historyEntry)))); allocationToken.ifPresent(
t -> entitiesToSave.add(allocationTokenFlowUtils.redeemToken(t, Key.create(historyEntry))));
// Anchor tenant registrations override LRP, and landrush applications can skip it. // Anchor tenant registrations override LRP, and landrush applications can skip it.
// If a token is passed in outside of an LRP phase, it is simply ignored (i.e. never redeemed). // If a token is passed in outside of an LRP phase, it is simply ignored (i.e. never redeemed).
if (isLrpCreate(registry, isAnchorTenant, now)) { if (isLrpCreate(registry, isAnchorTenant, now)) {
@ -335,7 +336,7 @@ public class DomainCreateFlow implements TransactionalFlow {
enqueueTasks(isSunriseCreate, hasClaimsNotice, newDomain); enqueueTasks(isSunriseCreate, hasClaimsNotice, newDomain);
EntityChanges entityChanges = EntityChanges entityChanges =
customLogic.beforeSave( flowCustomLogic.beforeSave(
DomainCreateFlowCustomLogic.BeforeSaveParameters.newBuilder() DomainCreateFlowCustomLogic.BeforeSaveParameters.newBuilder()
.setNewDomain(newDomain) .setNewDomain(newDomain)
.setHistoryEntry(historyEntry) .setHistoryEntry(historyEntry)
@ -346,7 +347,7 @@ public class DomainCreateFlow implements TransactionalFlow {
persistEntityChanges(entityChanges); persistEntityChanges(entityChanges);
BeforeResponseReturnData responseData = BeforeResponseReturnData responseData =
customLogic.beforeResponse( flowCustomLogic.beforeResponse(
BeforeResponseParameters.newBuilder() BeforeResponseParameters.newBuilder()
.setResData(DomainCreateData.create(targetId, now, registrationExpirationTime)) .setResData(DomainCreateData.create(targetId, now, registrationExpirationTime))
.setResponseExtensions(createResponseExtensions(feeCreate, feesAndCredits)) .setResponseExtensions(createResponseExtensions(feeCreate, feesAndCredits))
@ -389,7 +390,8 @@ public class DomainCreateFlow implements TransactionalFlow {
eppInput.getSingleExtension(AllocationTokenExtension.class); eppInput.getSingleExtension(AllocationTokenExtension.class);
return Optional.ofNullable( return Optional.ofNullable(
extension.isPresent() extension.isPresent()
? verifyToken(domainName, extension.get().getAllocationToken(), registry, clientId) ? allocationTokenFlowUtils.verifyToken(
domainName, extension.get().getAllocationToken(), registry, clientId)
: null); : null);
} }

View file

@ -128,14 +128,14 @@ public final class DomainDeleteFlow implements TransactionalFlow {
@Inject DnsQueue dnsQueue; @Inject DnsQueue dnsQueue;
@Inject Trid trid; @Inject Trid trid;
@Inject EppResponse.Builder responseBuilder; @Inject EppResponse.Builder responseBuilder;
@Inject DomainDeleteFlowCustomLogic customLogic; @Inject DomainDeleteFlowCustomLogic flowCustomLogic;
@Inject DomainDeleteFlow() {} @Inject DomainDeleteFlow() {}
@Override @Override
public final EppResponse run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register( extensionManager.register(
MetadataExtension.class, SecDnsCreateExtension.class, DomainDeleteSuperuserExtension.class); MetadataExtension.class, SecDnsCreateExtension.class, DomainDeleteSuperuserExtension.class);
customLogic.beforeValidation(); flowCustomLogic.beforeValidation();
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = ofy().getTransactionTime(); DateTime now = ofy().getTransactionTime();
@ -143,7 +143,7 @@ public final class DomainDeleteFlow implements TransactionalFlow {
DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now);
Registry registry = Registry.get(existingDomain.getTld()); Registry registry = Registry.get(existingDomain.getTld());
verifyDeleteAllowed(existingDomain, registry, now); verifyDeleteAllowed(existingDomain, registry, now);
customLogic.afterValidation( flowCustomLogic.afterValidation(
AfterValidationParameters.newBuilder().setExistingDomain(existingDomain).build()); AfterValidationParameters.newBuilder().setExistingDomain(existingDomain).build());
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>(); ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
Builder builder; Builder builder;
@ -214,7 +214,7 @@ public final class DomainDeleteFlow implements TransactionalFlow {
} }
} }
entitiesToSave.add(newDomain, historyEntry); entitiesToSave.add(newDomain, historyEntry);
EntityChanges entityChanges = customLogic.beforeSave( EntityChanges entityChanges = flowCustomLogic.beforeSave(
BeforeSaveParameters.newBuilder() BeforeSaveParameters.newBuilder()
.setExistingDomain(existingDomain) .setExistingDomain(existingDomain)
.setNewDomain(newDomain) .setNewDomain(newDomain)
@ -223,7 +223,7 @@ public final class DomainDeleteFlow implements TransactionalFlow {
.build()); .build());
persistEntityChanges(entityChanges); persistEntityChanges(entityChanges);
BeforeResponseReturnData responseData = BeforeResponseReturnData responseData =
customLogic.beforeResponse( flowCustomLogic.beforeResponse(
BeforeResponseParameters.newBuilder() BeforeResponseParameters.newBuilder()
.setResultCode( .setResultCode(
newDomain.getDeletionTime().isAfter(now) newDomain.getDeletionTime().isAfter(now)

View file

@ -82,7 +82,7 @@ public final class DomainInfoFlow implements Flow {
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject Clock clock; @Inject Clock clock;
@Inject EppResponse.Builder responseBuilder; @Inject EppResponse.Builder responseBuilder;
@Inject DomainInfoFlowCustomLogic customLogic; @Inject DomainInfoFlowCustomLogic flowCustomLogic;
@Inject DomainPricingLogic pricingLogic; @Inject DomainPricingLogic pricingLogic;
@Inject @Inject
@ -91,14 +91,15 @@ public final class DomainInfoFlow implements Flow {
@Override @Override
public final EppResponse run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(FeeInfoCommandExtensionV06.class); extensionManager.register(FeeInfoCommandExtensionV06.class);
customLogic.beforeValidation(); flowCustomLogic.beforeValidation();
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = clock.nowUtc(); DateTime now = clock.nowUtc();
DomainResource domain = verifyExistence( DomainResource domain = verifyExistence(
DomainResource.class, targetId, loadByForeignKey(DomainResource.class, targetId, now)); DomainResource.class, targetId, loadByForeignKey(DomainResource.class, targetId, now));
verifyOptionalAuthInfo(authInfo, domain); verifyOptionalAuthInfo(authInfo, domain);
customLogic.afterValidation(AfterValidationParameters.newBuilder().setDomain(domain).build()); flowCustomLogic.afterValidation(
AfterValidationParameters.newBuilder().setDomain(domain).build());
// Prefetch all referenced resources. Calling values() blocks until loading is done. // Prefetch all referenced resources. Calling values() blocks until loading is done.
ofy().load() ofy().load()
.values(union(domain.getNameservers(), domain.getReferencedContacts())).values(); .values(union(domain.getNameservers(), domain.getReferencedContacts())).values();
@ -131,7 +132,7 @@ public final class DomainInfoFlow implements Flow {
.setAuthInfo(domain.getAuthInfo()); .setAuthInfo(domain.getAuthInfo());
} }
BeforeResponseReturnData responseData = BeforeResponseReturnData responseData =
customLogic.beforeResponse( flowCustomLogic.beforeResponse(
BeforeResponseParameters.newBuilder() BeforeResponseParameters.newBuilder()
.setDomain(domain) .setDomain(domain)
.setResData(infoBuilder.build()) .setResData(infoBuilder.build())

View file

@ -123,14 +123,14 @@ public final class DomainRenewFlow implements TransactionalFlow {
@Inject @Superuser boolean isSuperuser; @Inject @Superuser boolean isSuperuser;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject EppResponse.Builder responseBuilder; @Inject EppResponse.Builder responseBuilder;
@Inject DomainRenewFlowCustomLogic customLogic; @Inject DomainRenewFlowCustomLogic flowCustomLogic;
@Inject DomainPricingLogic pricingLogic; @Inject DomainPricingLogic pricingLogic;
@Inject DomainRenewFlow() {} @Inject DomainRenewFlow() {}
@Override @Override
public final EppResponse run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(FeeRenewCommandExtension.class, MetadataExtension.class); extensionManager.register(FeeRenewCommandExtension.class, MetadataExtension.class);
customLogic.beforeValidation(); flowCustomLogic.beforeValidation();
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = ofy().getTransactionTime(); DateTime now = ofy().getTransactionTime();
@ -147,7 +147,7 @@ public final class DomainRenewFlow implements TransactionalFlow {
FeesAndCredits feesAndCredits = FeesAndCredits feesAndCredits =
pricingLogic.getRenewPrice(Registry.get(existingDomain.getTld()), targetId, now, years); pricingLogic.getRenewPrice(Registry.get(existingDomain.getTld()), targetId, now, years);
validateFeeChallenge(targetId, existingDomain.getTld(), now, feeRenew, feesAndCredits); validateFeeChallenge(targetId, existingDomain.getTld(), now, feeRenew, feesAndCredits);
customLogic.afterValidation( flowCustomLogic.afterValidation(
AfterValidationParameters.newBuilder() AfterValidationParameters.newBuilder()
.setExistingDomain(existingDomain) .setExistingDomain(existingDomain)
.setNow(now) .setNow(now)
@ -178,7 +178,7 @@ public final class DomainRenewFlow implements TransactionalFlow {
.addGracePeriod(GracePeriod.forBillingEvent(GracePeriodStatus.RENEW, explicitRenewEvent)) .addGracePeriod(GracePeriod.forBillingEvent(GracePeriodStatus.RENEW, explicitRenewEvent))
.build(); .build();
EntityChanges entityChanges = EntityChanges entityChanges =
customLogic.beforeSave( flowCustomLogic.beforeSave(
BeforeSaveParameters.newBuilder() BeforeSaveParameters.newBuilder()
.setExistingDomain(existingDomain) .setExistingDomain(existingDomain)
.setNewDomain(newDomain) .setNewDomain(newDomain)
@ -198,7 +198,7 @@ public final class DomainRenewFlow implements TransactionalFlow {
.build()); .build());
persistEntityChanges(entityChanges); persistEntityChanges(entityChanges);
BeforeResponseReturnData responseData = BeforeResponseReturnData responseData =
customLogic.beforeResponse( flowCustomLogic.beforeResponse(
BeforeResponseParameters.newBuilder() BeforeResponseParameters.newBuilder()
.setDomain(newDomain) .setDomain(newDomain)
.setResData(DomainRenewData.create(targetId, newExpirationTime)) .setResData(DomainRenewData.create(targetId, newExpirationTime))

View file

@ -155,7 +155,7 @@ public final class DomainUpdateFlow implements TransactionalFlow {
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject DnsQueue dnsQueue; @Inject DnsQueue dnsQueue;
@Inject EppResponse.Builder responseBuilder; @Inject EppResponse.Builder responseBuilder;
@Inject DomainUpdateFlowCustomLogic customLogic; @Inject DomainUpdateFlowCustomLogic flowCustomLogic;
@Inject DomainPricingLogic pricingLogic; @Inject DomainPricingLogic pricingLogic;
@Inject DomainUpdateFlow() {} @Inject DomainUpdateFlow() {}
@ -165,14 +165,14 @@ public final class DomainUpdateFlow implements TransactionalFlow {
FeeUpdateCommandExtension.class, FeeUpdateCommandExtension.class,
MetadataExtension.class, MetadataExtension.class,
SecDnsUpdateExtension.class); SecDnsUpdateExtension.class);
customLogic.beforeValidation(); flowCustomLogic.beforeValidation();
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = ofy().getTransactionTime(); DateTime now = ofy().getTransactionTime();
Update command = cloneAndLinkReferences((Update) resourceCommand, now); Update command = cloneAndLinkReferences((Update) resourceCommand, now);
DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now);
verifyUpdateAllowed(command, existingDomain, now); verifyUpdateAllowed(command, existingDomain, now);
customLogic.afterValidation( flowCustomLogic.afterValidation(
AfterValidationParameters.newBuilder().setExistingDomain(existingDomain).build()); AfterValidationParameters.newBuilder().setExistingDomain(existingDomain).build());
HistoryEntry historyEntry = buildHistoryEntry(existingDomain, now); HistoryEntry historyEntry = buildHistoryEntry(existingDomain, now);
DomainResource newDomain = performUpdate(command, existingDomain, now); DomainResource newDomain = performUpdate(command, existingDomain, now);
@ -194,7 +194,7 @@ public final class DomainUpdateFlow implements TransactionalFlow {
createBillingEventForStatusUpdates(existingDomain, newDomain, historyEntry, now); createBillingEventForStatusUpdates(existingDomain, newDomain, historyEntry, now);
statusUpdateBillingEvent.ifPresent(entitiesToSave::add); statusUpdateBillingEvent.ifPresent(entitiesToSave::add);
EntityChanges entityChanges = EntityChanges entityChanges =
customLogic.beforeSave( flowCustomLogic.beforeSave(
BeforeSaveParameters.newBuilder() BeforeSaveParameters.newBuilder()
.setHistoryEntry(historyEntry) .setHistoryEntry(historyEntry)
.setNewDomain(newDomain) .setNewDomain(newDomain)

View file

@ -0,0 +1,44 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.flows.domain.token;
import com.google.common.collect.ImmutableMap;
import com.google.common.net.InternetDomainName;
import google.registry.flows.EppException;
import google.registry.model.domain.AllocationToken;
import google.registry.model.registry.Registry;
/**
* A no-op base class for allocation token custom logic.
*
* <p>Extend this class and override the hook(s) to perform custom logic.
*/
public class AllocationTokenCustomLogic {
/** Performs additional custom logic for verifying a token. */
public AllocationToken verifyToken(
InternetDomainName domainName, AllocationToken token, Registry registry, String clientId)
throws EppException {
// Do nothing.
return token;
}
/** Performs additional custom logic for performing domain checks using a token. */
public ImmutableMap<String, String> checkDomainsWithToken(
ImmutableMap<String, String> checkResults, AllocationToken tokenEntity, String clientId) {
// Do nothing.
return checkResults;
}
}

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package google.registry.flows.domain; package google.registry.flows.domain.token;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
@ -27,17 +27,26 @@ import google.registry.model.registry.Registry;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import javax.inject.Inject;
/** Static utility functions for dealing with {@link AllocationToken}s in domain flows. */ /** Utility functions for dealing with {@link AllocationToken}s in domain flows. */
// TODO: Add a test class.
public class AllocationTokenFlowUtils { public class AllocationTokenFlowUtils {
final AllocationTokenCustomLogic tokenCustomLogic;
@Inject
AllocationTokenFlowUtils(AllocationTokenCustomLogic tokenCustomLogic) {
this.tokenCustomLogic = tokenCustomLogic;
}
/** /**
* Verifies that a given allocation token string is valid. * Verifies that a given allocation token string is valid.
* *
* @return the loaded {@link AllocationToken} for that string. * @return the loaded {@link AllocationToken} for that string.
* @throws InvalidAllocationTokenException if the token doesn't exist. * @throws InvalidAllocationTokenException if the token doesn't exist.
*/ */
static AllocationToken verifyToken( public AllocationToken verifyToken(
InternetDomainName domainName, String token, Registry registry, String clientId) InternetDomainName domainName, String token, Registry registry, String clientId)
throws EppException { throws EppException {
AllocationToken tokenEntity = ofy().load().key(Key.create(AllocationToken.class, token)).now(); AllocationToken tokenEntity = ofy().load().key(Key.create(AllocationToken.class, token)).now();
@ -47,7 +56,7 @@ public class AllocationTokenFlowUtils {
if (tokenEntity.isRedeemed()) { if (tokenEntity.isRedeemed()) {
throw new AlreadyRedeemedAllocationTokenException(); throw new AlreadyRedeemedAllocationTokenException();
} }
return tokenEntity; return tokenCustomLogic.verifyToken(domainName, tokenEntity, registry, clientId);
} }
/** /**
@ -55,9 +64,9 @@ public class AllocationTokenFlowUtils {
* *
* @return A map of domain names to domain check error response messages. If a message is present * @return A map of domain names to domain check error response messages. If a message is present
* for a a given domain then it does not validate with this allocation token; domains that do * for a a given domain then it does not validate with this allocation token; domains that do
* validate are not present in the map. * validate have blank messages (i.e. no error).
*/ */
static ImmutableMap<String, String> checkDomainsWithToken( public ImmutableMap<String, String> checkDomainsWithToken(
List<String> domainNames, String token, String clientId) { List<String> domainNames, String token, String clientId) {
AllocationToken tokenEntity = ofy().load().key(Key.create(AllocationToken.class, token)).now(); AllocationToken tokenEntity = ofy().load().key(Key.create(AllocationToken.class, token)).now();
String result; String result;
@ -66,23 +75,23 @@ public class AllocationTokenFlowUtils {
} else if (tokenEntity.isRedeemed()) { } else if (tokenEntity.isRedeemed()) {
result = AlreadyRedeemedAllocationTokenException.ERROR_MSG_SHORT; result = AlreadyRedeemedAllocationTokenException.ERROR_MSG_SHORT;
} else { } else {
return ImmutableMap.of(); result = "";
} }
// TODO(b/70628322): For now all checks yield the same result, but custom logic will soon allow ImmutableMap<String, String> checkResults =
// them to differ, e.g. if tokens can only be used on certain TLDs. domainNames
return domainNames .stream()
.stream() .collect(ImmutableMap.toImmutableMap(Function.identity(), domainName -> result));
.collect(ImmutableMap.toImmutableMap(Function.identity(), domainName -> result)); return tokenCustomLogic.checkDomainsWithToken(checkResults, tokenEntity, clientId);
} }
/** Redeems an {@link AllocationToken}, returning the redeemed copy. */ /** Redeems an {@link AllocationToken}, returning the redeemed copy. */
static AllocationToken redeemToken( public AllocationToken redeemToken(
AllocationToken token, Key<HistoryEntry> redemptionHistoryEntry) { AllocationToken token, Key<HistoryEntry> redemptionHistoryEntry) {
return token.asBuilder().setRedemptionHistoryEntry(redemptionHistoryEntry).build(); return token.asBuilder().setRedemptionHistoryEntry(redemptionHistoryEntry).build();
} }
/** The allocation token was already redeemed. */ /** The allocation token was already redeemed. */
static class AlreadyRedeemedAllocationTokenException public static class AlreadyRedeemedAllocationTokenException
extends AssociationProhibitsOperationException { extends AssociationProhibitsOperationException {
public static final String ERROR_MSG_LONG = "The allocation token was already redeemed"; public static final String ERROR_MSG_LONG = "The allocation token was already redeemed";
@ -96,7 +105,7 @@ public class AllocationTokenFlowUtils {
} }
/** The allocation token is invalid. */ /** The allocation token is invalid. */
static class InvalidAllocationTokenException extends ParameterValueSyntaxErrorException { public static class InvalidAllocationTokenException extends ParameterValueSyntaxErrorException {
public InvalidAllocationTokenException() { public InvalidAllocationTokenException() {
super("The allocation token is invalid"); super("The allocation token is invalid");
} }

View file

@ -0,0 +1,33 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.flows.domain.token;
import static google.registry.util.TypeUtils.getClassFromString;
import static google.registry.util.TypeUtils.instantiate;
import dagger.Module;
import dagger.Provides;
import google.registry.config.RegistryConfig.Config;
/** Dagger module for allocation token classes. */
@Module
public class AllocationTokenModule {
@Provides
static AllocationTokenCustomLogic provideAllocationTokenCustomLogic(
@Config("allocationTokenCustomLogicClass") String customClass) {
return instantiate(getClassFromString(customClass, AllocationTokenCustomLogic.class));
}
}

View file

@ -71,7 +71,7 @@ GenTestRules(
shard_count = 4, shard_count = 4,
test_files = glob([ test_files = glob([
"*Test.java", "*Test.java",
"*/*Test.java", "**/*Test.java",
]), ]),
deps = [":flows"], deps = [":flows"],
) )

View file

@ -64,8 +64,6 @@ import google.registry.flows.EppException.UnimplementedExtensionException;
import google.registry.flows.EppRequestSource; import google.registry.flows.EppRequestSource;
import google.registry.flows.ExtensionManager.UndeclaredServiceExtensionException; import google.registry.flows.ExtensionManager.UndeclaredServiceExtensionException;
import google.registry.flows.ResourceFlowTestCase; import google.registry.flows.ResourceFlowTestCase;
import google.registry.flows.domain.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException;
import google.registry.flows.domain.AllocationTokenFlowUtils.InvalidAllocationTokenException;
import google.registry.flows.domain.DomainCreateFlow.DomainHasOpenApplicationsException; import google.registry.flows.domain.DomainCreateFlow.DomainHasOpenApplicationsException;
import google.registry.flows.domain.DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException; import google.registry.flows.domain.DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException;
import google.registry.flows.domain.DomainFlowUtils.AcceptedTooLongAgoException; import google.registry.flows.domain.DomainFlowUtils.AcceptedTooLongAgoException;
@ -117,6 +115,8 @@ import google.registry.flows.domain.DomainFlowUtils.TrailingDashException;
import google.registry.flows.domain.DomainFlowUtils.UnexpectedClaimsNoticeException; import google.registry.flows.domain.DomainFlowUtils.UnexpectedClaimsNoticeException;
import google.registry.flows.domain.DomainFlowUtils.UnsupportedFeeAttributeException; import google.registry.flows.domain.DomainFlowUtils.UnsupportedFeeAttributeException;
import google.registry.flows.domain.DomainFlowUtils.UnsupportedMarkTypeException; import google.registry.flows.domain.DomainFlowUtils.UnsupportedMarkTypeException;
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException;
import google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException;
import google.registry.flows.exceptions.OnlyToolCanPassMetadataException; import google.registry.flows.exceptions.OnlyToolCanPassMetadataException;
import google.registry.flows.exceptions.ResourceAlreadyExistsException; import google.registry.flows.exceptions.ResourceAlreadyExistsException;
import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent;

View file

@ -0,0 +1,157 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.flows.domain.token;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
import static google.registry.testing.JUnitBackports.expectThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.net.InternetDomainName;
import com.googlecode.objectify.Key;
import google.registry.flows.EppException;
import google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException;
import google.registry.model.domain.AllocationToken;
import google.registry.model.registry.Registry;
import google.registry.model.reporting.HistoryEntry;
import google.registry.testing.AppEngineRule;
import google.registry.testing.ShardableTestCase;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link AllocationTokenFlowUtils}. */
@RunWith(JUnit4.class)
public class AllocationTokenFlowUtilsTest extends ShardableTestCase {
@Rule public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build();
@Before
public void initTest() {
createTld("tld");
}
@Test
public void test_verifyToken_successfullyVerifiesValidToken() throws Exception {
AllocationToken token =
persistResource(new AllocationToken.Builder().setToken("tokeN").build());
AllocationTokenFlowUtils flowUtils =
new AllocationTokenFlowUtils(new AllocationTokenCustomLogic());
assertThat(
flowUtils.verifyToken(
InternetDomainName.from("blah.tld"), "tokeN", Registry.get("tld"), "TheRegistrar"))
.isEqualTo(token);
}
@Test
public void test_verifyToken_failsOnNonexistentToken() throws Exception {
AllocationTokenFlowUtils flowUtils =
new AllocationTokenFlowUtils(new AllocationTokenCustomLogic());
EppException thrown =
expectThrows(
InvalidAllocationTokenException.class,
() ->
flowUtils.verifyToken(
InternetDomainName.from("blah.tld"),
"tokeN",
Registry.get("tld"),
"TheRegistrar"));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
public void test_verifyToken_callsCustomLogic() throws Exception {
persistResource(new AllocationToken.Builder().setToken("tokeN").build());
AllocationTokenFlowUtils flowUtils =
new AllocationTokenFlowUtils(new FailingAllocationTokenCustomLogic());
Exception thrown =
expectThrows(
IllegalStateException.class,
() ->
flowUtils.verifyToken(
InternetDomainName.from("blah.tld"),
"tokeN",
Registry.get("tld"),
"TheRegistrar"));
assertThat(thrown).hasMessageThat().isEqualTo("failed for tests");
}
@Test
public void test_checkDomainsWithToken_successfullyVerifiesValidToken() throws Exception {
persistResource(new AllocationToken.Builder().setToken("tokeN").build());
AllocationTokenFlowUtils flowUtils =
new AllocationTokenFlowUtils(new AllocationTokenCustomLogic());
assertThat(
flowUtils.checkDomainsWithToken(
ImmutableList.of("blah.tld", "blah2.tld"), "tokeN", "TheRegistrar"))
.containsExactlyEntriesIn(ImmutableMap.of("blah.tld", "", "blah2.tld", ""));
}
@Test
public void test_checkDomainsWithToken_showsFailureMessageForRedeemedToken() throws Exception {
persistResource(
new AllocationToken.Builder()
.setToken("tokeN")
.setRedemptionHistoryEntry(Key.create(HistoryEntry.class, 101L))
.build());
AllocationTokenFlowUtils flowUtils =
new AllocationTokenFlowUtils(new AllocationTokenCustomLogic());
assertThat(
flowUtils.checkDomainsWithToken(
ImmutableList.of("blah.tld", "blah2.tld"), "tokeN", "TheRegistrar"))
.containsExactlyEntriesIn(
ImmutableMap.of(
"blah.tld",
"Alloc token was already redeemed",
"blah2.tld",
"Alloc token was already redeemed"));
}
@Test
public void test_checkDomainsWithToken_callsCustomLogic() throws Exception {
persistResource(new AllocationToken.Builder().setToken("tokeN").build());
AllocationTokenFlowUtils flowUtils =
new AllocationTokenFlowUtils(new FailingAllocationTokenCustomLogic());
Exception thrown =
expectThrows(
IllegalStateException.class,
() ->
flowUtils.checkDomainsWithToken(
ImmutableList.of("blah.tld", "blah2.tld"), "tokeN", "TheRegistrar"));
assertThat(thrown).hasMessageThat().isEqualTo("failed for tests");
}
/** An {@link AllocationTokenCustomLogic} class that throws exceptions on every method. */
private static class FailingAllocationTokenCustomLogic extends AllocationTokenCustomLogic {
@Override
public AllocationToken verifyToken(
InternetDomainName domainName, AllocationToken token, Registry registry, String clientId)
throws EppException {
throw new IllegalStateException("failed for tests");
}
@Override
public ImmutableMap<String, String> checkDomainsWithToken(
ImmutableMap<String, String> checkResults, AllocationToken tokenEntity, String clientId) {
throw new IllegalStateException("failed for tests");
}
}
}