Add period restriction to domain flows

Specifically, Domain[Create,Allocate,ApplicationCreate]Flow

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=147458283
This commit is contained in:
ctingue 2017-02-14 04:43:04 -08:00 committed by Ben McIlwain
parent 335b3f0cf8
commit 53262d82bb
14 changed files with 171 additions and 21 deletions

View file

@ -529,9 +529,9 @@ comes in at the exact millisecond that the domain would have expired.
* Fees must be explicitly acknowledged when performing any operations on a
premium name.
* 2004
* New registration period exceeds maximum number of years.
* The fees passed in the transform command do not match the fees that will
be charged.
* New registration period exceeds maximum number of years.
* The current expiration date is incorrect.
* 2005
* The requested fee is expressed in a scale that is invalid for the given
@ -620,6 +620,7 @@ An EPP flow that creates a new domain resource.
* 2004
* The acceptance time specified in the claim notice is more than 48 hours
in the past.
* New registration period exceeds maximum number of years.
* The expiration time specified in the claim notice has elapsed.
* The fees passed in the transform command do not match the fees that will
be charged.
@ -830,6 +831,7 @@ An EPP flow that creates a new application for a domain resource.
* 2004
* The acceptance time specified in the claim notice is more than 48 hours
in the past.
* New registration period exceeds maximum number of years.
* The expiration time specified in the claim notice has elapsed.
* The fees passed in the transform command do not match the fees that will
be charged.
@ -907,6 +909,8 @@ An EPP flow that allocates a new domain resource from a domain application.
### Errors
* 2004
* New registration period exceeds maximum number of years.
* 2201
* Only a superuser can allocate domains.
* 2302

View file

@ -26,6 +26,7 @@ import static google.registry.flows.domain.DomainFlowUtils.prepareMarkedLrpToken
import static google.registry.flows.domain.DomainFlowUtils.validateCreateCommandContactsAndNameservers;
import static google.registry.flows.domain.DomainFlowUtils.validateDomainName;
import static google.registry.flows.domain.DomainFlowUtils.validateDomainNameWithIdnTables;
import static google.registry.flows.domain.DomainFlowUtils.validateRegistrationPeriod;
import static google.registry.flows.domain.DomainFlowUtils.validateSecDnsExtension;
import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears;
import static google.registry.model.EppResourceUtils.createDomainRepoId;
@ -94,6 +95,7 @@ import org.joda.time.DateTime;
* @error {@link DomainAllocateFlow.HasFinalStatusException}
* @error {@link DomainAllocateFlow.MissingApplicationException}
* @error {@link DomainAllocateFlow.OnlySuperuserCanAllocateException}
* @error {@link DomainFlowUtils.ExceedsMaxRegistrationYearsException}
*/
public class DomainAllocateFlow implements TransactionalFlow {
@ -134,6 +136,7 @@ public class DomainAllocateFlow implements TransactionalFlow {
Period period = command.getPeriod();
Integer years = period.getValue();
verifyUnitIsYears(period);
validateRegistrationPeriod(years);
validateCreateCommandContactsAndNameservers(command, registry.getTldStr());
SecDnsCreateExtension secDnsCreate =
validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class));

View file

@ -28,6 +28,7 @@ import static google.registry.flows.domain.DomainFlowUtils.validateDomainName;
import static google.registry.flows.domain.DomainFlowUtils.validateDomainNameWithIdnTables;
import static google.registry.flows.domain.DomainFlowUtils.validateFeeChallenge;
import static google.registry.flows.domain.DomainFlowUtils.validateLaunchCreateNotice;
import static google.registry.flows.domain.DomainFlowUtils.validateRegistrationPeriod;
import static google.registry.flows.domain.DomainFlowUtils.validateSecDnsExtension;
import static google.registry.flows.domain.DomainFlowUtils.verifyClaimsNoticeIfAndOnlyIfNeeded;
import static google.registry.flows.domain.DomainFlowUtils.verifyClaimsPeriodNotEnded;
@ -117,6 +118,7 @@ import org.joda.time.DateTime;
* @error {@link DomainFlowUtils.DomainReservedException}
* @error {@link DomainFlowUtils.DuplicateContactForRoleException}
* @error {@link DomainFlowUtils.EmptyDomainNamePartException}
* @error {@link DomainFlowUtils.ExceedsMaxRegistrationYearsException}
* @error {@link DomainFlowUtils.ExpiredClaimException}
* @error {@link DomainFlowUtils.FeesMismatchException}
* @error {@link DomainFlowUtils.FeesRequiredForPremiumNameException}
@ -203,6 +205,7 @@ public final class DomainApplicationCreateFlow implements TransactionalFlow {
// notice without specifying a claims key, and override blocks on registering premium domains.
verifyUnitIsYears(command.getPeriod());
int years = command.getPeriod().getValue();
validateRegistrationPeriod(years);
validateCreateCommandContactsAndNameservers(command, tld);
LaunchCreateExtension launchCreate = eppInput.getSingleExtension(LaunchCreateExtension.class);
if (launchCreate != null) {

View file

@ -27,6 +27,7 @@ import static google.registry.flows.domain.DomainFlowUtils.validateDomainName;
import static google.registry.flows.domain.DomainFlowUtils.validateDomainNameWithIdnTables;
import static google.registry.flows.domain.DomainFlowUtils.validateFeeChallenge;
import static google.registry.flows.domain.DomainFlowUtils.validateLaunchCreateNotice;
import static google.registry.flows.domain.DomainFlowUtils.validateRegistrationPeriod;
import static google.registry.flows.domain.DomainFlowUtils.validateSecDnsExtension;
import static google.registry.flows.domain.DomainFlowUtils.verifyClaimsNoticeIfAndOnlyIfNeeded;
import static google.registry.flows.domain.DomainFlowUtils.verifyClaimsPeriodNotEnded;
@ -116,6 +117,7 @@ import org.joda.time.DateTime;
* @error {@link DomainFlowUtils.DomainReservedException}
* @error {@link DomainFlowUtils.DuplicateContactForRoleException}
* @error {@link DomainFlowUtils.EmptyDomainNamePartException}
* @error {@link DomainFlowUtils.ExceedsMaxRegistrationYearsException}
* @error {@link DomainFlowUtils.ExpiredClaimException}
* @error {@link DomainFlowUtils.FeesMismatchException}
* @error {@link DomainFlowUtils.FeesRequiredForPremiumNameException}
@ -242,6 +244,7 @@ public class DomainCreateFlow implements TransactionalFlow {
validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class));
String repoId = createDomainRepoId(ObjectifyService.allocateId(), registry.getTldStr());
DateTime registrationExpirationTime = leapSafeAddYears(now, years);
validateRegistrationPeriod(years);
HistoryEntry historyEntry = buildHistory(repoId, period, now);
// Bill for the create.
BillingEvent.OneTime createBillingEvent =

View file

@ -22,6 +22,8 @@ import static com.google.common.collect.Sets.difference;
import static com.google.common.collect.Sets.union;
import static google.registry.flows.domain.DomainPricingLogic.getMatchingLrpToken;
import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.model.domain.DomainResource.MAX_REGISTRATION_YEARS;
import static google.registry.model.domain.DomainResource.extendRegistrationWithCap;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.registry.Registries.findTldForName;
import static google.registry.model.registry.label.ReservedList.getReservation;
@ -30,6 +32,7 @@ import static google.registry.tldconfig.idn.IdnLabelValidator.findValidIdnTableF
import static google.registry.util.CollectionUtils.nullToEmpty;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.isAtOrAfter;
import static google.registry.util.DateTimeUtils.leapSafeAddYears;
import static google.registry.util.DomainNameUtils.ACE_PREFIX;
import com.google.common.base.CharMatcher;
@ -262,7 +265,6 @@ public class DomainFlowUtils {
private static void verifyNotInPendingDelete(
Key<? extends EppResource> resourceKey) throws EppException {
EppResource resource = ofy().load().key(resourceKey).now();
if (resource.getStatusValues().contains(StatusValue.PENDING_DELETE)) {
throw new LinkedResourceInPendingDeleteProhibitsOperationException(resource.getForeignKey());
@ -621,6 +623,35 @@ public class DomainFlowUtils {
}
}
/**
* Check whether a new registration period (via a renew) does not extend beyond a maximum number
* of years (e.g. {@link DomainResource#MAX_REGISTRATION_YEARS}).
*
* @throws ExceedsMaxRegistrationYearsException if the new registration period is too long
*/
public static void validateRegistrationPeriod(
DateTime now,
DateTime oldExpirationTime,
int years) throws EppException {
DateTime newExpirationTime = leapSafeAddYears(oldExpirationTime, years); // uncapped
if (extendRegistrationWithCap(now, oldExpirationTime, years).isBefore(newExpirationTime)) {
throw new ExceedsMaxRegistrationYearsException();
}
}
/**
* Check whether a new registration period (via a create, allocate, or application create) does
* not extend beyond a maximum number of years (e.g.
* {@link DomainResource#MAX_REGISTRATION_YEARS}).
*
* @throws ExceedsMaxRegistrationYearsException if the new registration period is too long
*/
public static void validateRegistrationPeriod(int years) throws EppException {
if (years > MAX_REGISTRATION_YEARS) {
throw new ExceedsMaxRegistrationYearsException();
}
}
/**
* Adds a secDns extension to a list if the given set of dsData is non-empty.
*
@ -830,7 +861,7 @@ public class DomainFlowUtils {
}
}
/** Create a response extension listign the fees on a domain or application create. */
/** Create a response extension listing the fees on a domain or application create. */
static FeeTransformResponseExtension createFeeCreateResponse(
FeeTransformCommandExtension feeCreate, FeesAndCredits feesAndCredits) {
return feeCreate
@ -1260,4 +1291,13 @@ public class DomainFlowUtils {
super("Only encoded signed marks are supported");
}
}
/** New registration period exceeds maximum number of years. */
static class ExceedsMaxRegistrationYearsException extends ParameterValueRangeErrorException {
public ExceedsMaxRegistrationYearsException() {
super(String.format(
"New registration period exceeds maximum number of years (%d)",
MAX_REGISTRATION_YEARS));
}
}
}

View file

@ -25,9 +25,8 @@ import static google.registry.flows.domain.DomainFlowUtils.newAutorenewBillingEv
import static google.registry.flows.domain.DomainFlowUtils.newAutorenewPollMessage;
import static google.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime;
import static google.registry.flows.domain.DomainFlowUtils.validateFeeChallenge;
import static google.registry.flows.domain.DomainFlowUtils.validateRegistrationPeriod;
import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears;
import static google.registry.model.domain.DomainResource.MAX_REGISTRATION_YEARS;
import static google.registry.model.domain.DomainResource.extendRegistrationWithCap;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.util.DateTimeUtils.leapSafeAddYears;
@ -95,12 +94,12 @@ import org.joda.time.DateTime;
* @error {@link DomainFlowUtils.BadPeriodUnitException}
* @error {@link DomainFlowUtils.CurrencyUnitMismatchException}
* @error {@link DomainFlowUtils.CurrencyValueScaleException}
* @error {@link DomainFlowUtils.ExceedsMaxRegistrationYearsException}
* @error {@link DomainFlowUtils.FeesMismatchException}
* @error {@link DomainFlowUtils.FeesRequiredForPremiumNameException}
* @error {@link DomainFlowUtils.NotAuthorizedForTldException}
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
* @error {@link DomainRenewFlow.DomainHasPendingTransferException}
* @error {@link DomainRenewFlow.ExceedsMaxRegistrationYearsException}
* @error {@link DomainRenewFlow.IncorrectCurrentExpirationDateException}
*/
public final class DomainRenewFlow implements TransactionalFlow {
@ -154,9 +153,7 @@ public final class DomainRenewFlow implements TransactionalFlow {
.build();
DateTime oldExpirationTime = existingDomain.getRegistrationExpirationTime();
DateTime newExpirationTime = leapSafeAddYears(oldExpirationTime, years); // Uncapped
if (extendRegistrationWithCap(now, oldExpirationTime, years).isBefore(newExpirationTime)) {
throw new ExceedsMaxRegistrationYearsException();
}
validateRegistrationPeriod(now, oldExpirationTime, years);
String tld = existingDomain.getTld();
// Bill for this explicit renew itself.
BillingEvent.OneTime explicitRenewEvent =
@ -271,13 +268,4 @@ public final class DomainRenewFlow implements TransactionalFlow {
super("The current expiration date is incorrect");
}
}
/** New registration period exceeds maximum number of years. */
static class ExceedsMaxRegistrationYearsException extends ParameterValueRangeErrorException {
public ExceedsMaxRegistrationYearsException() {
super(String.format(
"Registrations cannot extend for more than %d years into the future",
MAX_REGISTRATION_YEARS));
}
}
}

View file

@ -44,6 +44,7 @@ import google.registry.model.transfer.TransferData;
import google.registry.model.transfer.TransferStatus;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
import org.joda.time.Interval;
@ -334,11 +335,14 @@ public class DomainResource extends DomainBase
/** Return what the expiration time would be if the given number of years were added to it. */
public static DateTime extendRegistrationWithCap(
DateTime now, DateTime currentExpirationTime, Integer extendedRegistrationYears) {
DateTime now,
DateTime currentExpirationTime,
@Nullable Integer extendedRegistrationYears) {
// We must cap registration at the max years (aka 10), even if that truncates the last year.
return earliestOf(
leapSafeAddYears(
currentExpirationTime, Optional.fromNullable(extendedRegistrationYears).or(0)),
currentExpirationTime,
Optional.fromNullable(extendedRegistrationYears).or(0)),
leapSafeAddYears(now, MAX_REGISTRATION_YEARS));
}

View file

@ -45,6 +45,7 @@ import google.registry.flows.ResourceFlowTestCase;
import google.registry.flows.domain.DomainAllocateFlow.HasFinalStatusException;
import google.registry.flows.domain.DomainAllocateFlow.MissingApplicationException;
import google.registry.flows.domain.DomainAllocateFlow.OnlySuperuserCanAllocateException;
import google.registry.flows.domain.DomainFlowUtils.ExceedsMaxRegistrationYearsException;
import google.registry.flows.exceptions.ResourceAlreadyExistsException;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Flag;
@ -484,4 +485,12 @@ public class DomainAllocateFlowTest
thrown.expect(OnlySuperuserCanAllocateException.class);
runFlow(CommitMode.LIVE, UserPrivileges.NORMAL);
}
@Test
public void testFailure_max10Years() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
setEppInput("domain_allocate_11_years.xml");
thrown.expect(ExceedsMaxRegistrationYearsException.class);
runFlowAsSuperuser();
}
}

View file

@ -74,6 +74,7 @@ import google.registry.flows.domain.DomainFlowUtils.DomainLabelTooLongException;
import google.registry.flows.domain.DomainFlowUtils.DomainReservedException;
import google.registry.flows.domain.DomainFlowUtils.DuplicateContactForRoleException;
import google.registry.flows.domain.DomainFlowUtils.EmptyDomainNamePartException;
import google.registry.flows.domain.DomainFlowUtils.ExceedsMaxRegistrationYearsException;
import google.registry.flows.domain.DomainFlowUtils.ExpiredClaimException;
import google.registry.flows.domain.DomainFlowUtils.FeesMismatchException;
import google.registry.flows.domain.DomainFlowUtils.FeesRequiredForPremiumNameException;
@ -1575,6 +1576,16 @@ public class DomainApplicationCreateFlowTest
runFlow();
}
@Test
public void testFailure_max10Years() throws Exception {
createTld("tld", TldState.LANDRUSH);
setEppInput("domain_create_landrush_11_years.xml");
persistContactsAndHosts();
clock.advanceOneMilli();
thrown.expect(ExceedsMaxRegistrationYearsException.class);
runFlow();
}
/**
* There is special logic that disallows a failfast for domains in add grace period and sunrush
* add grace period, so make sure that they fail anyways in the actual flow.

View file

@ -70,6 +70,7 @@ import google.registry.flows.domain.DomainFlowUtils.DomainLabelTooLongException;
import google.registry.flows.domain.DomainFlowUtils.DomainReservedException;
import google.registry.flows.domain.DomainFlowUtils.DuplicateContactForRoleException;
import google.registry.flows.domain.DomainFlowUtils.EmptyDomainNamePartException;
import google.registry.flows.domain.DomainFlowUtils.ExceedsMaxRegistrationYearsException;
import google.registry.flows.domain.DomainFlowUtils.ExpiredClaimException;
import google.registry.flows.domain.DomainFlowUtils.FeesMismatchException;
import google.registry.flows.domain.DomainFlowUtils.FeesRequiredForPremiumNameException;
@ -1792,4 +1793,12 @@ public class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow,
.build());
doSuccessfulTest("tld", "domain_create_response.xml");
}
@Test
public void testFailure_max10Years() throws Exception {
setEppInput("domain_create_11_years.xml");
persistContactsAndHosts();
thrown.expect(ExceedsMaxRegistrationYearsException.class);
runFlow();
}
}

View file

@ -13,6 +13,7 @@
// limitations under the License.
package google.registry.flows.domain;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.flows.domain.DomainTransferFlowTestCase.persistWithPendingTransfer;
import static google.registry.model.ofy.ObjectifyService.ofy;
@ -40,12 +41,12 @@ import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import google.registry.flows.domain.DomainFlowUtils.BadPeriodUnitException;
import google.registry.flows.domain.DomainFlowUtils.CurrencyUnitMismatchException;
import google.registry.flows.domain.DomainFlowUtils.CurrencyValueScaleException;
import google.registry.flows.domain.DomainFlowUtils.ExceedsMaxRegistrationYearsException;
import google.registry.flows.domain.DomainFlowUtils.FeesMismatchException;
import google.registry.flows.domain.DomainFlowUtils.FeesRequiredForPremiumNameException;
import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
import google.registry.flows.domain.DomainFlowUtils.UnsupportedFeeAttributeException;
import google.registry.flows.domain.DomainRenewFlow.DomainHasPendingTransferException;
import google.registry.flows.domain.DomainRenewFlow.ExceedsMaxRegistrationYearsException;
import google.registry.flows.domain.DomainRenewFlow.IncorrectCurrentExpirationDateException;
import google.registry.flows.exceptions.ResourceStatusProhibitsOperationException;
import google.registry.model.billing.BillingEvent;

View file

@ -0,0 +1,24 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<domain:create
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example-one.tld</domain:name>
<domain:period unit="y">11</domain:period>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<extension>
<allocate:create xmlns:allocate="urn:google:params:xml:ns:allocate-1.0">
<allocate:applicationRoid>2-TLD</allocate:applicationRoid>
<allocate:applicationTime>2010-08-16T10:00:00.0Z</allocate:applicationTime>
</allocate:create>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<domain:create
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example.tld</domain:name>
<domain:period unit="y">11</domain:period>
<domain:ns>
<domain:hostObj>ns1.example.net</domain:hostObj>
<domain:hostObj>ns2.example.net</domain:hostObj>
</domain:ns>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<domain:create
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>test-validate.tld</domain:name>
<domain:period unit="y">11</domain:period>
<domain:ns>
<domain:hostObj>ns1.example.net</domain:hostObj>
<domain:hostObj>ns2.example.net</domain:hostObj>
</domain:ns>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<extension>
<launch:create
xmlns:launch="urn:ietf:params:xml:ns:launch-1.0">
<launch:phase>landrush</launch:phase>
</launch:create>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>