diff --git a/core/src/main/java/google/registry/flows/domain/DomainCheckFlow.java b/core/src/main/java/google/registry/flows/domain/DomainCheckFlow.java index 035760e17..43a70b1d0 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainCheckFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainCheckFlow.java @@ -53,6 +53,11 @@ import google.registry.flows.custom.DomainCheckFlowCustomLogic.BeforeResponsePar import google.registry.flows.custom.DomainCheckFlowCustomLogic.BeforeResponseReturnData; import google.registry.flows.domain.token.AllocationTokenDomainCheckResults; import google.registry.flows.domain.token.AllocationTokenFlowUtils; +import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException; +import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForCommandException; +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.model.EppResource; import google.registry.model.ForeignKeyUtils; import google.registry.model.billing.BillingEvent; @@ -277,22 +282,52 @@ public final class DomainCheckFlow implements Flow { DomainFlowUtils.checkForDefaultToken( Registry.get(InternetDomainName.from(domainName).parent().toString()), domainName, + feeCheckItem.getCommandName(), registrarId, now); FeeCheckResponseExtensionItem.Builder builder = feeCheckItem.createResponseBuilder(); Optional domain = Optional.ofNullable(domainObjs.get(domainName)); - handleFeeRequest( - feeCheckItem, - builder, - domainNames.get(domainName), - domain, - feeCheck.getCurrency(), - now, - pricingLogic, - allocationToken.isPresent() ? allocationToken : defaultToken, - availableDomains.contains(domainName), - recurrences.getOrDefault(domainName, null)); - responseItems.add(builder.setDomainNameIfSupported(domainName).build()); + try { + if (allocationToken.isPresent()) { + AllocationTokenFlowUtils.validateToken( + InternetDomainName.from(domainName), + allocationToken.get(), + feeCheckItem.getCommandName(), + registrarId, + now); + } + handleFeeRequest( + feeCheckItem, + builder, + domainNames.get(domainName), + domain, + feeCheck.getCurrency(), + now, + pricingLogic, + allocationToken.isPresent() ? allocationToken : defaultToken, + availableDomains.contains(domainName), + recurrences.getOrDefault(domainName, null)); + responseItems.add(builder.setDomainNameIfSupported(domainName).build()); + } catch (AllocationTokenNotValidForCommandException + | AllocationTokenNotValidForDomainException + | AllocationTokenNotValidForRegistrarException + | AllocationTokenNotValidForTldException + | AllocationTokenNotInPromotionException e) { + // Allocation token is either not an active token or it is not valid for the EPP command, + // registrar, domain, or TLD. + Registry registry = Registry.get(InternetDomainName.from(domainName).parent().toString()); + responseItems.add( + builder + .setDomainNameIfSupported(domainName) + .setPeriod(feeCheckItem.getPeriod()) + .setCommand( + feeCheckItem.getCommandName(), + feeCheckItem.getPhase(), + feeCheckItem.getSubphase()) + .setCurrencyIfSupported(registry.getCurrency()) + .setClass("token-not-supported") + .build()); + } } } return ImmutableList.of(feeCheck.createResponse(responseItems.build())); diff --git a/core/src/main/java/google/registry/flows/domain/DomainCreateFlow.java b/core/src/main/java/google/registry/flows/domain/DomainCreateFlow.java index 8b47d6494..fcb208e6d 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainCreateFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainCreateFlow.java @@ -89,6 +89,7 @@ import google.registry.model.domain.DomainHistory; import google.registry.model.domain.GracePeriod; import google.registry.model.domain.Period; import google.registry.model.domain.fee.FeeCreateCommandExtension; +import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName; import google.registry.model.domain.fee.FeeTransformResponseExtension; import google.registry.model.domain.launch.LaunchCreateExtension; import google.registry.model.domain.metadata.MetadataExtension; @@ -276,7 +277,8 @@ public final class DomainCreateFlow implements TransactionalFlow { boolean defaultTokenUsed = false; if (!allocationToken.isPresent() && !registry.getDefaultPromoTokens().isEmpty()) { allocationToken = - DomainFlowUtils.checkForDefaultToken(registry, command.getDomainName(), registrarId, now); + DomainFlowUtils.checkForDefaultToken( + registry, command.getDomainName(), CommandName.CREATE, registrarId, now); if (allocationToken.isPresent()) { defaultTokenUsed = true; } diff --git a/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java b/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java index 6af91c55d..72d642a72 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java +++ b/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java @@ -97,6 +97,7 @@ import google.registry.model.domain.fee.BaseFee.FeeType; import google.registry.model.domain.fee.Credit; import google.registry.model.domain.fee.Fee; import google.registry.model.domain.fee.FeeQueryCommandExtensionItem; +import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName; import google.registry.model.domain.fee.FeeQueryResponseExtensionItem; import google.registry.model.domain.fee.FeeTransformCommandExtension; import google.registry.model.domain.fee.FeeTransformResponseExtension; @@ -674,8 +675,6 @@ public class DomainFlowUtils { String feeClass = null; ImmutableList fees = ImmutableList.of(); switch (feeRequest.getCommandName()) { - // TODO(sarahbot@): Add check of valid EPP actions on token before passing the token to the - // fee request. case CREATE: // Don't return a create price for reserved names. if (isReserved(domainName, isSunrise) && !isAvailable) { @@ -1204,7 +1203,12 @@ public class DomainFlowUtils { * token found on the TLD's default token list will be returned. */ public static Optional checkForDefaultToken( - Registry registry, String domainName, String registrarId, DateTime now) throws EppException { + Registry registry, + String domainName, + CommandName commandName, + String registrarId, + DateTime now) + throws EppException { if (isNullOrEmpty(registry.getDefaultPromoTokens())) { return Optional.empty(); } @@ -1222,7 +1226,7 @@ public class DomainFlowUtils { for (Optional token : tokenList) { try { AllocationTokenFlowUtils.validateToken( - InternetDomainName.from(domainName), token.get(), registrarId, now); + InternetDomainName.from(domainName), token.get(), commandName, registrarId, now); } catch (AssociationProhibitsOperationException | StatusProhibitsOperationException e) { // Allocation token was not valid for this registration, continue to check the next token in // the list diff --git a/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java b/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java index 118f421fd..d135068ca 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java @@ -66,6 +66,7 @@ import google.registry.model.domain.GracePeriod; import google.registry.model.domain.Period; import google.registry.model.domain.fee.BaseFee.FeeType; import google.registry.model.domain.fee.Fee; +import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName; import google.registry.model.domain.fee.FeeRenewCommandExtension; import google.registry.model.domain.fee.FeeTransformResponseExtension; import google.registry.model.domain.metadata.MetadataExtension; @@ -172,13 +173,13 @@ public final class DomainRenewFlow implements TransactionalFlow { Renew command = (Renew) resourceCommand; // Loads the target resource if it exists Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now); - // TODO(sarahbot@): Add check for valid EPP actions on the token Optional allocationToken = allocationTokenFlowUtils.verifyAllocationTokenIfPresent( existingDomain, Registry.get(existingDomain.getTld()), registrarId, now, + CommandName.RENEW, eppInput.getSingleExtension(AllocationTokenExtension.class)); verifyRenewAllowed(authInfo, existingDomain, command, allocationToken); diff --git a/core/src/main/java/google/registry/flows/domain/DomainTransferApproveFlow.java b/core/src/main/java/google/registry/flows/domain/DomainTransferApproveFlow.java index 13ca12829..f9eece8e6 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainTransferApproveFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainTransferApproveFlow.java @@ -53,6 +53,7 @@ import google.registry.model.billing.BillingEvent.RenewalPriceBehavior; import google.registry.model.domain.Domain; import google.registry.model.domain.DomainHistory; import google.registry.model.domain.GracePeriod; +import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName; import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.domain.token.AllocationTokenExtension; @@ -135,6 +136,7 @@ public final class DomainTransferApproveFlow implements TransactionalFlow { Registry.get(existingDomain.getTld()), registrarId, now, + CommandName.TRANSFER, eppInput.getSingleExtension(AllocationTokenExtension.class)); verifyOptionalAuthInfo(authInfo, existingDomain); verifyHasPendingTransfer(existingDomain); diff --git a/core/src/main/java/google/registry/flows/domain/DomainTransferRequestFlow.java b/core/src/main/java/google/registry/flows/domain/DomainTransferRequestFlow.java index d6aa33066..8a4439d58 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainTransferRequestFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainTransferRequestFlow.java @@ -57,6 +57,7 @@ import google.registry.model.domain.Domain; import google.registry.model.domain.DomainCommand.Transfer; import google.registry.model.domain.DomainHistory; import google.registry.model.domain.Period; +import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName; import google.registry.model.domain.fee.FeeTransferCommandExtension; import google.registry.model.domain.fee.FeeTransformResponseExtension; import google.registry.model.domain.metadata.MetadataExtension; @@ -173,6 +174,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow { Registry.get(existingDomain.getTld()), gainingClientId, now, + CommandName.TRANSFER, eppInput.getSingleExtension(AllocationTokenExtension.class)); Optional superuserExtension = eppInput.getSingleExtension(DomainTransferRequestSuperuserExtension.class); diff --git a/core/src/main/java/google/registry/flows/domain/token/AllocationTokenFlowUtils.java b/core/src/main/java/google/registry/flows/domain/token/AllocationTokenFlowUtils.java index 9ac2e086b..b78bc7935 100644 --- a/core/src/main/java/google/registry/flows/domain/token/AllocationTokenFlowUtils.java +++ b/core/src/main/java/google/registry/flows/domain/token/AllocationTokenFlowUtils.java @@ -30,6 +30,7 @@ import google.registry.model.billing.BillingEvent.Recurring; import google.registry.model.billing.BillingEvent.RenewalPriceBehavior; import google.registry.model.domain.Domain; import google.registry.model.domain.DomainCommand; +import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName; import google.registry.model.domain.token.AllocationToken; import google.registry.model.domain.token.AllocationToken.TokenBehavior; import google.registry.model.domain.token.AllocationToken.TokenStatus; @@ -78,7 +79,7 @@ public class AllocationTokenFlowUtils { ImmutableMap.Builder resultsBuilder = new ImmutableMap.Builder<>(); for (InternetDomainName domainName : domainNames) { try { - validateToken(domainName, tokenEntity, registrarId, now); + validateToken(domainName, tokenEntity, CommandName.CREATE, registrarId, now); validDomainNames.add(domainName); } catch (EppException e) { resultsBuilder.put(domainName, e.getMessage()); @@ -109,11 +110,19 @@ public class AllocationTokenFlowUtils { * @throws EppException if the token is invalid in any way */ public static void validateToken( - InternetDomainName domainName, AllocationToken token, String registrarId, DateTime now) + InternetDomainName domainName, + AllocationToken token, + CommandName commandName, + String registrarId, + DateTime now) throws EppException { // Only tokens with default behavior require validation if (TokenBehavior.DEFAULT.equals(token.getTokenBehavior())) { + if (!token.getAllowedEppActions().isEmpty() + && !token.getAllowedEppActions().contains(commandName)) { + throw new AllocationTokenNotValidForCommandException(); + } if (!token.getAllowedRegistrarIds().isEmpty() && !token.getAllowedRegistrarIds().contains(registrarId)) { throw new AllocationTokenNotValidForRegistrarException(); @@ -173,7 +182,12 @@ public class AllocationTokenFlowUtils { return Optional.empty(); } AllocationToken tokenEntity = loadToken(extension.get().getAllocationToken()); - validateToken(InternetDomainName.from(command.getDomainName()), tokenEntity, registrarId, now); + validateToken( + InternetDomainName.from(command.getDomainName()), + tokenEntity, + CommandName.CREATE, + registrarId, + now); return Optional.of( tokenCustomLogic.validateToken(command, tokenEntity, registry, registrarId, now)); } @@ -184,6 +198,7 @@ public class AllocationTokenFlowUtils { Registry registry, String registrarId, DateTime now, + CommandName commandName, Optional extension) throws EppException { if (!extension.isPresent()) { @@ -191,7 +206,11 @@ public class AllocationTokenFlowUtils { } AllocationToken tokenEntity = loadToken(extension.get().getAllocationToken()); validateToken( - InternetDomainName.from(existingDomain.getDomainName()), tokenEntity, registrarId, now); + InternetDomainName.from(existingDomain.getDomainName()), + tokenEntity, + commandName, + registrarId, + now); return Optional.of( tokenCustomLogic.validateToken(existingDomain, tokenEntity, registry, registrarId, now)); } @@ -280,6 +299,14 @@ public class AllocationTokenFlowUtils { } } + /** The allocation token is not valid for this EPP command. */ + public static class AllocationTokenNotValidForCommandException + extends AssociationProhibitsOperationException { + AllocationTokenNotValidForCommandException() { + super("Allocation token not valid for the EPP command"); + } + } + /** The allocation token is invalid. */ public static class InvalidAllocationTokenException extends AuthorizationErrorException { InvalidAllocationTokenException() { diff --git a/core/src/test/java/google/registry/flows/domain/DomainCheckFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainCheckFlowTest.java index 30f304b36..2fcf1e3ea 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainCheckFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainCheckFlowTest.java @@ -75,6 +75,7 @@ import google.registry.model.billing.BillingEvent.Flag; import google.registry.model.billing.BillingEvent.Reason; import google.registry.model.domain.Domain; import google.registry.model.domain.DomainHistory; +import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName; import google.registry.model.domain.token.AllocationToken; import google.registry.model.domain.token.AllocationToken.TokenStatus; import google.registry.model.eppcommon.StatusValue; @@ -792,6 +793,7 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase - - example2.example - USD - create - 1 - 0.00 - example1.tld USD @@ -35,12 +28,19 @@ 1 0.00 + + example2.example + USD + create + 1 + token-not-supported + reserved.tld USD create 1 - reserved + token-not-supported diff --git a/core/src/test/resources/google/registry/flows/domain/domain_check_allocationtoken_fee_specificuse_response.xml b/core/src/test/resources/google/registry/flows/domain/domain_check_allocationtoken_fee_specificuse_response.xml index d6910dbf2..e7afe5292 100644 --- a/core/src/test/resources/google/registry/flows/domain/domain_check_allocationtoken_fee_specificuse_response.xml +++ b/core/src/test/resources/google/registry/flows/domain/domain_check_allocationtoken_fee_specificuse_response.xml @@ -25,26 +25,26 @@ - - example2.example - USD - create - 1 - 13.00 - example1.tld USD create 1 - 13.00 + token-not-supported + + + example2.example + USD + create + 1 + token-not-supported reserved.tld USD create 1 - reserved + token-not-supported specificuse.tld diff --git a/core/src/test/resources/google/registry/flows/domain/domain_check_fee_multiple_commands_allocationtoken_response_v06.xml b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_multiple_commands_allocationtoken_response_v06.xml new file mode 100644 index 000000000..83b067ea3 --- /dev/null +++ b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_multiple_commands_allocationtoken_response_v06.xml @@ -0,0 +1,57 @@ + + + + Command completed successfully + + + + + example1.tld + + + + + + + example1.tld + USD + create + 1 + 13.00 + + + example1.tld + USD + renew + 1 + token-not-supported + + + example1.tld + USD + transfer + 1 + 11.00 + + + example1.tld + USD + restore + 1 + token-not-supported + + + example1.tld + USD + update + 1 + token-not-supported + + + + + ABC-12345 + server-trid + + + diff --git a/core/src/test/resources/google/registry/flows/domain/domain_check_fee_multiple_commands_allocationtoken_response_v12.xml b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_multiple_commands_allocationtoken_response_v12.xml new file mode 100644 index 000000000..35884fc4a --- /dev/null +++ b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_multiple_commands_allocationtoken_response_v12.xml @@ -0,0 +1,68 @@ + + + + Command completed successfully + + + + + example1.tld + + + + + + + + example1.tld + + + 1 + 13.00 + + + + + example1.tld + + + 1 + token-not-supported + + + + + example1.tld + + + 1 + 11.00 + + + + + example1.tld + + + 1 + token-not-supported + + + + + example1.tld + + + 1 + token-not-supported + + + + + + ABC-12345 + server-trid + + + diff --git a/core/src/test/resources/google/registry/flows/domain/domain_check_fee_multiple_commands_allocationtoken_v06.xml b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_multiple_commands_allocationtoken_v06.xml new file mode 100644 index 000000000..9e362c8f4 --- /dev/null +++ b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_multiple_commands_allocationtoken_v06.xml @@ -0,0 +1,39 @@ + + + + + example1.tld + + + + + abc123 + + + + example1.tld + create + + + example1.tld + renew + + + example1.tld + transfer + + + example1.tld + restore + + + example1.tld + update + + + + ABC-12345 + + \ No newline at end of file diff --git a/core/src/test/resources/google/registry/flows/domain/domain_check_fee_multiple_commands_allocationtoken_v12.xml b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_multiple_commands_allocationtoken_v12.xml new file mode 100644 index 000000000..8346e44ca --- /dev/null +++ b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_multiple_commands_allocationtoken_v12.xml @@ -0,0 +1,24 @@ + + + + + example1.tld + + + + + abc123 + + + + + + + + + + ABC-12345 + + diff --git a/core/src/test/resources/google/registry/flows/domain/domain_check_fee_multiple_commands_default_token_response_v06.xml b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_multiple_commands_default_token_response_v06.xml index 5ec9b04ae..caa97f5c3 100644 --- a/core/src/test/resources/google/registry/flows/domain/domain_check_fee_multiple_commands_default_token_response_v06.xml +++ b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_multiple_commands_default_token_response_v06.xml @@ -24,7 +24,7 @@ USD renew 1 - 5.50 + 11.00 example1.tld diff --git a/core/src/test/resources/google/registry/flows/domain/domain_check_fee_multiple_commands_default_token_response_v12.xml b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_multiple_commands_default_token_response_v12.xml index 26e3a8845..c2812ffe3 100644 --- a/core/src/test/resources/google/registry/flows/domain/domain_check_fee_multiple_commands_default_token_response_v12.xml +++ b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_multiple_commands_default_token_response_v12.xml @@ -28,7 +28,7 @@ 1 - 5.50 + 11.00