Prohibit renewals of package domains unless REMOVEPACKAGE token is included (#1758)

This commit is contained in:
Pavlo Tkach 2022-08-31 18:58:31 -04:00 committed by GitHub
parent b75ee28058
commit 86db66c01f
6 changed files with 198 additions and 31 deletions

View file

@ -30,6 +30,7 @@ import static google.registry.flows.domain.DomainFlowUtils.validateFeeChallenge;
import static google.registry.flows.domain.DomainFlowUtils.validateRegistrationPeriod; import static google.registry.flows.domain.DomainFlowUtils.validateRegistrationPeriod;
import static google.registry.flows.domain.DomainFlowUtils.verifyRegistrarIsActive; import static google.registry.flows.domain.DomainFlowUtils.verifyRegistrarIsActive;
import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears; import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears;
import static google.registry.flows.domain.token.AllocationTokenFlowUtils.verifyTokenAllowedOnDomain;
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_RENEW; import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_RENEW;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.leapSafeAddYears; import static google.registry.util.DateTimeUtils.leapSafeAddYears;
@ -119,6 +120,10 @@ import org.joda.time.Duration;
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException} * @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
* @error {@link DomainRenewFlow.IncorrectCurrentExpirationDateException} * @error {@link DomainRenewFlow.IncorrectCurrentExpirationDateException}
* @error {@link * @error {@link
* google.registry.flows.domain.token.AllocationTokenFlowUtils.MissingRemovePackageTokenOnPackageDomainException}
* @error {@link
* google.registry.flows.domain.token.AllocationTokenFlowUtils.RemovePackageTokenOnNonPackageDomainException}
* @error {@link
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException} * google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException}
* @error {@link * @error {@link
* google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException} * google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException}
@ -174,7 +179,7 @@ public final class DomainRenewFlow implements TransactionalFlow {
registrarId, registrarId,
now, now,
eppInput.getSingleExtension(AllocationTokenExtension.class)); eppInput.getSingleExtension(AllocationTokenExtension.class));
verifyRenewAllowed(authInfo, existingDomain, command); verifyRenewAllowed(authInfo, existingDomain, command, allocationToken);
int years = command.getPeriod().getValue(); int years = command.getPeriod().getValue();
DateTime newExpirationTime = DateTime newExpirationTime =
leapSafeAddYears(existingDomain.getRegistrationExpirationTime(), years); // Uncapped leapSafeAddYears(existingDomain.getRegistrationExpirationTime(), years); // Uncapped
@ -302,10 +307,16 @@ public final class DomainRenewFlow implements TransactionalFlow {
.build(); .build();
} }
private void verifyRenewAllowed(Optional<AuthInfo> authInfo, Domain existingDomain, Renew command) private void verifyRenewAllowed(
Optional<AuthInfo> authInfo,
Domain existingDomain,
Renew command,
Optional<AllocationToken> allocationToken)
throws EppException { throws EppException {
verifyOptionalAuthInfo(authInfo, existingDomain); verifyOptionalAuthInfo(authInfo, existingDomain);
verifyNoDisallowedStatuses(existingDomain, RENEW_DISALLOWED_STATUSES); verifyNoDisallowedStatuses(existingDomain, RENEW_DISALLOWED_STATUSES);
// We only allow __REMOVE_PACKAGE__ token on promo package domains for now
verifyTokenAllowedOnDomain(existingDomain, allocationToken);
if (!isSuperuser) { if (!isSuperuser) {
verifyResourceOwnership(registrarId, existingDomain); verifyResourceOwnership(registrarId, existingDomain);
checkAllowedAccessToTld(registrarId, existingDomain.getTld()); checkAllowedAccessToTld(registrarId, existingDomain.getTld());

View file

@ -29,6 +29,7 @@ import google.registry.flows.EppException.StatusProhibitsOperationException;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
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.TokenBehavior;
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.domain.token.AllocationTokenExtension;
@ -109,6 +110,9 @@ public class AllocationTokenFlowUtils {
private void validateToken( private void validateToken(
InternetDomainName domainName, AllocationToken token, String registrarId, DateTime now) InternetDomainName domainName, AllocationToken token, String registrarId, DateTime now)
throws EppException { throws EppException {
// Only tokens with default behavior require validation
if (TokenBehavior.DEFAULT.equals(token.getTokenBehavior())) {
if (!token.getAllowedRegistrarIds().isEmpty() if (!token.getAllowedRegistrarIds().isEmpty()
&& !token.getAllowedRegistrarIds().contains(registrarId)) { && !token.getAllowedRegistrarIds().contains(registrarId)) {
throw new AllocationTokenNotValidForRegistrarException(); throw new AllocationTokenNotValidForRegistrarException();
@ -128,6 +132,7 @@ public class AllocationTokenFlowUtils {
throw new AllocationTokenNotInPromotionException(); throw new AllocationTokenNotInPromotionException();
} }
} }
}
/** Loads a given token and validates that it is not redeemed */ /** Loads a given token and validates that it is not redeemed */
private AllocationToken loadToken(String token) throws EppException { private AllocationToken loadToken(String token) throws EppException {
@ -137,8 +142,15 @@ public class AllocationTokenFlowUtils {
// See https://tools.ietf.org/html/draft-ietf-regext-allocation-token-04#section-2.1 // See https://tools.ietf.org/html/draft-ietf-regext-allocation-token-04#section-2.1
throw new InvalidAllocationTokenException(); throw new InvalidAllocationTokenException();
} }
Optional<AllocationToken> maybeTokenEntity =
Optional<AllocationToken> maybeTokenEntity = AllocationToken.maybeGetStaticTokenInstance(token);
if (maybeTokenEntity.isPresent()) {
return maybeTokenEntity.get();
}
maybeTokenEntity =
tm().transact(() -> tm().loadByKeyIfPresent(VKey.create(AllocationToken.class, token))); tm().transact(() -> tm().loadByKeyIfPresent(VKey.create(AllocationToken.class, token)));
if (!maybeTokenEntity.isPresent()) { if (!maybeTokenEntity.isPresent()) {
throw new InvalidAllocationTokenException(); throw new InvalidAllocationTokenException();
} }
@ -187,6 +199,21 @@ public class AllocationTokenFlowUtils {
tokenCustomLogic.validateToken(existingDomain, tokenEntity, registry, registrarId, now)); tokenCustomLogic.validateToken(existingDomain, tokenEntity, registry, registrarId, now));
} }
public static void verifyTokenAllowedOnDomain(
Domain domain, Optional<AllocationToken> allocationToken) throws EppException {
boolean domainHasPackageToken = domain.getCurrentPackageToken().isPresent();
boolean hasRemovePackageToken =
allocationToken.isPresent()
&& TokenBehavior.REMOVE_PACKAGE.equals(allocationToken.get().getTokenBehavior());
if (hasRemovePackageToken && !domainHasPackageToken) {
throw new RemovePackageTokenOnNonPackageDomainException();
} else if (!hasRemovePackageToken && domainHasPackageToken) {
throw new MissingRemovePackageTokenOnPackageDomainException();
}
}
// 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. */
@ -234,4 +261,20 @@ public class AllocationTokenFlowUtils {
super("The allocation token is invalid"); super("The allocation token is invalid");
} }
} }
/** The __REMOVEPACKAGE__ token is missing on a renew package domain command */
public static class MissingRemovePackageTokenOnPackageDomainException
extends AssociationProhibitsOperationException {
MissingRemovePackageTokenOnPackageDomainException() {
super("Domains that are inside packages cannot be explicitly renewed");
}
}
/** The __REMOVEPACKAGE__ token is not allowed on non package domains */
public static class RemovePackageTokenOnNonPackageDomainException
extends AssociationProhibitsOperationException {
RemovePackageTokenOnNonPackageDomainException() {
super("__REMOVEPACKAGE__ token is not allowed on non package domains");
}
}
} }

View file

@ -26,6 +26,7 @@ import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedMap;
@ -79,6 +80,10 @@ import org.joda.time.DateTime;
public class AllocationToken extends BackupGroupRoot implements Buildable { public class AllocationToken extends BackupGroupRoot implements Buildable {
private static final long serialVersionUID = -3954475393220876903L; private static final long serialVersionUID = -3954475393220876903L;
private static final String REMOVE_PACKAGE = "__REMOVEPACKAGE__";
private static final ImmutableMap<String, TokenBehavior> STATIC_TOKEN_BEHAVIORS =
ImmutableMap.of(REMOVE_PACKAGE, TokenBehavior.REMOVE_PACKAGE);
// Promotions should only move forward, and ENDED / CANCELLED are terminal states. // Promotions should only move forward, and ENDED / CANCELLED are terminal states.
private static final ImmutableMultimap<TokenStatus, TokenStatus> VALID_TOKEN_STATUS_TRANSITIONS = private static final ImmutableMultimap<TokenStatus, TokenStatus> VALID_TOKEN_STATUS_TRANSITIONS =
@ -87,6 +92,18 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
.putAll(VALID, ENDED, CANCELLED) .putAll(VALID, ENDED, CANCELLED)
.build(); .build();
private static final ImmutableMap<String, AllocationToken> BEHAVIORAL_TOKENS =
ImmutableMap.of(
REMOVE_PACKAGE,
new AllocationToken.Builder()
.setTokenType(TokenType.UNLIMITED_USE)
.setToken(REMOVE_PACKAGE)
.build());
public static Optional<AllocationToken> maybeGetStaticTokenInstance(String name) {
return Optional.ofNullable(BEHAVIORAL_TOKENS.get(name));
}
/** Any special behavior that should be used when registering domains using this token. */ /** Any special behavior that should be used when registering domains using this token. */
public enum RegistrationBehavior { public enum RegistrationBehavior {
/** No special behavior */ /** No special behavior */
@ -110,7 +127,21 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
public enum TokenType { public enum TokenType {
PACKAGE, PACKAGE,
SINGLE_USE, SINGLE_USE,
UNLIMITED_USE UNLIMITED_USE,
}
/**
* System behaves differently based on a token it gets inside a command. This enumerates different
* types of behaviors we support.
*/
public enum TokenBehavior {
/** No special behavior */
DEFAULT,
/**
* REMOVE_PACKAGE triggers domain removal from promotional package, bypasses DEFAULT token
* validations.
*/
REMOVE_PACKAGE
} }
/** The status of this token with regard to any potential promotion. */ /** The status of this token with regard to any potential promotion. */
@ -255,6 +286,10 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
return registrationBehavior; return registrationBehavior;
} }
public TokenBehavior getTokenBehavior() {
return STATIC_TOKEN_BEHAVIORS.getOrDefault(token, TokenBehavior.DEFAULT);
}
@Override @Override
public VKey<AllocationToken> createVKey() { public VKey<AllocationToken> createVKey() {
return VKey.create(AllocationToken.class, getToken(), Key.create(this)); return VKey.create(AllocationToken.class, getToken(), Key.create(this));

View file

@ -20,6 +20,7 @@ import static google.registry.flows.domain.DomainTransferFlowTestCase.persistWit
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.PACKAGE;
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.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
@ -73,6 +74,8 @@ import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTok
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException; import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException;
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException; import google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException;
import google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException; import google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException;
import google.registry.flows.domain.token.AllocationTokenFlowUtils.MissingRemovePackageTokenOnPackageDomainException;
import google.registry.flows.domain.token.AllocationTokenFlowUtils.RemovePackageTokenOnNonPackageDomainException;
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;
@ -591,7 +594,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
@Test @Test
void testSuccess_allocationToken() throws Exception { void testSuccess_allocationToken() throws Exception {
setEppInput( setEppInput(
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2")); "domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain(); persistDomain();
AllocationToken allocationToken = AllocationToken allocationToken =
persistResource( persistResource(
@ -611,7 +615,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
@Test @Test
void testSuccess_allocationTokenMultiUse() throws Exception { void testSuccess_allocationTokenMultiUse() throws Exception {
setEppInput( setEppInput(
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2")); "domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain(); persistDomain();
persistResource( persistResource(
new AllocationToken.Builder().setToken("abc123").setTokenType(UNLIMITED_USE).build()); new AllocationToken.Builder().setToken("abc123").setTokenType(UNLIMITED_USE).build());
@ -622,7 +627,7 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
clock.advanceOneMilli(); clock.advanceOneMilli();
setEppInput( setEppInput(
"domain_renew_allocationtoken.xml", "domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "other-example.tld", "YEARS", "2")); ImmutableMap.of("DOMAIN", "other-example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain(); persistDomain();
runFlowAssertResponse( runFlowAssertResponse(
loadFile( loadFile(
@ -633,7 +638,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
@Test @Test
void testFailure_invalidAllocationToken() throws Exception { void testFailure_invalidAllocationToken() throws Exception {
setEppInput( setEppInput(
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2")); "domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain(); persistDomain();
EppException thrown = assertThrows(InvalidAllocationTokenException.class, this::runFlow); EppException thrown = assertThrows(InvalidAllocationTokenException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml(); assertAboutEppExceptions().that(thrown).marshalsToXml();
@ -642,7 +648,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
@Test @Test
void testFailure_allocationTokenIsForADifferentDomain() throws Exception { void testFailure_allocationTokenIsForADifferentDomain() throws Exception {
setEppInput( setEppInput(
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2")); "domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain(); persistDomain();
persistResource( persistResource(
new AllocationToken.Builder() new AllocationToken.Builder()
@ -660,7 +667,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
@Test @Test
void testFailure_promotionNotActive() throws Exception { void testFailure_promotionNotActive() throws Exception {
setEppInput( setEppInput(
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2")); "domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain(); persistDomain();
persistResource( persistResource(
new AllocationToken.Builder() new AllocationToken.Builder()
@ -682,7 +690,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
@Test @Test
void testFailure_promoTokenNotValidForRegistrar() throws Exception { void testFailure_promoTokenNotValidForRegistrar() throws Exception {
setEppInput( setEppInput(
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2")); "domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain(); persistDomain();
persistResource( persistResource(
new AllocationToken.Builder() new AllocationToken.Builder()
@ -706,7 +715,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
@Test @Test
void testFailure_promoTokenNotValidForTld() throws Exception { void testFailure_promoTokenNotValidForTld() throws Exception {
setEppInput( setEppInput(
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2")); "domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain(); persistDomain();
persistResource( persistResource(
new AllocationToken.Builder() new AllocationToken.Builder()
@ -730,7 +740,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
@Test @Test
void testFailure_alreadyRedemeedAllocationToken() throws Exception { void testFailure_alreadyRedemeedAllocationToken() throws Exception {
setEppInput( setEppInput(
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2")); "domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain(); persistDomain();
Domain domain = persistActiveDomain("foo.tld"); Domain domain = persistActiveDomain("foo.tld");
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 505L); Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 505L);
@ -1185,4 +1196,69 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
TransactionReportField.netRenewsFieldFromYears(5), TransactionReportField.netRenewsFieldFromYears(5),
1)); 1));
} }
@Test
void testFailsPackageDomainInvalidAllocationToken() throws Exception {
AllocationToken token =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(PACKAGE)
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
.setAllowedTlds(ImmutableSet.of("tld"))
.setRenewalPriceBehavior(SPECIFIED)
.build());
persistDomain();
persistResource(
reloadResourceByForeignKey()
.asBuilder()
.setCurrentPackageToken(token.createVKey())
.build());
setEppInput(
"domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
EppException thrown =
assertThrows(MissingRemovePackageTokenOnPackageDomainException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailsToRenewPackageDomainNoRemovePackageToken() throws Exception {
AllocationToken token =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(PACKAGE)
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
.setAllowedTlds(ImmutableSet.of("tld"))
.setRenewalPriceBehavior(SPECIFIED)
.build());
persistDomain();
persistResource(
reloadResourceByForeignKey()
.asBuilder()
.setCurrentPackageToken(token.createVKey())
.build());
setEppInput("domain_renew.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "5"));
EppException thrown =
assertThrows(MissingRemovePackageTokenOnPackageDomainException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailsToRenewNonPackageDomainWithRemovePackageToken() throws Exception {
persistDomain();
setEppInput(
"domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "__REMOVEPACKAGE__"));
EppException thrown =
assertThrows(RemovePackageTokenOnNonPackageDomainException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
} }

View file

@ -12,7 +12,7 @@
<allocationToken:allocationToken <allocationToken:allocationToken
xmlns:allocationToken= xmlns:allocationToken=
"urn:ietf:params:xml:ns:allocationToken-1.0"> "urn:ietf:params:xml:ns:allocationToken-1.0">
abc123 %TOKEN%
</allocationToken:allocationToken> </allocationToken:allocationToken>
</extension> </extension>
<clTRID>ABC-12345</clTRID> <clTRID>ABC-12345</clTRID>

View file

@ -497,6 +497,8 @@ comes in at the exact millisecond that the domain would have expired.
* Resource status prohibits this operation. * Resource status prohibits this operation.
* The allocation token is not currently valid. * The allocation token is not currently valid.
* 2305 * 2305
* The __REMOVEPACKAGE__ token is missing on a renew package domain command
* The __REMOVEPACKAGE__ token is not allowed on non package domains
* The allocation token is not valid for this domain. * 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 registrar.
* The allocation token is not valid for this TLD. * The allocation token is not valid for this TLD.