mirror of
https://github.com/google/nomulus.git
synced 2025-04-29 19:47:51 +02:00
Prohibit renewals of package domains unless REMOVEPACKAGE token is included (#1758)
This commit is contained in:
parent
b75ee28058
commit
86db66c01f
6 changed files with 198 additions and 31 deletions
|
@ -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.verifyRegistrarIsActive;
|
||||
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.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.leapSafeAddYears;
|
||||
|
@ -119,6 +120,10 @@ import org.joda.time.Duration;
|
|||
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
|
||||
* @error {@link DomainRenewFlow.IncorrectCurrentExpirationDateException}
|
||||
* @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}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException}
|
||||
|
@ -174,7 +179,7 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
|||
registrarId,
|
||||
now,
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||
verifyRenewAllowed(authInfo, existingDomain, command);
|
||||
verifyRenewAllowed(authInfo, existingDomain, command, allocationToken);
|
||||
int years = command.getPeriod().getValue();
|
||||
DateTime newExpirationTime =
|
||||
leapSafeAddYears(existingDomain.getRegistrationExpirationTime(), years); // Uncapped
|
||||
|
@ -302,10 +307,16 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
|||
.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 {
|
||||
verifyOptionalAuthInfo(authInfo, existingDomain);
|
||||
verifyNoDisallowedStatuses(existingDomain, RENEW_DISALLOWED_STATUSES);
|
||||
// We only allow __REMOVE_PACKAGE__ token on promo package domains for now
|
||||
verifyTokenAllowedOnDomain(existingDomain, allocationToken);
|
||||
if (!isSuperuser) {
|
||||
verifyResourceOwnership(registrarId, existingDomain);
|
||||
checkAllowedAccessToTld(registrarId, existingDomain.getTld());
|
||||
|
|
|
@ -29,6 +29,7 @@ import google.registry.flows.EppException.StatusProhibitsOperationException;
|
|||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainCommand;
|
||||
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.TokenType;
|
||||
import google.registry.model.domain.token.AllocationTokenExtension;
|
||||
|
@ -109,23 +110,27 @@ public class AllocationTokenFlowUtils {
|
|||
private void validateToken(
|
||||
InternetDomainName domainName, AllocationToken token, String registrarId, DateTime now)
|
||||
throws EppException {
|
||||
if (!token.getAllowedRegistrarIds().isEmpty()
|
||||
&& !token.getAllowedRegistrarIds().contains(registrarId)) {
|
||||
throw new AllocationTokenNotValidForRegistrarException();
|
||||
}
|
||||
if (!token.getAllowedTlds().isEmpty()
|
||||
&& !token.getAllowedTlds().contains(domainName.parent().toString())) {
|
||||
throw new AllocationTokenNotValidForTldException();
|
||||
}
|
||||
if (token.getDomainName().isPresent()
|
||||
&& !token.getDomainName().get().equals(domainName.toString())) {
|
||||
throw new AllocationTokenNotValidForDomainException();
|
||||
}
|
||||
// Tokens without status transitions will just have a single-entry NOT_STARTED map, so only
|
||||
// check the status transitions map if it's non-trivial.
|
||||
if (token.getTokenStatusTransitions().size() > 1
|
||||
&& !TokenStatus.VALID.equals(token.getTokenStatusTransitions().getValueAtTime(now))) {
|
||||
throw new AllocationTokenNotInPromotionException();
|
||||
|
||||
// Only tokens with default behavior require validation
|
||||
if (TokenBehavior.DEFAULT.equals(token.getTokenBehavior())) {
|
||||
if (!token.getAllowedRegistrarIds().isEmpty()
|
||||
&& !token.getAllowedRegistrarIds().contains(registrarId)) {
|
||||
throw new AllocationTokenNotValidForRegistrarException();
|
||||
}
|
||||
if (!token.getAllowedTlds().isEmpty()
|
||||
&& !token.getAllowedTlds().contains(domainName.parent().toString())) {
|
||||
throw new AllocationTokenNotValidForTldException();
|
||||
}
|
||||
if (token.getDomainName().isPresent()
|
||||
&& !token.getDomainName().get().equals(domainName.toString())) {
|
||||
throw new AllocationTokenNotValidForDomainException();
|
||||
}
|
||||
// Tokens without status transitions will just have a single-entry NOT_STARTED map, so only
|
||||
// check the status transitions map if it's non-trivial.
|
||||
if (token.getTokenStatusTransitions().size() > 1
|
||||
&& !TokenStatus.VALID.equals(token.getTokenStatusTransitions().getValueAtTime(now))) {
|
||||
throw new AllocationTokenNotInPromotionException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,8 +142,15 @@ public class AllocationTokenFlowUtils {
|
|||
// See https://tools.ietf.org/html/draft-ietf-regext-allocation-token-04#section-2.1
|
||||
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)));
|
||||
|
||||
if (!maybeTokenEntity.isPresent()) {
|
||||
throw new InvalidAllocationTokenException();
|
||||
}
|
||||
|
@ -187,6 +199,21 @@ public class AllocationTokenFlowUtils {
|
|||
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
|
||||
|
||||
/** The allocation token is not currently valid. */
|
||||
|
@ -234,4 +261,20 @@ public class AllocationTokenFlowUtils {
|
|||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
|||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
|
@ -79,6 +80,10 @@ import org.joda.time.DateTime;
|
|||
public class AllocationToken extends BackupGroupRoot implements Buildable {
|
||||
|
||||
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.
|
||||
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)
|
||||
.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. */
|
||||
public enum RegistrationBehavior {
|
||||
/** No special behavior */
|
||||
|
@ -110,7 +127,21 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
|
|||
public enum TokenType {
|
||||
PACKAGE,
|
||||
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. */
|
||||
|
@ -255,6 +286,10 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
|
|||
return registrationBehavior;
|
||||
}
|
||||
|
||||
public TokenBehavior getTokenBehavior() {
|
||||
return STATIC_TOKEN_BEHAVIORS.getOrDefault(token, TokenBehavior.DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VKey<AllocationToken> createVKey() {
|
||||
return VKey.create(AllocationToken.class, getToken(), Key.create(this));
|
||||
|
|
|
@ -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.NONPREMIUM;
|
||||
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.UNLIMITED_USE;
|
||||
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.AlreadyRedeemedAllocationTokenException;
|
||||
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.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Flag;
|
||||
|
@ -591,7 +594,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
|||
@Test
|
||||
void testSuccess_allocationToken() throws Exception {
|
||||
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();
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
|
@ -611,7 +615,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
|||
@Test
|
||||
void testSuccess_allocationTokenMultiUse() throws Exception {
|
||||
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();
|
||||
persistResource(
|
||||
new AllocationToken.Builder().setToken("abc123").setTokenType(UNLIMITED_USE).build());
|
||||
|
@ -622,7 +627,7 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
|||
clock.advanceOneMilli();
|
||||
setEppInput(
|
||||
"domain_renew_allocationtoken.xml",
|
||||
ImmutableMap.of("DOMAIN", "other-example.tld", "YEARS", "2"));
|
||||
ImmutableMap.of("DOMAIN", "other-example.tld", "YEARS", "2", "TOKEN", "abc123"));
|
||||
persistDomain();
|
||||
runFlowAssertResponse(
|
||||
loadFile(
|
||||
|
@ -633,7 +638,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
|||
@Test
|
||||
void testFailure_invalidAllocationToken() throws Exception {
|
||||
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();
|
||||
EppException thrown = assertThrows(InvalidAllocationTokenException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
|
@ -642,7 +648,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
|||
@Test
|
||||
void testFailure_allocationTokenIsForADifferentDomain() throws Exception {
|
||||
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();
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
|
@ -660,7 +667,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
|||
@Test
|
||||
void testFailure_promotionNotActive() throws Exception {
|
||||
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();
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
|
@ -682,7 +690,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
|||
@Test
|
||||
void testFailure_promoTokenNotValidForRegistrar() throws Exception {
|
||||
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();
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
|
@ -706,7 +715,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
|||
@Test
|
||||
void testFailure_promoTokenNotValidForTld() throws Exception {
|
||||
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();
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
|
@ -730,7 +740,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
|||
@Test
|
||||
void testFailure_alreadyRedemeedAllocationToken() throws Exception {
|
||||
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();
|
||||
Domain domain = persistActiveDomain("foo.tld");
|
||||
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 505L);
|
||||
|
@ -1185,4 +1196,69 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
|||
TransactionReportField.netRenewsFieldFromYears(5),
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<allocationToken:allocationToken
|
||||
xmlns:allocationToken=
|
||||
"urn:ietf:params:xml:ns:allocationToken-1.0">
|
||||
abc123
|
||||
%TOKEN%
|
||||
</allocationToken:allocationToken>
|
||||
</extension>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
|
|
|
@ -497,6 +497,8 @@ comes in at the exact millisecond that the domain would have expired.
|
|||
* Resource status prohibits this operation.
|
||||
* The allocation token is not currently valid.
|
||||
* 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 registrar.
|
||||
* The allocation token is not valid for this TLD.
|
||||
|
|
Loading…
Add table
Reference in a new issue