// 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; import static google.registry.flows.FlowUtils.persistEntityChanges; import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist; import static google.registry.flows.domain.DomainFlowUtils.COLLISION_MESSAGE; import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld; import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences; import static google.registry.flows.domain.DomainFlowUtils.createFeeCreateResponse; import static google.registry.flows.domain.DomainFlowUtils.getReservationTypes; import static google.registry.flows.domain.DomainFlowUtils.isAnchorTenant; import static google.registry.flows.domain.DomainFlowUtils.isReserved; import static google.registry.flows.domain.DomainFlowUtils.isValidReservedCreate; import static google.registry.flows.domain.DomainFlowUtils.validateCreateCommandContactsAndNameservers; import static google.registry.flows.domain.DomainFlowUtils.validateDomainAllowedOnCreateRestrictedTld; 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; import static google.registry.flows.domain.DomainFlowUtils.verifyLaunchPhaseMatchesRegistryPhase; import static google.registry.flows.domain.DomainFlowUtils.verifyNoCodeMarks; import static google.registry.flows.domain.DomainFlowUtils.verifyNotReserved; import static google.registry.flows.domain.DomainFlowUtils.verifyPremiumNameIsNotBlocked; import static google.registry.flows.domain.DomainFlowUtils.verifyRegistrarIsActive; import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears; import static google.registry.model.EppResourceUtils.createDomainRepoId; import static google.registry.model.eppcommon.StatusValue.SERVER_HOLD; import static google.registry.model.eppcommon.StatusValue.SERVER_TRANSFER_PROHIBITED; import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED; import static google.registry.model.index.DomainApplicationIndex.loadActiveApplicationsByDomainName; import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.registry.Registry.TldState.GENERAL_AVAILABILITY; import static google.registry.model.registry.Registry.TldState.START_DATE_SUNRISE; import static google.registry.model.registry.Registry.TldState.SUNRISE; import static google.registry.model.registry.Registry.TldState.SUNRUSH; import static google.registry.model.registry.label.ReservationType.NAME_COLLISION; import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.leapSafeAddYears; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.google.common.net.InternetDomainName; import com.googlecode.objectify.Key; import google.registry.dns.DnsQueue; import google.registry.flows.EppException; import google.registry.flows.EppException.CommandUseErrorException; import google.registry.flows.EppException.ParameterValuePolicyErrorException; import google.registry.flows.EppException.StatusProhibitsOperationException; import google.registry.flows.ExtensionManager; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.Superuser; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; import google.registry.flows.annotations.ReportingSpec; import google.registry.flows.custom.DomainCreateFlowCustomLogic; import google.registry.flows.custom.DomainCreateFlowCustomLogic.BeforeResponseParameters; import google.registry.flows.custom.DomainCreateFlowCustomLogic.BeforeResponseReturnData; import google.registry.flows.custom.EntityChanges; import google.registry.flows.domain.token.AllocationTokenFlowUtils; import google.registry.model.ImmutableObject; import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent.Flag; import google.registry.model.billing.BillingEvent.Reason; import google.registry.model.billing.BillingEvent.Recurring; import google.registry.model.domain.DomainApplication; import google.registry.model.domain.DomainCommand; import google.registry.model.domain.DomainCommand.Create; import google.registry.model.domain.DomainResource; 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.FeeTransformResponseExtension; import google.registry.model.domain.launch.LaunchCreateExtension; import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.domain.secdns.SecDnsCreateExtension; import google.registry.model.domain.token.AllocationToken; import google.registry.model.domain.token.AllocationTokenExtension; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppoutput.CreateData.DomainCreateData; import google.registry.model.eppoutput.EppResponse; import google.registry.model.index.EppResourceIndex; import google.registry.model.index.ForeignKeyIndex; import google.registry.model.ofy.ObjectifyService; import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse; import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage.Autorenew; import google.registry.model.registry.Registry; import google.registry.model.registry.Registry.TldState; import google.registry.model.registry.Registry.TldType; import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField; import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; import google.registry.tmch.LordnTaskUtils; import java.util.Optional; import javax.inject.Inject; import org.joda.time.DateTime; import org.joda.time.Duration; /** * 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.ResourceAlreadyExistsException} * @error {@link google.registry.flows.EppException.UnimplementedExtensionException} * @error {@link google.registry.flows.ExtensionManager.UndeclaredServiceExtensionException} * @error {@link DomainCreateFlow.AnchorTenantCreatePeriodException} * @error {@link DomainCreateFlow.DomainHasOpenApplicationsException} * @error {@link DomainCreateFlow.MustHaveSignedMarksInCurrentPhaseException} * @error {@link DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException} * @error {@link DomainCreateFlow.SignedMarksOnlyDuringSunriseException} * @error {@link DomainFlowTmchUtils.NoMarksFoundMatchingDomainException} * @error {@link DomainFlowTmchUtils.FoundMarkNotYetValidException} * @error {@link DomainFlowTmchUtils.FoundMarkExpiredException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException} * @error {@link DomainFlowUtils.AcceptedTooLongAgoException} * @error {@link DomainFlowUtils.BadDomainNameCharacterException} * @error {@link DomainFlowUtils.BadDomainNamePartsCountException} * @error {@link DomainFlowUtils.DomainNameExistsAsTldException} * @error {@link DomainFlowUtils.BadPeriodUnitException} * @error {@link DomainFlowUtils.ClaimsPeriodEndedException} * @error {@link DomainFlowUtils.CurrencyUnitMismatchException} * @error {@link DomainFlowUtils.CurrencyValueScaleException} * @error {@link DomainFlowUtils.DashesInThirdAndFourthException} * @error {@link DomainFlowUtils.DomainLabelTooLongException} * @error {@link DomainFlowUtils.DomainNotAllowedForTldWithCreateRestrictionException} * @error {@link DomainFlowUtils.DomainReservedException} * @error {@link DomainFlowUtils.DuplicateContactForRoleException} * @error {@link DomainFlowUtils.EmptyDomainNamePartException} * @error {@link DomainFlowUtils.ExceedsMaxRegistrationYearsException} * @error {@link DomainFlowUtils.ExpiredClaimException} * @error {@link DomainFlowUtils.FeeDescriptionMultipleMatchesException} * @error {@link DomainFlowUtils.FeeDescriptionParseException} * @error {@link DomainFlowUtils.FeesMismatchException} * @error {@link DomainFlowUtils.FeesRequiredDuringEarlyAccessProgramException} * @error {@link DomainFlowUtils.FeesRequiredForPremiumNameException} * @error {@link DomainFlowUtils.InvalidIdnDomainLabelException} * @error {@link DomainFlowUtils.InvalidPunycodeException} * @error {@link DomainFlowUtils.InvalidTcnIdChecksumException} * @error {@link DomainFlowUtils.InvalidTrademarkValidatorException} * @error {@link DomainFlowUtils.LeadingDashException} * @error {@link DomainFlowUtils.LinkedResourcesDoNotExistException} * @error {@link DomainFlowUtils.LinkedResourceInPendingDeleteProhibitsOperationException} * @error {@link DomainFlowUtils.MalformedTcnIdException} * @error {@link DomainFlowUtils.MaxSigLifeNotSupportedException} * @error {@link DomainFlowUtils.MissingAdminContactException} * @error {@link DomainFlowUtils.MissingClaimsNoticeException} * @error {@link DomainFlowUtils.MissingContactTypeException} * @error {@link DomainFlowUtils.MissingRegistrantException} * @error {@link DomainFlowUtils.MissingTechnicalContactException} * @error {@link DomainFlowUtils.NameserversNotAllowedForDomainException} * @error {@link DomainFlowUtils.NameserversNotAllowedForTldException} * @error {@link DomainFlowUtils.NameserversNotSpecifiedForNameserverRestrictedDomainException} * @error {@link DomainFlowUtils.NameserversNotSpecifiedForTldWithNameserverWhitelistException} * @error {@link DomainFlowUtils.PremiumNameBlockedException} * @error {@link DomainFlowUtils.RegistrantNotAllowedException} * @error {@link DomainFlowUtils.RegistrarMustBeActiveForThisOperationException} * @error {@link DomainFlowUtils.TldDoesNotExistException} * @error {@link DomainFlowUtils.TooManyDsRecordsException} * @error {@link DomainFlowUtils.TooManyNameserversException} * @error {@link DomainFlowUtils.TrailingDashException} * @error {@link DomainFlowUtils.UnexpectedClaimsNoticeException} * @error {@link DomainFlowUtils.UnsupportedFeeAttributeException} * @error {@link DomainFlowUtils.UnsupportedMarkTypeException} */ @ReportingSpec(ActivityReportField.DOMAIN_CREATE) public class DomainCreateFlow implements TransactionalFlow { /** * States when the TLD is in sunrise. * *
Note that a TLD in SUNRUSH means sunrise is in effect, but not necessarily that the "create" * command is a "sunrise create". It might be a landrush create. We must make sure there's a * signed mark to know if the create is "sunrise" or "landrush" for verification purposes. * *
Note also that SUNRISE (start-date sunrise) and LANDRUSH can't "naturally" succeed in this
* flow. They can only succeed if sent as a superuser or anchor tenant.
*/
private static final ImmutableSet A trademarked domain name requires either a signed mark or a claims notice. We then need to
* send out a LORDN message - either a "sunrise" LORDN if we have a signed mark, or a "claims"
* LORDN if we have a claims notice.
*
* This verification prevents us from either sending out a "sunrise" LORDN out of sunrise, or
* not sending out any LORDN, for a trademarked domain with a signed mark in GA.
*/
static void verifySignedMarkOnlyInSunrise(boolean hasSignedMarks, TldState tldState)
throws EppException {
if (hasSignedMarks && !SUNRISE_STATES.contains(tldState)) {
throw new SignedMarksOnlyDuringSunriseException();
}
}
/**
* Verifies anchor tenant creates are only done for {@value ANCHOR_TENANT_CREATE_VALID_YEARS} year
* periods, as anchor tenants get exactly that many years of free registration.
*/
static void verifyAnchorTenantValidPeriod(boolean isAnchorTenant, int registrationYears)
throws EppException {
if (isAnchorTenant && registrationYears != ANCHOR_TENANT_CREATE_VALID_YEARS) {
throw new AnchorTenantCreatePeriodException(registrationYears);
}
}
/** Prohibit creating a domain if there is an open application for the same name. */
private void verifyNoOpenApplications(DateTime now) throws DomainHasOpenApplicationsException {
for (DomainApplication application : loadActiveApplicationsByDomainName(targetId, now)) {
if (!application.getApplicationStatus().isFinalStatus()) {
throw new DomainHasOpenApplicationsException();
}
}
}
/**
* Prohibit registrations unless QLP, General Availability or Start Date Sunrise.
*
* During Start-Date Sunrise, we need a signed mark for registrations.
*
* Note that "superuser" status isn't tested here - this should only be called for
* non-superusers.
*/
private void verifyIsGaOrIsSpecialCase(
TldState tldState, boolean isAnchorTenant, boolean hasSignedMarks)
throws NoGeneralRegistrationsInCurrentPhaseException,
MustHaveSignedMarksInCurrentPhaseException {
// Anchor Tenant overrides any other consideration to allow registration.
if (isAnchorTenant) {
return;
}
// We allow general registration during GA.
if (GENERAL_AVAILABILITY.equals(tldState)) {
return;
}
// During START_DATE_SUNRISE, only allow registration with a signed marks.
if (START_DATE_SUNRISE.equals(tldState)) {
if (!hasSignedMarks) {
throw new MustHaveSignedMarksInCurrentPhaseException();
}
return;
}
// All other phases do not allow registration
throw new NoGeneralRegistrationsInCurrentPhaseException();
}
/** Verifies and returns the allocation token if one is specified, otherwise does nothing. */
private Optional