mirror of
https://github.com/google/nomulus.git
synced 2025-07-24 11:38:35 +02:00
Delete end-date sunrise, landrush, and sunrush phases
This also deletes the associated commands and domain application specific entities. We haven't used any of these TLD phases since early 2015 and have no intent to do so in the future, so it makes sense to delete them now so we don't have to carry them through the Registry 3.0 migration. Note that, while there are data model changes, there should be no required data migrations. The fields and entities being removed will simply remain as orphans. I confirmed that the removed types (such as the SUNRUSH_ADD GracePeriodType) are no longer used in production data, and left types that are still used, e.g. BillingEvent.Flag.LANDRUSH or HistoryEntry.Type.ALLOCATE. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=228752843
This commit is contained in:
parent
c74ffd7559
commit
580302898d
282 changed files with 344 additions and 17634 deletions
|
@ -250,12 +250,4 @@ public abstract class EppException extends Exception {
|
|||
super("Specified protocol version is not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
/** Command failed. */
|
||||
@EppResultCode(Code.COMMAND_FAILED)
|
||||
public static class CommandFailedException extends EppException {
|
||||
public CommandFailedException() {
|
||||
super("Command failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,11 +30,6 @@ import google.registry.flows.contact.ContactTransferRejectFlow;
|
|||
import google.registry.flows.contact.ContactTransferRequestFlow;
|
||||
import google.registry.flows.contact.ContactUpdateFlow;
|
||||
import google.registry.flows.custom.CustomLogicModule;
|
||||
import google.registry.flows.domain.DomainAllocateFlow;
|
||||
import google.registry.flows.domain.DomainApplicationCreateFlow;
|
||||
import google.registry.flows.domain.DomainApplicationDeleteFlow;
|
||||
import google.registry.flows.domain.DomainApplicationInfoFlow;
|
||||
import google.registry.flows.domain.DomainApplicationUpdateFlow;
|
||||
import google.registry.flows.domain.DomainCheckFlow;
|
||||
import google.registry.flows.domain.DomainClaimsCheckFlow;
|
||||
import google.registry.flows.domain.DomainCreateFlow;
|
||||
|
@ -86,11 +81,6 @@ public interface FlowComponent {
|
|||
ContactTransferRejectFlow contactTransferRejectFlow();
|
||||
ContactTransferRequestFlow contactTransferRequestFlow();
|
||||
ContactUpdateFlow contactUpdateFlow();
|
||||
DomainAllocateFlow domainAllocateFlow();
|
||||
DomainApplicationCreateFlow domainApplicationCreateFlow();
|
||||
DomainApplicationDeleteFlow domainApplicationDeleteFlow();
|
||||
DomainApplicationInfoFlow domainApplicationInfoFlow();
|
||||
DomainApplicationUpdateFlow domainApplicationUpdateFlow();
|
||||
DomainCheckFlow domainCheckFlow();
|
||||
DomainClaimsCheckFlow domainClaimsCheckFlow();
|
||||
DomainCreateFlow domainCreateFlow();
|
||||
|
@ -140,11 +130,6 @@ public interface FlowComponent {
|
|||
: clazz.equals(ContactTransferRejectFlow.class) ? flows.contactTransferRejectFlow()
|
||||
: clazz.equals(ContactTransferRequestFlow.class) ? flows.contactTransferRequestFlow()
|
||||
: clazz.equals(ContactUpdateFlow.class) ? flows.contactUpdateFlow()
|
||||
: clazz.equals(DomainAllocateFlow.class) ? flows.domainAllocateFlow()
|
||||
: clazz.equals(DomainApplicationCreateFlow.class) ? flows.domainApplicationCreateFlow()
|
||||
: clazz.equals(DomainApplicationDeleteFlow.class) ? flows.domainApplicationDeleteFlow()
|
||||
: clazz.equals(DomainApplicationInfoFlow.class) ? flows.domainApplicationInfoFlow()
|
||||
: clazz.equals(DomainApplicationUpdateFlow.class) ? flows.domainApplicationUpdateFlow()
|
||||
: clazz.equals(DomainCheckFlow.class) ? flows.domainCheckFlow()
|
||||
: clazz.equals(DomainClaimsCheckFlow.class) ? flows.domainClaimsCheckFlow()
|
||||
: clazz.equals(DomainCreateFlow.class) ? flows.domainCreateFlow()
|
||||
|
|
|
@ -15,13 +15,11 @@
|
|||
package google.registry.flows;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.base.Strings.nullToEmpty;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.flows.picker.FlowPicker;
|
||||
import google.registry.model.domain.launch.ApplicationIdTargetExtension;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.eppcommon.AuthInfo;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
|
@ -205,19 +203,6 @@ public class FlowModule {
|
|||
return ((SingleResourceCommand) resourceCommand).getTargetId();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FlowScope
|
||||
@ApplicationId
|
||||
static String provideApplicationId(EppInput eppInput) {
|
||||
// Treat a missing application id as empty so we can always inject a non-null value.
|
||||
Optional<ApplicationIdTargetExtension> extension =
|
||||
eppInput.getSingleExtension(ApplicationIdTargetExtension.class);
|
||||
checkState(
|
||||
extension.isPresent(),
|
||||
"ApplicationIdTargetExtension must be used to provide the application ID");
|
||||
return nullToEmpty(extension.get().getApplicationId());
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FlowScope
|
||||
@PollMessageId
|
||||
|
@ -294,11 +279,6 @@ public class FlowModule {
|
|||
@Documented
|
||||
public @interface TargetId {}
|
||||
|
||||
/** Dagger qualifier for the application id for domain application flows. */
|
||||
@Qualifier
|
||||
@Documented
|
||||
public @interface ApplicationId {}
|
||||
|
||||
/** Dagger qualifier for the message id for poll flows. */
|
||||
@Qualifier
|
||||
@Documented
|
||||
|
|
|
@ -32,11 +32,6 @@ import google.registry.model.eppinput.EppInput;
|
|||
*/
|
||||
public class CustomLogicFactory {
|
||||
|
||||
public DomainApplicationCreateFlowCustomLogic forDomainApplicationCreateFlow(
|
||||
EppInput eppInput, SessionMetadata sessionMetadata, FlowMetadata flowMetadata) {
|
||||
return new DomainApplicationCreateFlowCustomLogic(eppInput, sessionMetadata, flowMetadata);
|
||||
}
|
||||
|
||||
public DomainCreateFlowCustomLogic forDomainCreateFlow(
|
||||
EppInput eppInput, SessionMetadata sessionMetadata, FlowMetadata flowMetadata) {
|
||||
return new DomainCreateFlowCustomLogic(eppInput, sessionMetadata, flowMetadata);
|
||||
|
|
|
@ -24,15 +24,6 @@ import google.registry.model.eppinput.EppInput;
|
|||
@Module
|
||||
public class CustomLogicModule {
|
||||
|
||||
@Provides
|
||||
static DomainApplicationCreateFlowCustomLogic provideDomainApplicationCreateFlowCustomLogic(
|
||||
CustomLogicFactory factory,
|
||||
EppInput eppInput,
|
||||
SessionMetadata sessionMetadata,
|
||||
FlowMetadata flowMetadata) {
|
||||
return factory.forDomainApplicationCreateFlow(eppInput, sessionMetadata, flowMetadata);
|
||||
}
|
||||
|
||||
@Provides
|
||||
static DomainCreateFlowCustomLogic provideDomainCreateFlowCustomLogic(
|
||||
CustomLogicFactory factory,
|
||||
|
|
|
@ -1,221 +0,0 @@
|
|||
// 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.custom;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.FlowMetadata;
|
||||
import google.registry.flows.SessionMetadata;
|
||||
import google.registry.flows.domain.DomainApplicationCreateFlow;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.domain.DomainApplication;
|
||||
import google.registry.model.eppinput.EppInput;
|
||||
import google.registry.model.eppoutput.EppResponse.ResponseData;
|
||||
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
|
||||
/**
|
||||
* A no-op base class for {@link DomainApplicationCreateFlow} custom logic.
|
||||
*
|
||||
* <p>Extend this class and override the hook(s) to perform custom logic.
|
||||
*/
|
||||
public class DomainApplicationCreateFlowCustomLogic extends BaseFlowCustomLogic {
|
||||
|
||||
protected DomainApplicationCreateFlowCustomLogic(
|
||||
EppInput eppInput, SessionMetadata sessionMetadata, FlowMetadata flowMetadata) {
|
||||
super(eppInput, sessionMetadata, flowMetadata);
|
||||
}
|
||||
|
||||
/** A hook that runs before any validation. This is useful to e.g. add allowable extensions. */
|
||||
@SuppressWarnings("unused")
|
||||
public void beforeValidation() throws EppException {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
/** A hook that runs at the end of the validation step to perform additional validation. */
|
||||
@SuppressWarnings("unused")
|
||||
public void afterValidation(AfterValidationParameters parameters) throws EppException {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
/**
|
||||
* A hook that runs before new entities are persisted, allowing them to be changed.
|
||||
*
|
||||
* <p>It returns the actual entity changes that should be persisted to Datastore. It is important
|
||||
* to be careful when changing the flow behavior for existing entities, because the core logic
|
||||
* across many different flows expects the existence of these entities and many of the fields on
|
||||
* them.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public EntityChanges beforeSave(BeforeSaveParameters parameters) throws EppException {
|
||||
return parameters.entityChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* A hook that runs before the response is returned.
|
||||
*
|
||||
* <p>This takes the {@link ResponseData} and {@link ResponseExtension}s as input and returns
|
||||
* them, potentially with modifications.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public BeforeResponseReturnData beforeResponse(BeforeResponseParameters parameters)
|
||||
throws EppException {
|
||||
return BeforeResponseReturnData.newBuilder()
|
||||
.setResData(parameters.resData())
|
||||
.setResponseExtensions(parameters.responseExtensions())
|
||||
.build();
|
||||
}
|
||||
|
||||
/** A class to encapsulate parameters for a call to {@link #afterValidation}. */
|
||||
@AutoValue
|
||||
public abstract static class AfterValidationParameters extends ImmutableObject {
|
||||
|
||||
/** The parsed domain name of the domain that is being applied for. */
|
||||
public abstract InternetDomainName domainName();
|
||||
|
||||
/**
|
||||
* The number of years that the application is requesting registration for.
|
||||
*
|
||||
* <p>On standard TLDs, this is usually 1.
|
||||
*/
|
||||
public abstract int years();
|
||||
|
||||
public static Builder newBuilder() {
|
||||
return new AutoValue_DomainApplicationCreateFlowCustomLogic_AfterValidationParameters
|
||||
.Builder();
|
||||
}
|
||||
|
||||
/** Builder for {@link AfterValidationParameters}. */
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
|
||||
public abstract Builder setDomainName(InternetDomainName domainName);
|
||||
|
||||
public abstract Builder setYears(int years);
|
||||
|
||||
public abstract AfterValidationParameters build();
|
||||
}
|
||||
}
|
||||
|
||||
/** A class to encapsulate parameters for a call to {@link #beforeSave}. */
|
||||
@AutoValue
|
||||
public abstract static class BeforeSaveParameters extends ImmutableObject {
|
||||
|
||||
/**
|
||||
* The new {@link DomainApplication} entity that is going to be persisted at the end of the
|
||||
* transaction.
|
||||
*/
|
||||
public abstract DomainApplication newApplication();
|
||||
|
||||
/**
|
||||
* The new {@link HistoryEntry} entity for the application's creation that is going to be
|
||||
* persisted at the end of the transaction.
|
||||
*/
|
||||
public abstract HistoryEntry historyEntry();
|
||||
|
||||
/**
|
||||
* The collection of {@link EntityChanges} (including new entities and those to delete) that
|
||||
* will be persisted at the end of the transaction.
|
||||
*
|
||||
* <p>Note that the new application and history entry are also included as saves in this
|
||||
* collection, and are separated out above solely for convenience, as they are most likely to
|
||||
* need to be changed. Removing them from the collection will cause them not to be saved, which
|
||||
* is most likely not what you intended.
|
||||
*/
|
||||
public abstract EntityChanges entityChanges();
|
||||
|
||||
/**
|
||||
* The number of years that the application is requesting registration for.
|
||||
*
|
||||
* <p>On standard TLDs, this is usually 1.
|
||||
*/
|
||||
public abstract int years();
|
||||
|
||||
public static Builder newBuilder() {
|
||||
return new AutoValue_DomainApplicationCreateFlowCustomLogic_BeforeSaveParameters.Builder();
|
||||
}
|
||||
|
||||
/** Builder for {@link BeforeSaveParameters}. */
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
|
||||
public abstract Builder setNewApplication(DomainApplication newApplication);
|
||||
|
||||
public abstract Builder setHistoryEntry(HistoryEntry historyEntry);
|
||||
|
||||
public abstract Builder setEntityChanges(EntityChanges entityChanges);
|
||||
|
||||
public abstract Builder setYears(int years);
|
||||
|
||||
public abstract BeforeSaveParameters build();
|
||||
}
|
||||
}
|
||||
|
||||
/** A class to encapsulate parameters for a call to {@link #beforeResponse}. */
|
||||
@AutoValue
|
||||
public abstract static class BeforeResponseParameters extends ImmutableObject {
|
||||
|
||||
public abstract ResponseData resData();
|
||||
|
||||
public abstract ImmutableList<? extends ResponseExtension> responseExtensions();
|
||||
|
||||
public static BeforeResponseParameters.Builder newBuilder() {
|
||||
return new AutoValue_DomainApplicationCreateFlowCustomLogic_BeforeResponseParameters
|
||||
.Builder();
|
||||
}
|
||||
|
||||
/** Builder for {@link DomainApplicationCreateFlowCustomLogic.BeforeResponseParameters}. */
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
|
||||
public abstract BeforeResponseParameters.Builder setResData(ResponseData resData);
|
||||
|
||||
public abstract BeforeResponseParameters.Builder setResponseExtensions(
|
||||
ImmutableList<? extends ResponseExtension> responseExtensions);
|
||||
|
||||
public abstract BeforeResponseParameters build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class to encapsulate parameters for the return values from a call to {@link #beforeResponse}.
|
||||
*/
|
||||
@AutoValue
|
||||
public abstract static class BeforeResponseReturnData extends ImmutableObject {
|
||||
|
||||
public abstract ResponseData resData();
|
||||
|
||||
public abstract ImmutableList<? extends ResponseExtension> responseExtensions();
|
||||
|
||||
public static BeforeResponseReturnData.Builder newBuilder() {
|
||||
return new AutoValue_DomainApplicationCreateFlowCustomLogic_BeforeResponseReturnData
|
||||
.Builder();
|
||||
}
|
||||
|
||||
/** Builder for {@link DomainApplicationCreateFlowCustomLogic.BeforeResponseReturnData}. */
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
|
||||
public abstract BeforeResponseReturnData.Builder setResData(ResponseData resData);
|
||||
|
||||
public abstract BeforeResponseReturnData.Builder setResponseExtensions(
|
||||
ImmutableList<? extends ResponseExtension> responseExtensions);
|
||||
|
||||
public abstract BeforeResponseReturnData build();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,7 +22,6 @@ import google.registry.flows.SessionMetadata;
|
|||
import google.registry.flows.domain.DomainPricingLogic;
|
||||
import google.registry.flows.domain.FeesAndCredits;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.domain.DomainApplication;
|
||||
import google.registry.model.eppinput.EppInput;
|
||||
import google.registry.model.registry.Registry;
|
||||
import org.joda.time.DateTime;
|
||||
|
@ -39,13 +38,6 @@ public class DomainPricingCustomLogic extends BaseFlowCustomLogic {
|
|||
super(eppInput, sessionMetadata, flowMetadata);
|
||||
}
|
||||
|
||||
/** A hook that customizes the application update price. */
|
||||
@SuppressWarnings("unused")
|
||||
public FeesAndCredits customizeApplicationUpdatePrice(
|
||||
ApplicationUpdatePriceParameters priceParameters) throws EppException {
|
||||
return priceParameters.feesAndCredits();
|
||||
}
|
||||
|
||||
/** A hook that customizes the create price. */
|
||||
@SuppressWarnings("unused")
|
||||
public FeesAndCredits customizeCreatePrice(CreatePriceParameters priceParameters)
|
||||
|
@ -81,38 +73,6 @@ public class DomainPricingCustomLogic extends BaseFlowCustomLogic {
|
|||
return priceParameters.feesAndCredits();
|
||||
}
|
||||
|
||||
/** A class to encapsulate parameters for a call to {@link #customizeApplicationUpdatePrice} . */
|
||||
@AutoValue
|
||||
public abstract static class ApplicationUpdatePriceParameters extends ImmutableObject {
|
||||
|
||||
public abstract FeesAndCredits feesAndCredits();
|
||||
|
||||
public abstract Registry registry();
|
||||
|
||||
public abstract DomainApplication domainApplication();
|
||||
|
||||
public abstract DateTime asOfDate();
|
||||
|
||||
public static Builder newBuilder() {
|
||||
return new AutoValue_DomainPricingCustomLogic_ApplicationUpdatePriceParameters.Builder();
|
||||
}
|
||||
|
||||
/** Builder for {@link ApplicationUpdatePriceParameters}. */
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
|
||||
public abstract Builder setFeesAndCredits(FeesAndCredits feesAndCredits);
|
||||
|
||||
public abstract Builder setRegistry(Registry registry);
|
||||
|
||||
public abstract Builder setDomainApplication(DomainApplication domainApplication);
|
||||
|
||||
public abstract Builder setAsOfDate(DateTime asOfDate);
|
||||
|
||||
public abstract ApplicationUpdatePriceParameters build();
|
||||
}
|
||||
}
|
||||
|
||||
/** A class to encapsulate parameters for a call to {@link #customizeCreatePrice} . */
|
||||
@AutoValue
|
||||
public abstract static class CreatePriceParameters extends ImmutableObject {
|
||||
|
|
|
@ -1,424 +0,0 @@
|
|||
// 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 com.google.common.collect.MoreCollectors.onlyElement;
|
||||
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.cloneAndLinkReferences;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.createFeeCreateResponse;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.getReservationTypes;
|
||||
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;
|
||||
import static google.registry.model.EppResourceUtils.loadDomainApplication;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.pricing.PricingEngineProxy.getDomainCreateCost;
|
||||
import static google.registry.util.CollectionUtils.isNullOrEmpty;
|
||||
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.Streams;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.googlecode.objectify.Key;
|
||||
import dagger.Lazy;
|
||||
import google.registry.dns.DnsQueue;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.AuthorizationErrorException;
|
||||
import google.registry.flows.EppException.ObjectDoesNotExistException;
|
||||
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.domain.DomainFlowUtils.NameserversNotSpecifiedForNameserverRestrictedDomainException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedForTldWithNameserverWhitelistException;
|
||||
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.domain.DomainApplication;
|
||||
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.allocate.AllocateCreateExtension;
|
||||
import google.registry.model.domain.fee.FeeCreateCommandExtension;
|
||||
import google.registry.model.domain.fee.FeeTransformResponseExtension;
|
||||
import google.registry.model.domain.launch.ApplicationStatus;
|
||||
import google.registry.model.domain.launch.LaunchInfoResponseExtension;
|
||||
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.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.registry.Registry;
|
||||
import google.registry.model.registry.label.ReservationType;
|
||||
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 java.util.Set;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* An EPP flow that allocates a new domain resource from a domain application.
|
||||
*
|
||||
* <p>Note that this flow is only run by superusers.
|
||||
*
|
||||
* @error {@link google.registry.flows.exceptions.ResourceAlreadyExistsException}
|
||||
* @error {@link DomainAllocateFlow.HasFinalStatusException}
|
||||
* @error {@link DomainAllocateFlow.MissingApplicationException}
|
||||
* @error {@link DomainAllocateFlow.OnlySuperuserCanAllocateException}
|
||||
* @error {@link DomainFlowUtils.ExceedsMaxRegistrationYearsException}
|
||||
* @error {@link DomainFlowUtils.RegistrantNotAllowedException}
|
||||
* @error {@link DomainFlowUtils.NameserversNotAllowedForDomainException}
|
||||
* @error {@link DomainFlowUtils.NameserversNotAllowedForTldException}
|
||||
* @error {@link NameserversNotSpecifiedForNameserverRestrictedDomainException}
|
||||
* @error {@link NameserversNotSpecifiedForTldWithNameserverWhitelistException}
|
||||
*/
|
||||
@ReportingSpec(ActivityReportField.DOMAIN_CREATE) // Allocates are special domain creates.
|
||||
public class DomainAllocateFlow implements TransactionalFlow {
|
||||
|
||||
@Inject ExtensionManager extensionManager;
|
||||
@Inject AuthInfo authInfo;
|
||||
@Inject ResourceCommand resourceCommand;
|
||||
@Inject @ClientId String clientId;
|
||||
@Inject @TargetId String targetId;
|
||||
@Inject @Superuser boolean isSuperuser;
|
||||
@Inject HistoryEntry.Builder historyBuilder;
|
||||
@Inject EppInput eppInput;
|
||||
@Inject Lazy<DnsQueue> dnsQueue;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
@Inject DomainAllocateFlow() {}
|
||||
|
||||
@Override
|
||||
public final EppResponse run() throws EppException {
|
||||
extensionManager.register(
|
||||
FeeCreateCommandExtension.class,
|
||||
SecDnsCreateExtension.class,
|
||||
MetadataExtension.class,
|
||||
AllocateCreateExtension.class);
|
||||
extensionManager.validate();
|
||||
validateClientIsLoggedIn(clientId);
|
||||
verifyIsSuperuser();
|
||||
DateTime now = ofy().getTransactionTime();
|
||||
Create command = cloneAndLinkReferences((Create) resourceCommand, now);
|
||||
verifyResourceDoesNotExist(DomainResource.class, targetId, now);
|
||||
InternetDomainName domainName = validateDomainName(command.getFullyQualifiedDomainName());
|
||||
Registry registry = Registry.get(domainName.parent().toString());
|
||||
Period period = command.getPeriod();
|
||||
verifyUnitIsYears(period);
|
||||
int years = period.getValue();
|
||||
validateRegistrationPeriod(years);
|
||||
validateCreateCommandContactsAndNameservers(command, registry, domainName);
|
||||
Optional<SecDnsCreateExtension> secDnsCreate =
|
||||
validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class));
|
||||
boolean isSunrushAddGracePeriod = isNullOrEmpty(command.getNameservers());
|
||||
AllocateCreateExtension allocateCreate =
|
||||
eppInput.getSingleExtension(AllocateCreateExtension.class).get();
|
||||
DomainApplication application =
|
||||
loadAndValidateApplication(allocateCreate.getApplicationRoid(), now);
|
||||
String repoId = createDomainRepoId(ObjectifyService.allocateId(), registry.getTldStr());
|
||||
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
|
||||
HistoryEntry historyEntry = buildHistoryEntry(
|
||||
repoId, registry.getTldStr(), now, period, registry.getAddGracePeriodLength());
|
||||
entitiesToSave.add(historyEntry);
|
||||
ImmutableSet<? extends ImmutableObject> billsAndPolls = createBillingEventsAndPollMessages(
|
||||
domainName, application, historyEntry, isSunrushAddGracePeriod, registry, now, years);
|
||||
entitiesToSave.addAll(billsAndPolls);
|
||||
DateTime registrationExpirationTime = leapSafeAddYears(now, years);
|
||||
DomainResource newDomain =
|
||||
new DomainResource.Builder()
|
||||
.setCreationClientId(clientId)
|
||||
.setPersistedCurrentSponsorClientId(clientId)
|
||||
.setRepoId(repoId)
|
||||
.setIdnTableName(validateDomainNameWithIdnTables(domainName))
|
||||
.setRegistrationExpirationTime(registrationExpirationTime)
|
||||
.setAutorenewBillingEvent(
|
||||
Key.create(getOnly(billsAndPolls, BillingEvent.Recurring.class)))
|
||||
.setAutorenewPollMessage(
|
||||
Key.create(getOnly(billsAndPolls, PollMessage.Autorenew.class)))
|
||||
.setApplicationTime(allocateCreate.getApplicationTime())
|
||||
.setApplication(Key.create(application))
|
||||
.setSmdId(allocateCreate.getSmdId())
|
||||
.setLaunchNotice(allocateCreate.getNotice())
|
||||
.setDsData(
|
||||
secDnsCreate.isPresent() ? secDnsCreate.get().getDsData() : application.getDsData())
|
||||
.addGracePeriod(
|
||||
createGracePeriod(
|
||||
isSunrushAddGracePeriod, getOnly(billsAndPolls, BillingEvent.OneTime.class)))
|
||||
// Names on the collision list will not be delegated. Set server hold.
|
||||
.setStatusValues(
|
||||
getReservationTypes(domainName).contains(ReservationType.NAME_COLLISION)
|
||||
? ImmutableSet.of(StatusValue.SERVER_HOLD)
|
||||
: ImmutableSet.of())
|
||||
.setRegistrant(command.getRegistrant())
|
||||
.setAuthInfo(command.getAuthInfo())
|
||||
.setFullyQualifiedDomainName(targetId)
|
||||
.setNameservers(command.getNameservers())
|
||||
.setContacts(command.getContacts())
|
||||
.build();
|
||||
entitiesToSave.add(
|
||||
newDomain,
|
||||
buildApplicationHistory(application, now),
|
||||
updateApplication(application),
|
||||
ForeignKeyIndex.create(newDomain, newDomain.getDeletionTime()),
|
||||
EppResourceIndex.create(Key.create(newDomain)));
|
||||
ofy().save().entities(entitiesToSave.build());
|
||||
enqueueTasks(allocateCreate, newDomain);
|
||||
return responseBuilder
|
||||
.setResData(DomainCreateData.create(targetId, now, registrationExpirationTime))
|
||||
.setExtensions(createResponseExtensions(now, registry, years))
|
||||
.build();
|
||||
}
|
||||
|
||||
private <T extends ImmutableObject> T getOnly(
|
||||
Iterable<? extends ImmutableObject> objects, Class<T> clazz) {
|
||||
return Streams.stream(objects)
|
||||
.filter(clazz::isInstance)
|
||||
.map(clazz::cast)
|
||||
.collect(onlyElement());
|
||||
}
|
||||
|
||||
private void verifyIsSuperuser() throws OnlySuperuserCanAllocateException {
|
||||
if (!isSuperuser) {
|
||||
throw new OnlySuperuserCanAllocateException();
|
||||
}
|
||||
}
|
||||
|
||||
private DomainApplication loadAndValidateApplication(
|
||||
String applicationRoid, DateTime now) throws EppException {
|
||||
DomainApplication application =
|
||||
loadDomainApplication(applicationRoid, now)
|
||||
.orElseThrow(() -> new MissingApplicationException(applicationRoid));
|
||||
if (application.getApplicationStatus().isFinalStatus()) {
|
||||
throw new HasFinalStatusException();
|
||||
}
|
||||
return application;
|
||||
}
|
||||
|
||||
private HistoryEntry buildHistoryEntry(
|
||||
String repoId, String tld, DateTime now, Period period, Duration addGracePeriod) {
|
||||
return historyBuilder
|
||||
.setType(HistoryEntry.Type.DOMAIN_ALLOCATE)
|
||||
.setPeriod(period)
|
||||
.setModificationTime(now)
|
||||
.setParent(Key.create(DomainResource.class, repoId))
|
||||
.setDomainTransactionRecords(
|
||||
ImmutableSet.of(
|
||||
DomainTransactionRecord.create(
|
||||
tld,
|
||||
now.plus(addGracePeriod),
|
||||
TransactionReportField.netAddsFieldFromYears(period.getValue()),
|
||||
1)))
|
||||
.build();
|
||||
}
|
||||
|
||||
private ImmutableSet<? extends ImmutableObject> createBillingEventsAndPollMessages(
|
||||
InternetDomainName domainName,
|
||||
DomainApplication application,
|
||||
HistoryEntry historyEntry,
|
||||
boolean isSunrushAddGracePeriod,
|
||||
Registry registry,
|
||||
DateTime now,
|
||||
int years) {
|
||||
DateTime registrationExpirationTime = leapSafeAddYears(now, years);
|
||||
BillingEvent.OneTime oneTimeBillingEvent = createOneTimeBillingEvent(
|
||||
application, historyEntry, isSunrushAddGracePeriod, registry, now, years);
|
||||
PollMessage.OneTime oneTimePollMessage =
|
||||
createOneTimePollMessage(application, historyEntry, getReservationTypes(domainName), now);
|
||||
// Create a new autorenew billing event and poll message starting at the expiration time.
|
||||
BillingEvent.Recurring autorenewBillingEvent =
|
||||
createAutorenewBillingEvent(historyEntry, registrationExpirationTime);
|
||||
PollMessage.Autorenew autorenewPollMessage =
|
||||
createAutorenewPollMessage(historyEntry, registrationExpirationTime);
|
||||
return ImmutableSet.of(
|
||||
oneTimePollMessage,
|
||||
oneTimeBillingEvent,
|
||||
autorenewBillingEvent,
|
||||
autorenewPollMessage);
|
||||
}
|
||||
|
||||
private BillingEvent.OneTime createOneTimeBillingEvent(
|
||||
DomainApplication application,
|
||||
HistoryEntry historyEntry,
|
||||
boolean isSunrushAddGracePeriod,
|
||||
Registry registry,
|
||||
DateTime now,
|
||||
int years) {
|
||||
return new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.CREATE)
|
||||
.setFlags(ImmutableSet.of(
|
||||
Flag.ALLOCATION,
|
||||
application.getEncodedSignedMarks().isEmpty() ? Flag.LANDRUSH : Flag.SUNRISE))
|
||||
.setTargetId(targetId)
|
||||
.setClientId(clientId)
|
||||
// Note that the cost is calculated as of now, i.e. the event time, not the billing time,
|
||||
// which may be some additional days into the future.
|
||||
.setCost(getDomainCreateCost(targetId, now, years))
|
||||
.setPeriodYears(years)
|
||||
.setEventTime(now)
|
||||
// If there are no nameservers on the domain, then they get the benefit of the sunrush
|
||||
// add grace period, which is longer than the standard add grace period.
|
||||
.setBillingTime(now.plus(isSunrushAddGracePeriod
|
||||
? registry.getSunrushAddGracePeriodLength()
|
||||
: registry.getAddGracePeriodLength()))
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
}
|
||||
|
||||
private BillingEvent.Recurring createAutorenewBillingEvent(
|
||||
HistoryEntry historyEntry, DateTime registrationExpirationTime) {
|
||||
return new BillingEvent.Recurring.Builder()
|
||||
.setReason(Reason.RENEW)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setTargetId(targetId)
|
||||
.setClientId(clientId)
|
||||
.setEventTime(registrationExpirationTime)
|
||||
.setRecurrenceEndTime(END_OF_TIME)
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
}
|
||||
|
||||
private PollMessage.Autorenew createAutorenewPollMessage(
|
||||
HistoryEntry historyEntry, DateTime registrationExpirationTime) {
|
||||
return new PollMessage.Autorenew.Builder()
|
||||
.setTargetId(targetId)
|
||||
.setClientId(clientId)
|
||||
.setEventTime(registrationExpirationTime)
|
||||
.setMsg("Domain was auto-renewed.")
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static GracePeriod createGracePeriod(boolean isSunrushAddGracePeriod,
|
||||
BillingEvent.OneTime oneTimeBillingEvent) {
|
||||
return GracePeriod.forBillingEvent(
|
||||
isSunrushAddGracePeriod ? GracePeriodStatus.SUNRUSH_ADD : GracePeriodStatus.ADD,
|
||||
oneTimeBillingEvent);
|
||||
}
|
||||
|
||||
/** Create a history entry (with no xml or trid) to record that we updated the application. */
|
||||
private static HistoryEntry buildApplicationHistory(DomainApplication application, DateTime now) {
|
||||
return new HistoryEntry.Builder()
|
||||
.setType(HistoryEntry.Type.DOMAIN_APPLICATION_STATUS_UPDATE)
|
||||
.setParent(application)
|
||||
.setModificationTime(now)
|
||||
.setClientId(application.getCurrentSponsorClientId())
|
||||
.setBySuperuser(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
/** Update the application itself. */
|
||||
private DomainApplication updateApplication(DomainApplication application) {
|
||||
return application.asBuilder()
|
||||
.setApplicationStatus(ApplicationStatus.ALLOCATED)
|
||||
.removeStatusValue(StatusValue.PENDING_CREATE)
|
||||
.build();
|
||||
}
|
||||
|
||||
/** Create a poll message informing the registrar that the application status was updated. */
|
||||
private PollMessage.OneTime createOneTimePollMessage(
|
||||
DomainApplication application,
|
||||
HistoryEntry historyEntry,
|
||||
Set<ReservationType> reservationTypes,
|
||||
DateTime now) {
|
||||
return new PollMessage.OneTime.Builder()
|
||||
.setClientId(historyEntry.getClientId())
|
||||
.setEventTime(now)
|
||||
.setMsg(
|
||||
reservationTypes.contains(ReservationType.NAME_COLLISION)
|
||||
? COLLISION_MESSAGE // Remind the registrar of the name collision policy.
|
||||
: "Domain was allocated")
|
||||
.setResponseData(
|
||||
ImmutableList.of(
|
||||
DomainPendingActionNotificationResponse.create(
|
||||
targetId, true, application.getCreationTrid(), now)))
|
||||
.setResponseExtensions(
|
||||
ImmutableList.of(
|
||||
new LaunchInfoResponseExtension.Builder()
|
||||
.setApplicationId(application.getForeignKey())
|
||||
.setPhase(application.getPhase())
|
||||
.setApplicationStatus(ApplicationStatus.ALLOCATED)
|
||||
.build()))
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void enqueueTasks(AllocateCreateExtension allocateCreate, DomainResource newDomain) {
|
||||
if (newDomain.shouldPublishToDns()) {
|
||||
dnsQueue.get().addDomainRefreshTask(newDomain.getFullyQualifiedDomainName());
|
||||
}
|
||||
if (allocateCreate.getSmdId() != null || allocateCreate.getNotice() != null) {
|
||||
LordnTaskUtils.enqueueDomainResourceTask(newDomain);
|
||||
}
|
||||
}
|
||||
|
||||
private ImmutableList<FeeTransformResponseExtension> createResponseExtensions(
|
||||
DateTime now, Registry registry, int years) throws EppException {
|
||||
FeesAndCredits feesAndCredits =
|
||||
pricingLogic.getCreatePrice(registry, targetId, now, years, false);
|
||||
Optional<FeeCreateCommandExtension> feeCreate =
|
||||
eppInput.getSingleExtension(FeeCreateCommandExtension.class);
|
||||
return feeCreate.isPresent()
|
||||
? ImmutableList.of(createFeeCreateResponse(feeCreate.get(), feesAndCredits))
|
||||
: ImmutableList.of();
|
||||
}
|
||||
|
||||
/** Domain application with specific ROID does not exist. */
|
||||
static class MissingApplicationException extends ObjectDoesNotExistException {
|
||||
public MissingApplicationException(String applicationRoid) {
|
||||
super(DomainApplication.class, applicationRoid);
|
||||
}
|
||||
}
|
||||
|
||||
/** Domain application already has a final status. */
|
||||
static class HasFinalStatusException extends StatusProhibitsOperationException {
|
||||
public HasFinalStatusException() {
|
||||
super("Domain application already has a final status");
|
||||
}
|
||||
}
|
||||
|
||||
/** Only a superuser can allocate domains. */
|
||||
static class OnlySuperuserCanAllocateException extends AuthorizationErrorException {
|
||||
public OnlySuperuserCanAllocateException() {
|
||||
super("Only a superuser can allocate domains");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,420 +0,0 @@
|
|||
// 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 com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.Iterables.getOnlyElement;
|
||||
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.checkAllowedAccessToTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.createFeeCreateResponse;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.isAnchorTenant;
|
||||
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.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.verifyRegistryStateAllowsApplicationFlows;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears;
|
||||
import static google.registry.model.EppResourceUtils.createDomainRepoId;
|
||||
import static google.registry.model.index.DomainApplicationIndex.loadActiveApplicationsByDomainName;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.CommandUseErrorException;
|
||||
import google.registry.flows.EppException.ObjectAlreadyExistsException;
|
||||
import google.registry.flows.EppException.RequiredParameterMissingException;
|
||||
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.DomainApplicationCreateFlowCustomLogic;
|
||||
import google.registry.flows.custom.DomainApplicationCreateFlowCustomLogic.AfterValidationParameters;
|
||||
import google.registry.flows.custom.DomainApplicationCreateFlowCustomLogic.BeforeResponseParameters;
|
||||
import google.registry.flows.custom.DomainApplicationCreateFlowCustomLogic.BeforeResponseReturnData;
|
||||
import google.registry.flows.custom.EntityChanges;
|
||||
import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedForNameserverRestrictedDomainException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedForTldWithNameserverWhitelistException;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.domain.DomainApplication;
|
||||
import google.registry.model.domain.DomainCommand.Create;
|
||||
import google.registry.model.domain.DomainResource;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.domain.fee.FeeCreateCommandExtension;
|
||||
import google.registry.model.domain.fee.FeeTransformCommandExtension;
|
||||
import google.registry.model.domain.launch.ApplicationStatus;
|
||||
import google.registry.model.domain.launch.LaunchCreateExtension;
|
||||
import google.registry.model.domain.launch.LaunchCreateResponseExtension;
|
||||
import google.registry.model.domain.launch.LaunchPhase;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.domain.secdns.SecDnsCreateExtension;
|
||||
import google.registry.model.eppcommon.AuthInfo;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
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.eppoutput.EppResponse.ResponseExtension;
|
||||
import google.registry.model.index.DomainApplicationIndex;
|
||||
import google.registry.model.index.EppResourceIndex;
|
||||
import google.registry.model.ofy.ObjectifyService;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.Registry.TldState;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.smd.EncodedSignedMark;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* An EPP flow that creates a new application for a domain resource.
|
||||
*
|
||||
* @error {@link google.registry.flows.exceptions.ResourceAlreadyExistsException}
|
||||
* @error {@link google.registry.flows.EppException.UnimplementedExtensionException}
|
||||
* @error {@link DomainApplicationCreateFlow.LandrushApplicationDisallowedDuringSunriseException}
|
||||
* @error {@link DomainApplicationCreateFlow.NoticeCannotBeUsedWithSignedMarkException}
|
||||
* @error {@link DomainApplicationCreateFlow.SunriseApplicationDisallowedDuringLandrushException}
|
||||
* @error {@link
|
||||
* DomainApplicationCreateFlow.UncontestedSunriseApplicationBlockedInLandrushException}
|
||||
* @error {@link DomainFlowUtils.AcceptedTooLongAgoException}
|
||||
* @error {@link DomainFlowUtils.BadCommandForRegistryPhaseException}
|
||||
* @error {@link DomainFlowUtils.BadDomainNameCharacterException}
|
||||
* @error {@link DomainFlowUtils.BadDomainNamePartsCountException}
|
||||
* @error {@link DomainFlowUtils.DomainNameExistsAsTldException}
|
||||
* @error {@link DomainFlowUtils.BadPeriodUnitException}
|
||||
* @error {@link DomainFlowTmchUtils.Base64RequiredForEncodedSignedMarksException}
|
||||
* @error {@link DomainFlowUtils.ClaimsPeriodEndedException}
|
||||
* @error {@link DomainFlowUtils.CurrencyUnitMismatchException}
|
||||
* @error {@link DomainFlowUtils.CurrencyValueScaleException}
|
||||
* @error {@link DomainFlowUtils.DashesInThirdAndFourthException}
|
||||
* @error {@link DomainFlowUtils.DomainLabelTooLongException}
|
||||
* @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}
|
||||
* @error {@link DomainFlowUtils.InvalidIdnDomainLabelException}
|
||||
* @error {@link DomainFlowUtils.InvalidPunycodeException}
|
||||
* @error {@link DomainFlowUtils.InvalidTcnIdChecksumException}
|
||||
* @error {@link DomainFlowUtils.InvalidTrademarkValidatorException}
|
||||
* @error {@link DomainFlowUtils.LaunchPhaseMismatchException}
|
||||
* @error {@link DomainFlowUtils.LeadingDashException}
|
||||
* @error {@link DomainFlowUtils.LinkedResourcesDoNotExistException}
|
||||
* @error {@link DomainFlowUtils.MalformedTcnIdException}
|
||||
* @error {@link DomainFlowUtils.MaxSigLifeNotSupportedException}
|
||||
* @error {@link DomainFlowUtils.MissingClaimsNoticeException}
|
||||
* @error {@link DomainFlowUtils.MissingContactTypeException}
|
||||
* @error {@link DomainFlowUtils.NameserversNotAllowedForDomainException}
|
||||
* @error {@link DomainFlowUtils.NameserversNotAllowedForTldException}
|
||||
* @error {@link NameserversNotSpecifiedForNameserverRestrictedDomainException}
|
||||
* @error {@link NameserversNotSpecifiedForTldWithNameserverWhitelistException}
|
||||
* @error {@link DomainFlowTmchUtils.NoMarksFoundMatchingDomainException}
|
||||
* @error {@link DomainFlowTmchUtils.FoundMarkNotYetValidException}
|
||||
* @error {@link DomainFlowTmchUtils.FoundMarkExpiredException}
|
||||
* @error {@link DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link DomainFlowUtils.PremiumNameBlockedException}
|
||||
* @error {@link DomainFlowUtils.RegistrantNotAllowedException}
|
||||
* @error {@link DomainFlowUtils.RegistrarMustBeActiveForThisOperationException}
|
||||
* @error {@link DomainFlowTmchUtils.SignedMarksMustBeEncodedException}
|
||||
* @error {@link DomainFlowTmchUtils.SignedMarkCertificateExpiredException}
|
||||
* @error {@link DomainFlowTmchUtils.SignedMarkCertificateInvalidException}
|
||||
* @error {@link DomainFlowTmchUtils.SignedMarkCertificateNotYetValidException}
|
||||
* @error {@link DomainFlowTmchUtils.SignedMarkCertificateRevokedException}
|
||||
* @error {@link DomainFlowTmchUtils.SignedMarkCertificateSignatureException}
|
||||
* @error {@link DomainFlowTmchUtils.SignedMarkEncodingErrorException}
|
||||
* @error {@link DomainFlowTmchUtils.SignedMarkParsingErrorException}
|
||||
* @error {@link DomainFlowTmchUtils.SignedMarkRevokedErrorException}
|
||||
* @error {@link DomainFlowTmchUtils.SignedMarkSignatureException}
|
||||
* @error {@link DomainFlowUtils.TldDoesNotExistException}
|
||||
* @error {@link DomainFlowUtils.TooManyDsRecordsException}
|
||||
* @error {@link DomainFlowUtils.TooManyNameserversException}
|
||||
* @error {@link DomainFlowTmchUtils.TooManySignedMarksException}
|
||||
* @error {@link DomainFlowUtils.TrailingDashException}
|
||||
* @error {@link DomainFlowUtils.UnexpectedClaimsNoticeException}
|
||||
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
|
||||
* @error {@link DomainFlowUtils.UnsupportedMarkTypeException}
|
||||
*/
|
||||
@ReportingSpec(ActivityReportField.DOMAIN_CREATE) // Applications are technically domains in EPP.
|
||||
public final class DomainApplicationCreateFlow implements TransactionalFlow {
|
||||
|
||||
@Inject ExtensionManager extensionManager;
|
||||
@Inject EppInput eppInput;
|
||||
@Inject AuthInfo authInfo;
|
||||
@Inject ResourceCommand resourceCommand;
|
||||
@Inject @ClientId String clientId;
|
||||
@Inject @TargetId String targetId;
|
||||
@Inject @Superuser boolean isSuperuser;
|
||||
@Inject HistoryEntry.Builder historyBuilder;
|
||||
@Inject Trid trid;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject DomainApplicationCreateFlowCustomLogic flowCustomLogic;
|
||||
@Inject DomainFlowTmchUtils tmchUtils;
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
@Inject DomainApplicationCreateFlow() {}
|
||||
|
||||
@Override
|
||||
public final EppResponse run() throws EppException {
|
||||
extensionManager.register(
|
||||
FeeCreateCommandExtension.class,
|
||||
SecDnsCreateExtension.class,
|
||||
MetadataExtension.class,
|
||||
LaunchCreateExtension.class);
|
||||
flowCustomLogic.beforeValidation();
|
||||
extensionManager.validate();
|
||||
validateClientIsLoggedIn(clientId);
|
||||
verifyRegistrarIsActive(clientId);
|
||||
DateTime now = ofy().getTransactionTime();
|
||||
Create command = cloneAndLinkReferences((Create) resourceCommand, now);
|
||||
// Fail if the domain is already registered (e.g. this is a landrush application but the domain
|
||||
// was awarded at the end of sunrise). However, multiple domain applications can be created for
|
||||
// the same domain name, so don't try to load an existing application.
|
||||
verifyResourceDoesNotExist(DomainResource.class, targetId, now);
|
||||
// Validate that this is actually a legal domain name on a TLD that the registrar has access to.
|
||||
InternetDomainName domainName = validateDomainName(targetId);
|
||||
String idnTableName = validateDomainNameWithIdnTables(domainName);
|
||||
String tld = domainName.parent().toString();
|
||||
if (!isSuperuser) {
|
||||
// Access to the TLD should be checked before the subsequent checks as it is a greater concern
|
||||
checkAllowedAccessToTld(clientId, tld);
|
||||
}
|
||||
Registry registry = Registry.get(tld);
|
||||
boolean isAnchorTenant =
|
||||
isAnchorTenant(domainName, Optional.empty(), authInfo.getPw().getValue(), Optional.empty());
|
||||
FeesAndCredits feesAndCredits =
|
||||
pricingLogic.getCreatePrice(
|
||||
registry, targetId, now, command.getPeriod().getValue(), isAnchorTenant);
|
||||
verifyUnitIsYears(command.getPeriod());
|
||||
int years = command.getPeriod().getValue();
|
||||
validateRegistrationPeriod(years);
|
||||
validateCreateCommandContactsAndNameservers(command, registry, domainName);
|
||||
LaunchCreateExtension launchCreate =
|
||||
eppInput.getSingleExtension(LaunchCreateExtension.class).get();
|
||||
validateLaunchCreateExtension(launchCreate, registry, domainName, now);
|
||||
// Superusers can create reserved domains, force creations on domains that require a claims
|
||||
// notice without specifying a claims key, and override blocks on registering premium domains.
|
||||
if (!isSuperuser) {
|
||||
verifyPremiumNameIsNotBlocked(targetId, now, clientId);
|
||||
prohibitLandrushIfExactlyOneSunrise(registry, now);
|
||||
if (!isAnchorTenant) {
|
||||
boolean isSunriseApplication = !launchCreate.getSignedMarks().isEmpty();
|
||||
verifyNotReserved(domainName, isSunriseApplication);
|
||||
}
|
||||
}
|
||||
Optional<FeeCreateCommandExtension> feeCreate =
|
||||
eppInput.getSingleExtension(FeeCreateCommandExtension.class);
|
||||
validateFeeChallenge(targetId, tld, clientId, now, feeCreate, feesAndCredits);
|
||||
Optional<SecDnsCreateExtension> secDnsCreate =
|
||||
validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class));
|
||||
flowCustomLogic.afterValidation(
|
||||
AfterValidationParameters.newBuilder()
|
||||
.setDomainName(domainName)
|
||||
.setYears(years)
|
||||
.build());
|
||||
DomainApplication newApplication =
|
||||
new DomainApplication.Builder()
|
||||
.setCreationTrid(trid)
|
||||
.setCreationClientId(clientId)
|
||||
.setPersistedCurrentSponsorClientId(clientId)
|
||||
.setRepoId(createDomainRepoId(ObjectifyService.allocateId(), tld))
|
||||
.setLaunchNotice(launchCreate == null ? null : launchCreate.getNotice())
|
||||
.setIdnTableName(idnTableName)
|
||||
.setPhase(launchCreate.getPhase())
|
||||
.setPeriod(command.getPeriod())
|
||||
.setApplicationStatus(ApplicationStatus.VALIDATED)
|
||||
.addStatusValue(StatusValue.PENDING_CREATE)
|
||||
.setDsData(secDnsCreate.isPresent() ? secDnsCreate.get().getDsData() : null)
|
||||
.setRegistrant(command.getRegistrant())
|
||||
.setAuthInfo(command.getAuthInfo())
|
||||
.setFullyQualifiedDomainName(targetId)
|
||||
.setNameservers(command.getNameservers())
|
||||
.setContacts(command.getContacts())
|
||||
.setEncodedSignedMarks(
|
||||
launchCreate
|
||||
.getSignedMarks()
|
||||
.stream()
|
||||
.map(abstractSignedMark -> (EncodedSignedMark) abstractSignedMark)
|
||||
.collect(toImmutableList()))
|
||||
.build();
|
||||
HistoryEntry historyEntry =
|
||||
buildHistoryEntry(newApplication.getRepoId(), command.getPeriod(), now);
|
||||
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
|
||||
entitiesToSave.add(
|
||||
newApplication,
|
||||
historyEntry,
|
||||
DomainApplicationIndex.createUpdatedInstance(newApplication),
|
||||
EppResourceIndex.create(Key.create(newApplication)));
|
||||
|
||||
EntityChanges entityChanges =
|
||||
flowCustomLogic.beforeSave(
|
||||
DomainApplicationCreateFlowCustomLogic.BeforeSaveParameters.newBuilder()
|
||||
.setNewApplication(newApplication)
|
||||
.setHistoryEntry(historyEntry)
|
||||
.setEntityChanges(
|
||||
EntityChanges.newBuilder().setSaves(entitiesToSave.build()).build())
|
||||
.setYears(years)
|
||||
.build());
|
||||
persistEntityChanges(entityChanges);
|
||||
BeforeResponseReturnData responseData =
|
||||
flowCustomLogic.beforeResponse(
|
||||
BeforeResponseParameters.newBuilder()
|
||||
.setResData(DomainCreateData.create(targetId, now, null))
|
||||
.setResponseExtensions(
|
||||
createResponseExtensions(
|
||||
newApplication.getForeignKey(),
|
||||
launchCreate.getPhase(),
|
||||
feeCreate,
|
||||
feesAndCredits))
|
||||
.build());
|
||||
return responseBuilder
|
||||
.setResData(responseData.resData())
|
||||
.setExtensions(responseData.responseExtensions())
|
||||
.build();
|
||||
}
|
||||
|
||||
private void validateLaunchCreateExtension(
|
||||
LaunchCreateExtension launchCreate,
|
||||
Registry registry,
|
||||
InternetDomainName domainName,
|
||||
DateTime now) throws EppException {
|
||||
verifyNoCodeMarks(launchCreate);
|
||||
boolean hasClaimsNotice = launchCreate.getNotice() != null;
|
||||
if (hasClaimsNotice) {
|
||||
verifyClaimsPeriodNotEnded(registry, now);
|
||||
}
|
||||
boolean isSunriseApplication = !launchCreate.getSignedMarks().isEmpty();
|
||||
if (!isSuperuser) { // Superusers can ignore the phase.
|
||||
verifyRegistryStateAllowsApplicationFlows(registry, now);
|
||||
verifyLaunchPhaseMatchesRegistryPhase(registry, launchCreate, now);
|
||||
}
|
||||
if (now.isBefore(registry.getClaimsPeriodEnd())) {
|
||||
verifyClaimsNoticeIfAndOnlyIfNeeded(domainName, isSunriseApplication, hasClaimsNotice);
|
||||
}
|
||||
TldState tldState = registry.getTldState(now);
|
||||
if (launchCreate.getSignedMarks().isEmpty()) {
|
||||
// During sunrise, a signed mark is required since only trademark holders are allowed to
|
||||
// create an application. However, we found no marks (ie, this was a landrush application).
|
||||
if (tldState == TldState.SUNRISE) {
|
||||
throw new LandrushApplicationDisallowedDuringSunriseException();
|
||||
}
|
||||
} else {
|
||||
if (hasClaimsNotice) { // Can't use a claims notice id with a signed mark.
|
||||
throw new NoticeCannotBeUsedWithSignedMarkException();
|
||||
}
|
||||
if (tldState == TldState.LANDRUSH) {
|
||||
throw new SunriseApplicationDisallowedDuringLandrushException();
|
||||
}
|
||||
}
|
||||
String domainLabel = domainName.parts().get(0);
|
||||
validateLaunchCreateNotice(launchCreate.getNotice(), domainLabel, isSuperuser, now);
|
||||
// If a signed mark was provided, then it must match the desired domain label.
|
||||
if (!launchCreate.getSignedMarks().isEmpty()) {
|
||||
tmchUtils.verifySignedMarks(launchCreate.getSignedMarks(), domainLabel, now);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prohibit creating a landrush application in LANDRUSH (but not in SUNRUSH) if there is exactly
|
||||
* one sunrise application for the same name.
|
||||
*/
|
||||
private void prohibitLandrushIfExactlyOneSunrise(Registry registry, DateTime now)
|
||||
throws UncontestedSunriseApplicationBlockedInLandrushException {
|
||||
if (registry.getTldState(now) == TldState.LANDRUSH) {
|
||||
ImmutableSet<DomainApplication> applications =
|
||||
loadActiveApplicationsByDomainName(targetId, now);
|
||||
if (applications.size() == 1
|
||||
&& getOnlyElement(applications).getPhase().equals(LaunchPhase.SUNRISE)) {
|
||||
throw new UncontestedSunriseApplicationBlockedInLandrushException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private HistoryEntry buildHistoryEntry(String repoId, Period period, DateTime now) {
|
||||
return historyBuilder
|
||||
.setType(HistoryEntry.Type.DOMAIN_APPLICATION_CREATE)
|
||||
.setPeriod(period)
|
||||
.setModificationTime(now)
|
||||
.setParent(Key.create(DomainApplication.class, repoId))
|
||||
.build();
|
||||
}
|
||||
|
||||
private static ImmutableList<ResponseExtension> createResponseExtensions(
|
||||
String applicationId,
|
||||
LaunchPhase launchPhase,
|
||||
Optional<? extends FeeTransformCommandExtension> feeCreate,
|
||||
FeesAndCredits feesAndCredits) {
|
||||
ImmutableList.Builder<ResponseExtension> responseExtensionsBuilder =
|
||||
new ImmutableList.Builder<>();
|
||||
responseExtensionsBuilder.add(new LaunchCreateResponseExtension.Builder()
|
||||
.setPhase(launchPhase)
|
||||
.setApplicationId(applicationId)
|
||||
.build());
|
||||
if (feeCreate.isPresent()) {
|
||||
responseExtensionsBuilder.add(createFeeCreateResponse(feeCreate.get(), feesAndCredits));
|
||||
}
|
||||
return responseExtensionsBuilder.build();
|
||||
}
|
||||
|
||||
/** Landrush applications are disallowed during sunrise. */
|
||||
static class LandrushApplicationDisallowedDuringSunriseException
|
||||
extends RequiredParameterMissingException {
|
||||
public LandrushApplicationDisallowedDuringSunriseException() {
|
||||
super("Landrush applications are disallowed during sunrise");
|
||||
}
|
||||
}
|
||||
|
||||
/** A notice cannot be specified when using a signed mark. */
|
||||
static class NoticeCannotBeUsedWithSignedMarkException extends CommandUseErrorException {
|
||||
public NoticeCannotBeUsedWithSignedMarkException() {
|
||||
super("A notice cannot be specified when using a signed mark");
|
||||
}
|
||||
}
|
||||
|
||||
/** Sunrise applications are disallowed during landrush. */
|
||||
static class SunriseApplicationDisallowedDuringLandrushException
|
||||
extends CommandUseErrorException {
|
||||
public SunriseApplicationDisallowedDuringLandrushException() {
|
||||
super("Sunrise applications are disallowed during landrush");
|
||||
}
|
||||
}
|
||||
|
||||
/** This name has already been claimed by a sunrise applicant. */
|
||||
static class UncontestedSunriseApplicationBlockedInLandrushException
|
||||
extends ObjectAlreadyExistsException {
|
||||
public UncontestedSunriseApplicationBlockedInLandrushException() {
|
||||
super("This name has already been claimed by a sunrise applicant");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
// 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.validateClientIsLoggedIn;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyExistence;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyApplicationDomainMatchesTargetId;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyLaunchPhaseMatchesRegistryPhase;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyRegistryStateAllowsApplicationFlows;
|
||||
import static google.registry.model.EppResourceUtils.loadDomainApplication;
|
||||
import static google.registry.model.ResourceTransferUtils.updateForeignKeyIndexDeletionTime;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
import google.registry.flows.ExtensionManager;
|
||||
import google.registry.flows.FlowModule.ApplicationId;
|
||||
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.model.domain.DomainApplication;
|
||||
import google.registry.model.domain.launch.LaunchDeleteExtension;
|
||||
import google.registry.model.domain.launch.LaunchPhase;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.eppcommon.AuthInfo;
|
||||
import google.registry.model.eppinput.EppInput;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.Registry.TldState;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* An EPP flow that deletes a domain application.
|
||||
*
|
||||
* @error {@link google.registry.flows.EppException.UnimplementedExtensionException}
|
||||
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException}
|
||||
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
|
||||
* @error {@link DomainApplicationDeleteFlow.SunriseApplicationCannotBeDeletedInLandrushException}
|
||||
* @error {@link DomainFlowUtils.ApplicationDomainNameMismatchException}
|
||||
* @error {@link DomainFlowUtils.BadCommandForRegistryPhaseException}
|
||||
* @error {@link DomainFlowUtils.LaunchPhaseMismatchException}
|
||||
* @error {@link DomainFlowUtils.NotAuthorizedForTldException}
|
||||
*/
|
||||
@ReportingSpec(ActivityReportField.DOMAIN_DELETE) // Applications are technically domains in EPP.
|
||||
public final class DomainApplicationDeleteFlow implements TransactionalFlow {
|
||||
|
||||
@Inject ExtensionManager extensionManager;
|
||||
@Inject EppInput eppInput;
|
||||
@Inject Optional<AuthInfo> authInfo;
|
||||
@Inject @ClientId String clientId;
|
||||
@Inject @TargetId String targetId;
|
||||
@Inject @ApplicationId String applicationId;
|
||||
@Inject @Superuser boolean isSuperuser;
|
||||
@Inject HistoryEntry.Builder historyBuilder;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject DomainApplicationDeleteFlow() {}
|
||||
|
||||
@Override
|
||||
public final EppResponse run() throws EppException {
|
||||
extensionManager.register(MetadataExtension.class, LaunchDeleteExtension.class);
|
||||
extensionManager.validate();
|
||||
validateClientIsLoggedIn(clientId);
|
||||
DateTime now = ofy().getTransactionTime();
|
||||
DomainApplication existingApplication = verifyExistence(
|
||||
DomainApplication.class, applicationId, loadDomainApplication(applicationId, now));
|
||||
verifyApplicationDomainMatchesTargetId(existingApplication, targetId);
|
||||
verifyOptionalAuthInfo(authInfo, existingApplication);
|
||||
String tld = existingApplication.getTld();
|
||||
if (!isSuperuser) {
|
||||
checkAllowedAccessToTld(clientId, tld);
|
||||
Registry registry = Registry.get(tld);
|
||||
verifyRegistryStateAllowsApplicationFlows(registry, now);
|
||||
verifyLaunchPhaseMatchesRegistryPhase(
|
||||
registry, eppInput.getSingleExtension(LaunchDeleteExtension.class).get(), now);
|
||||
verifyResourceOwnership(clientId, existingApplication);
|
||||
// Don't allow deleting a sunrise application during landrush.
|
||||
if (existingApplication.getPhase().equals(LaunchPhase.SUNRISE)
|
||||
&& registry.getTldState(now).equals(TldState.LANDRUSH)) {
|
||||
throw new SunriseApplicationCannotBeDeletedInLandrushException();
|
||||
}
|
||||
}
|
||||
DomainApplication newApplication = existingApplication.asBuilder()
|
||||
.setDeletionTime(now)
|
||||
.setStatusValues(null)
|
||||
.build();
|
||||
HistoryEntry historyEntry = historyBuilder
|
||||
.setType(HistoryEntry.Type.DOMAIN_APPLICATION_DELETE)
|
||||
.setModificationTime(now)
|
||||
.setParent(Key.create(existingApplication))
|
||||
.build();
|
||||
updateForeignKeyIndexDeletionTime(newApplication);
|
||||
ofy().save().<Object>entities(newApplication, historyEntry);
|
||||
return responseBuilder.build();
|
||||
}
|
||||
|
||||
/** A sunrise application cannot be deleted during landrush. */
|
||||
static class SunriseApplicationCannotBeDeletedInLandrushException
|
||||
extends StatusProhibitsOperationException {
|
||||
public SunriseApplicationCannotBeDeletedInLandrushException() {
|
||||
super("A sunrise application cannot be deleted during landrush");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,167 +0,0 @@
|
|||
// 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 com.google.common.collect.Sets.union;
|
||||
import static google.registry.flows.FlowUtils.unmarshalEpp;
|
||||
import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyExistence;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.addSecDnsExtensionIfPresent;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.loadForeignKeyedDesignatedContacts;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyApplicationDomainMatchesTargetId;
|
||||
import static google.registry.model.EppResourceUtils.loadDomainApplication;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.ParameterValuePolicyErrorException;
|
||||
import google.registry.flows.EppException.RequiredParameterMissingException;
|
||||
import google.registry.flows.ExtensionManager;
|
||||
import google.registry.flows.Flow;
|
||||
import google.registry.flows.FlowModule.ApplicationId;
|
||||
import google.registry.flows.FlowModule.ClientId;
|
||||
import google.registry.flows.FlowModule.TargetId;
|
||||
import google.registry.flows.annotations.ReportingSpec;
|
||||
import google.registry.model.domain.DomainApplication;
|
||||
import google.registry.model.domain.DomainCommand.Info;
|
||||
import google.registry.model.domain.DomainInfoData;
|
||||
import google.registry.model.domain.launch.LaunchInfoExtension;
|
||||
import google.registry.model.domain.launch.LaunchInfoResponseExtension;
|
||||
import google.registry.model.eppcommon.AuthInfo;
|
||||
import google.registry.model.eppinput.EppInput;
|
||||
import google.registry.model.eppinput.ResourceCommand;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
|
||||
import google.registry.model.mark.Mark;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.smd.EncodedSignedMark;
|
||||
import google.registry.model.smd.SignedMark;
|
||||
import google.registry.util.Clock;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* An EPP flow that returns information about a domain application.
|
||||
*
|
||||
* <p>Only the registrar that owns the application can see its info. The flow can optionally include
|
||||
* delegated hosts in its response.
|
||||
*
|
||||
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException}
|
||||
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
|
||||
* @error {@link DomainFlowUtils.ApplicationDomainNameMismatchException}
|
||||
* @error {@link DomainApplicationInfoFlow.ApplicationLaunchPhaseMismatchException}
|
||||
* @error {@link MissingApplicationIdException}
|
||||
*/
|
||||
@ReportingSpec(ActivityReportField.DOMAIN_INFO) // Applications are technically domains in EPP.
|
||||
public final class DomainApplicationInfoFlow implements Flow {
|
||||
|
||||
@Inject ResourceCommand resourceCommand;
|
||||
@Inject ExtensionManager extensionManager;
|
||||
@Inject EppInput eppInput;
|
||||
@Inject Optional<AuthInfo> authInfo;
|
||||
@Inject @ClientId String clientId;
|
||||
@Inject @TargetId String targetId;
|
||||
@Inject @ApplicationId String applicationId;
|
||||
@Inject Clock clock;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject DomainApplicationInfoFlow() {}
|
||||
|
||||
@Override
|
||||
public final EppResponse run() throws EppException {
|
||||
extensionManager.register(LaunchInfoExtension.class);
|
||||
extensionManager.validate();
|
||||
validateClientIsLoggedIn(clientId);
|
||||
if (applicationId.isEmpty()) {
|
||||
throw new MissingApplicationIdException();
|
||||
}
|
||||
DomainApplication application =
|
||||
verifyExistence(
|
||||
DomainApplication.class,
|
||||
applicationId,
|
||||
loadDomainApplication(applicationId, clock.nowUtc()));
|
||||
verifyApplicationDomainMatchesTargetId(application, targetId);
|
||||
verifyOptionalAuthInfo(authInfo, application);
|
||||
LaunchInfoExtension launchInfo = eppInput.getSingleExtension(LaunchInfoExtension.class).get();
|
||||
if (!application.getPhase().equals(launchInfo.getPhase())) {
|
||||
throw new ApplicationLaunchPhaseMismatchException();
|
||||
}
|
||||
// We don't support authInfo for applications, so if it's another registrar always fail.
|
||||
verifyResourceOwnership(clientId, application);
|
||||
boolean showDelegatedHosts = ((Info) resourceCommand).getHostsRequest().requestDelegated();
|
||||
// Prefetch all referenced resources. Calling values() blocks until loading is done.
|
||||
ofy().load()
|
||||
.values(union(application.getNameservers(), application.getReferencedContacts())).values();
|
||||
return responseBuilder
|
||||
.setResData(DomainInfoData.newBuilder()
|
||||
.setFullyQualifiedDomainName(application.getFullyQualifiedDomainName())
|
||||
.setRepoId(application.getRepoId())
|
||||
.setStatusValues(application.getStatusValues())
|
||||
.setRegistrant(
|
||||
ofy().load().key(application.getRegistrant()).now().getContactId())
|
||||
.setContacts(loadForeignKeyedDesignatedContacts(application.getContacts()))
|
||||
.setNameservers(showDelegatedHosts
|
||||
? application.loadNameserverFullyQualifiedHostNames()
|
||||
: null)
|
||||
.setCurrentSponsorClientId(application.getCurrentSponsorClientId())
|
||||
.setCreationClientId(application.getCreationClientId())
|
||||
.setCreationTime(application.getCreationTime())
|
||||
.setLastEppUpdateClientId(application.getLastEppUpdateClientId())
|
||||
.setLastEppUpdateTime(application.getLastEppUpdateTime())
|
||||
.setAuthInfo(application.getAuthInfo())
|
||||
.build())
|
||||
.setExtensions(getDomainResponseExtensions(application, launchInfo))
|
||||
.build();
|
||||
}
|
||||
|
||||
ImmutableList<ResponseExtension> getDomainResponseExtensions(
|
||||
DomainApplication application, LaunchInfoExtension launchInfo) {
|
||||
ImmutableList.Builder<Mark> marksBuilder = new ImmutableList.Builder<>();
|
||||
if (Boolean.TRUE.equals(launchInfo.getIncludeMark())) { // Default to false.
|
||||
for (EncodedSignedMark encodedMark : application.getEncodedSignedMarks()) {
|
||||
try {
|
||||
marksBuilder.add(unmarshalEpp(SignedMark.class, encodedMark.getBytes()).getMark());
|
||||
} catch (EppException e) {
|
||||
// This is a serious error; don't let the benign EppException propagate.
|
||||
throw new IllegalStateException("Could not decode a stored encoded signed mark", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImmutableList.Builder<ResponseExtension> extensions = new ImmutableList.Builder<>();
|
||||
extensions.add(new LaunchInfoResponseExtension.Builder()
|
||||
.setPhase(application.getPhase())
|
||||
.setApplicationId(application.getForeignKey())
|
||||
.setApplicationStatus(application.getApplicationStatus())
|
||||
.setMarks(marksBuilder.build())
|
||||
.build());
|
||||
addSecDnsExtensionIfPresent(extensions, application.getDsData());
|
||||
return extensions.build();
|
||||
}
|
||||
|
||||
/** Application id is required. */
|
||||
static class MissingApplicationIdException extends RequiredParameterMissingException {
|
||||
public MissingApplicationIdException() {
|
||||
super("Application id is required");
|
||||
}
|
||||
}
|
||||
|
||||
/** Declared launch extension phase does not match phase of the application. */
|
||||
static class ApplicationLaunchPhaseMismatchException extends ParameterValuePolicyErrorException {
|
||||
public ApplicationLaunchPhaseMismatchException() {
|
||||
super("Declared launch extension phase does not match the phase of the application");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,263 +0,0 @@
|
|||
// 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 com.google.common.base.CaseFormat.LOWER_CAMEL;
|
||||
import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
|
||||
import static com.google.common.base.MoreObjects.firstNonNull;
|
||||
import static com.google.common.collect.Sets.union;
|
||||
import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
|
||||
import static google.registry.flows.ResourceFlowUtils.checkSameValuesNotAddedAndRemoved;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyAllStatusesAreClientSettable;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyExistence;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.updateDsData;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateContactsHaveTypes;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateDsData;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateFeesAckedIfPresent;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateNameserversAllowedOnDomain;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateNameserversAllowedOnTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateNameserversCountForTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateNoDuplicateContacts;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateRegistrantAllowedOnTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateRequiredContactsPresent;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyApplicationDomainMatchesTargetId;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyClientUpdateNotProhibited;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete;
|
||||
import static google.registry.model.EppResourceUtils.loadDomainApplication;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.util.CollectionUtils.nullToEmpty;
|
||||
|
||||
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.flows.EppException;
|
||||
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
import google.registry.flows.ExtensionManager;
|
||||
import google.registry.flows.FlowModule.ApplicationId;
|
||||
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.model.ImmutableObject;
|
||||
import google.registry.model.domain.DomainApplication;
|
||||
import google.registry.model.domain.DomainCommand.Update;
|
||||
import google.registry.model.domain.DomainCommand.Update.AddRemove;
|
||||
import google.registry.model.domain.DomainCommand.Update.Change;
|
||||
import google.registry.model.domain.fee.FeeUpdateCommandExtension;
|
||||
import google.registry.model.domain.launch.ApplicationStatus;
|
||||
import google.registry.model.domain.launch.LaunchUpdateExtension;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.domain.secdns.SecDnsUpdateExtension;
|
||||
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.EppResponse;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* An EPP flow that updates a domain application.
|
||||
*
|
||||
* <p>Updates can change contacts, nameservers and delegation signer data of an application. Updates
|
||||
* cannot change the domain name that is being applied for.
|
||||
*
|
||||
* @error {@link google.registry.flows.EppException.UnimplementedExtensionException}
|
||||
* @error {@link google.registry.flows.ResourceFlowUtils.AddRemoveSameValueException}
|
||||
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException}
|
||||
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
|
||||
* @error {@link google.registry.flows.ResourceFlowUtils.StatusNotClientSettableException}
|
||||
* @error {@link google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException}
|
||||
* @error {@link google.registry.flows.exceptions.ResourceStatusProhibitsOperationException}
|
||||
* @error {@link DomainFlowUtils.ApplicationDomainNameMismatchException}
|
||||
* @error {@link DomainFlowUtils.DuplicateContactForRoleException}
|
||||
* @error {@link DomainFlowUtils.EmptySecDnsUpdateException}
|
||||
* @error {@link DomainFlowUtils.FeesMismatchException}
|
||||
* @error {@link DomainFlowUtils.LinkedResourcesDoNotExistException}
|
||||
* @error {@link DomainFlowUtils.MaxSigLifeChangeNotSupportedException}
|
||||
* @error {@link DomainFlowUtils.MissingAdminContactException}
|
||||
* @error {@link DomainFlowUtils.MissingContactTypeException}
|
||||
* @error {@link DomainFlowUtils.MissingTechnicalContactException}
|
||||
* @error {@link DomainFlowUtils.NameserversNotAllowedForTldException}
|
||||
* @error {@link DomainFlowUtils.NameserversNotSpecifiedForTldWithNameserverWhitelistException}
|
||||
* @error {@link DomainFlowUtils.NameserversNotAllowedForDomainException}
|
||||
* @error {@link DomainFlowUtils.NameserversNotSpecifiedForNameserverRestrictedDomainException}
|
||||
* @error {@link DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link DomainFlowUtils.RegistrantNotAllowedException}
|
||||
* @error {@link DomainFlowUtils.SecDnsAllUsageException}
|
||||
* @error {@link DomainFlowUtils.TooManyDsRecordsException}
|
||||
* @error {@link DomainFlowUtils.TooManyNameserversException}
|
||||
* @error {@link DomainFlowUtils.UrgentAttributeNotSupportedException}
|
||||
* @error {@link DomainApplicationUpdateFlow.ApplicationStatusProhibitsUpdateException}
|
||||
*/
|
||||
@ReportingSpec(ActivityReportField.DOMAIN_UPDATE) // Applications are technically domains in EPP.
|
||||
public class DomainApplicationUpdateFlow implements TransactionalFlow {
|
||||
|
||||
/**
|
||||
* Note that CLIENT_UPDATE_PROHIBITED is intentionally not in this list. This is because it
|
||||
* requires special checking, since you must be able to clear the status off the object with an
|
||||
* update.
|
||||
*/
|
||||
private static final ImmutableSet<StatusValue> UPDATE_DISALLOWED_STATUSES =
|
||||
Sets.immutableEnumSet(
|
||||
StatusValue.PENDING_DELETE,
|
||||
StatusValue.SERVER_UPDATE_PROHIBITED);
|
||||
|
||||
private static final ImmutableSet<ApplicationStatus> UPDATE_DISALLOWED_APPLICATION_STATUSES =
|
||||
Sets.immutableEnumSet(
|
||||
ApplicationStatus.INVALID,
|
||||
ApplicationStatus.REJECTED,
|
||||
ApplicationStatus.ALLOCATED);
|
||||
|
||||
@Inject ResourceCommand resourceCommand;
|
||||
@Inject ExtensionManager extensionManager;
|
||||
@Inject EppInput eppInput;
|
||||
@Inject Optional<AuthInfo> authInfo;
|
||||
@Inject @ClientId String clientId;
|
||||
@Inject @TargetId String targetId;
|
||||
@Inject @ApplicationId String applicationId;
|
||||
@Inject @Superuser boolean isSuperuser;
|
||||
@Inject HistoryEntry.Builder historyBuilder;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
@Inject DomainApplicationUpdateFlow() {}
|
||||
|
||||
@Override
|
||||
public final EppResponse run() throws EppException {
|
||||
extensionManager.register(
|
||||
FeeUpdateCommandExtension.class,
|
||||
LaunchUpdateExtension.class,
|
||||
MetadataExtension.class,
|
||||
SecDnsUpdateExtension.class);
|
||||
extensionManager.validate();
|
||||
validateClientIsLoggedIn(clientId);
|
||||
DateTime now = ofy().getTransactionTime();
|
||||
Update command = cloneAndLinkReferences((Update) resourceCommand, now);
|
||||
DomainApplication existingApplication = verifyExistence(
|
||||
DomainApplication.class, applicationId, loadDomainApplication(applicationId, now));
|
||||
verifyApplicationDomainMatchesTargetId(existingApplication, targetId);
|
||||
verifyNoDisallowedStatuses(existingApplication, UPDATE_DISALLOWED_STATUSES);
|
||||
verifyOptionalAuthInfo(authInfo, existingApplication);
|
||||
verifyUpdateAllowed(existingApplication, command, now);
|
||||
HistoryEntry historyEntry = buildHistoryEntry(existingApplication, now);
|
||||
DomainApplication newApplication = updateApplication(existingApplication, command, now);
|
||||
validateNewApplication(newApplication);
|
||||
ofy().save().<ImmutableObject>entities(newApplication, historyEntry);
|
||||
return responseBuilder.build();
|
||||
}
|
||||
|
||||
protected final void verifyUpdateAllowed(
|
||||
DomainApplication existingApplication, Update command, DateTime now) throws EppException {
|
||||
AddRemove add = command.getInnerAdd();
|
||||
AddRemove remove = command.getInnerRemove();
|
||||
String tld = existingApplication.getTld();
|
||||
if (!isSuperuser) {
|
||||
verifyResourceOwnership(clientId, existingApplication);
|
||||
verifyClientUpdateNotProhibited(command, existingApplication);
|
||||
verifyAllStatusesAreClientSettable(union(add.getStatusValues(), remove.getStatusValues()));
|
||||
checkAllowedAccessToTld(clientId, tld);
|
||||
}
|
||||
if (UPDATE_DISALLOWED_APPLICATION_STATUSES
|
||||
.contains(existingApplication.getApplicationStatus())) {
|
||||
throw new ApplicationStatusProhibitsUpdateException(
|
||||
existingApplication.getApplicationStatus());
|
||||
}
|
||||
Registry registry = Registry.get(tld);
|
||||
FeesAndCredits feesAndCredits =
|
||||
pricingLogic.getApplicationUpdatePrice(registry, existingApplication, now);
|
||||
Optional<FeeUpdateCommandExtension> feeUpdate =
|
||||
eppInput.getSingleExtension(FeeUpdateCommandExtension.class);
|
||||
validateFeesAckedIfPresent(feeUpdate, feesAndCredits);
|
||||
verifyNotInPendingDelete(
|
||||
add.getContacts(),
|
||||
command.getInnerChange().getRegistrant(),
|
||||
add.getNameservers());
|
||||
validateContactsHaveTypes(add.getContacts());
|
||||
validateContactsHaveTypes(remove.getContacts());
|
||||
validateRegistrantAllowedOnTld(tld, command.getInnerChange().getRegistrantContactId());
|
||||
validateNameserversAllowedOnTld(
|
||||
tld, add.getNameserverFullyQualifiedHostNames());
|
||||
InternetDomainName domainName =
|
||||
InternetDomainName.from(existingApplication.getFullyQualifiedDomainName());
|
||||
validateNameserversAllowedOnDomain(
|
||||
domainName, nullToEmpty(add.getNameserverFullyQualifiedHostNames()));
|
||||
}
|
||||
|
||||
private HistoryEntry buildHistoryEntry(DomainApplication existingApplication, DateTime now) {
|
||||
return historyBuilder
|
||||
.setType(HistoryEntry.Type.DOMAIN_APPLICATION_UPDATE)
|
||||
.setModificationTime(now)
|
||||
.setParent(Key.create(existingApplication))
|
||||
.build();
|
||||
}
|
||||
|
||||
private DomainApplication updateApplication(
|
||||
DomainApplication application, Update command, DateTime now) throws EppException {
|
||||
AddRemove add = command.getInnerAdd();
|
||||
AddRemove remove = command.getInnerRemove();
|
||||
checkSameValuesNotAddedAndRemoved(add.getNameservers(), remove.getNameservers());
|
||||
checkSameValuesNotAddedAndRemoved(add.getContacts(), remove.getContacts());
|
||||
checkSameValuesNotAddedAndRemoved(add.getStatusValues(), remove.getStatusValues());
|
||||
Change change = command.getInnerChange();
|
||||
Optional<SecDnsUpdateExtension> secDnsUpdate =
|
||||
eppInput.getSingleExtension(SecDnsUpdateExtension.class);
|
||||
return application.asBuilder()
|
||||
// Handle the secDNS extension.
|
||||
.setDsData(secDnsUpdate.isPresent()
|
||||
? updateDsData(application.getDsData(), secDnsUpdate.get())
|
||||
: application.getDsData())
|
||||
.setLastEppUpdateTime(now)
|
||||
.setLastEppUpdateClientId(clientId)
|
||||
.addStatusValues(add.getStatusValues())
|
||||
.removeStatusValues(remove.getStatusValues())
|
||||
.addNameservers(add.getNameservers())
|
||||
.removeNameservers(remove.getNameservers())
|
||||
.addContacts(add.getContacts())
|
||||
.removeContacts(remove.getContacts())
|
||||
.setRegistrant(firstNonNull(change.getRegistrant(), application.getRegistrant()))
|
||||
.setAuthInfo(firstNonNull(change.getAuthInfo(), application.getAuthInfo()))
|
||||
.build();
|
||||
}
|
||||
|
||||
private void validateNewApplication(DomainApplication newApplication) throws EppException {
|
||||
validateNoDuplicateContacts(newApplication.getContacts());
|
||||
validateRequiredContactsPresent(newApplication.getRegistrant(), newApplication.getContacts());
|
||||
validateDsData(newApplication.getDsData());
|
||||
validateNameserversCountForTld(
|
||||
newApplication.getTld(),
|
||||
InternetDomainName.from(newApplication.getFullyQualifiedDomainName()),
|
||||
newApplication.getNameservers().size());
|
||||
}
|
||||
|
||||
/** Application status prohibits this domain update. */
|
||||
static class ApplicationStatusProhibitsUpdateException extends StatusProhibitsOperationException {
|
||||
public ApplicationStatusProhibitsUpdateException(ApplicationStatus status) {
|
||||
super(String.format(
|
||||
"Applications in state %s can not be updated",
|
||||
UPPER_UNDERSCORE.to(LOWER_CAMEL, status.name())));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,13 +24,11 @@ import static google.registry.flows.domain.DomainFlowUtils.validateDomainName;
|
|||
import static google.registry.flows.domain.DomainFlowUtils.validateDomainNameWithIdnTables;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPredelegation;
|
||||
import static google.registry.model.EppResourceUtils.checkResourcesExist;
|
||||
import static google.registry.model.index.DomainApplicationIndex.loadActiveApplicationsByDomainName;
|
||||
import static google.registry.model.registry.label.ReservationType.getTypeOfHighestSeverity;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.flows.EppException;
|
||||
|
@ -58,7 +56,6 @@ import google.registry.model.eppoutput.CheckData.DomainCheckData;
|
|||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.Registry.TldState;
|
||||
import google.registry.model.registry.label.ReservationType;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.util.Clock;
|
||||
|
@ -99,14 +96,6 @@ import org.joda.time.DateTime;
|
|||
@ReportingSpec(ActivityReportField.DOMAIN_CHECK)
|
||||
public final class DomainCheckFlow implements Flow {
|
||||
|
||||
/**
|
||||
* The TLD states during which we want to report a domain with pending applications as
|
||||
* unavailable.
|
||||
*/
|
||||
private static final ImmutableSet<TldState> PENDING_ALLOCATION_TLD_STATES =
|
||||
Sets.immutableEnumSet(
|
||||
TldState.GENERAL_AVAILABILITY, TldState.START_DATE_SUNRISE, TldState.QUIET_PERIOD);
|
||||
|
||||
@Inject ResourceCommand resourceCommand;
|
||||
@Inject ExtensionManager extensionManager;
|
||||
@Inject EppInput eppInput;
|
||||
|
@ -166,7 +155,7 @@ public final class DomainCheckFlow implements Flow {
|
|||
ImmutableList.Builder<DomainCheck> checks = new ImmutableList.Builder<>();
|
||||
for (String targetId : targetIds) {
|
||||
Optional<String> message =
|
||||
getMessageForCheck(domainNames.get(targetId), existingIds, tokenCheckResults, now);
|
||||
getMessageForCheck(domainNames.get(targetId), existingIds, tokenCheckResults);
|
||||
checks.add(DomainCheck.create(!message.isPresent(), targetId, message.orElse(null)));
|
||||
}
|
||||
BeforeResponseReturnData responseData =
|
||||
|
@ -185,18 +174,10 @@ public final class DomainCheckFlow implements Flow {
|
|||
private Optional<String> getMessageForCheck(
|
||||
InternetDomainName domainName,
|
||||
Set<String> existingIds,
|
||||
ImmutableMap<InternetDomainName, String> tokenCheckResults,
|
||||
DateTime now) {
|
||||
ImmutableMap<InternetDomainName, String> tokenCheckResults) {
|
||||
if (existingIds.contains(domainName.toString())) {
|
||||
return Optional.of("In use");
|
||||
}
|
||||
Registry registry = Registry.get(domainName.parent().toString());
|
||||
if (PENDING_ALLOCATION_TLD_STATES.contains(registry.getTldState(now))
|
||||
&& loadActiveApplicationsByDomainName(domainName.toString(), now)
|
||||
.stream()
|
||||
.anyMatch(input -> !input.getApplicationStatus().isFinalStatus())) {
|
||||
return Optional.of("Pending allocation");
|
||||
}
|
||||
ImmutableSet<ReservationType> reservationTypes = getReservationTypes(domainName);
|
||||
if (!reservationTypes.isEmpty()) {
|
||||
return Optional.of(getTypeOfHighestSeverity(reservationTypes).getMessageForCheck());
|
||||
|
|
|
@ -44,7 +44,6 @@ import google.registry.model.eppinput.EppInput;
|
|||
import google.registry.model.eppinput.ResourceCommand;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.Registry.TldState;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tmch.ClaimsListShard;
|
||||
import google.registry.util.Clock;
|
||||
|
@ -63,7 +62,6 @@ import org.joda.time.DateTime;
|
|||
* @error {@link DomainFlowUtils.ClaimsPeriodEndedException}
|
||||
* @error {@link DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link DomainFlowUtils.TldDoesNotExistException}
|
||||
* @error {@link DomainClaimsCheckNotAllowedInSunrise}
|
||||
* @error {@link DomainClaimsCheckNotAllowedWithAllocationTokens}
|
||||
*/
|
||||
@ReportingSpec(ActivityReportField.DOMAIN_CHECK) // Claims check is a special domain check.
|
||||
|
@ -104,9 +102,6 @@ public final class DomainClaimsCheckFlow implements Flow {
|
|||
Registry registry = Registry.get(tld);
|
||||
DateTime now = clock.nowUtc();
|
||||
verifyNotInPredelegation(registry, now);
|
||||
if (registry.getTldState(now) == TldState.SUNRISE) {
|
||||
throw new DomainClaimsCheckNotAllowedInSunrise();
|
||||
}
|
||||
verifyClaimsPeriodNotEnded(registry, now);
|
||||
}
|
||||
}
|
||||
|
@ -120,13 +115,6 @@ public final class DomainClaimsCheckFlow implements Flow {
|
|||
.build();
|
||||
}
|
||||
|
||||
/** Claims checks are not allowed during sunrise. */
|
||||
static class DomainClaimsCheckNotAllowedInSunrise extends CommandUseErrorException {
|
||||
public DomainClaimsCheckNotAllowedInSunrise() {
|
||||
super("Claims checks are not allowed during sunrise");
|
||||
}
|
||||
}
|
||||
|
||||
/** Claims checks are not allowed with allocation tokens. */
|
||||
static class DomainClaimsCheckNotAllowedWithAllocationTokens extends CommandUseErrorException {
|
||||
public DomainClaimsCheckNotAllowedWithAllocationTokens() {
|
||||
|
|
|
@ -45,26 +45,21 @@ 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;
|
||||
|
@ -81,7 +76,6 @@ 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;
|
||||
|
@ -130,7 +124,6 @@ import org.joda.time.Duration;
|
|||
* @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}
|
||||
|
@ -191,19 +184,6 @@ import org.joda.time.Duration;
|
|||
@ReportingSpec(ActivityReportField.DOMAIN_CREATE)
|
||||
public class DomainCreateFlow implements TransactionalFlow {
|
||||
|
||||
/**
|
||||
* States when the TLD is in sunrise.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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<TldState> SUNRISE_STATES =
|
||||
Sets.immutableEnumSet(SUNRISE, SUNRUSH, START_DATE_SUNRISE);
|
||||
|
||||
/** Anchor tenant creates should always be for 2 years, since they get 2 years free. */
|
||||
private static final int ANCHOR_TENANT_CREATE_VALID_YEARS = 2;
|
||||
|
||||
|
@ -260,7 +240,7 @@ public class DomainCreateFlow implements TransactionalFlow {
|
|||
verifyNoCodeMarks(launchCreate.get());
|
||||
validateLaunchCreateNotice(launchCreate.get().getNotice(), domainLabel, isSuperuser, now);
|
||||
}
|
||||
boolean isSunriseCreate = hasSignedMarks && SUNRISE_STATES.contains(tldState);
|
||||
boolean isSunriseCreate = hasSignedMarks && tldState == START_DATE_SUNRISE;
|
||||
Optional<AllocationToken> allocationToken =
|
||||
verifyAllocationTokenIfPresent(command, registry, clientId, now);
|
||||
boolean isAnchorTenant =
|
||||
|
@ -275,6 +255,7 @@ public class DomainCreateFlow implements TransactionalFlow {
|
|||
// registering premium domains.
|
||||
if (!isSuperuser) {
|
||||
checkAllowedAccessToTld(clientId, registry.getTldStr());
|
||||
verifyIsGaOrIsSpecialCase(tldState, isAnchorTenant, hasSignedMarks);
|
||||
if (launchCreate.isPresent()) {
|
||||
verifyLaunchPhaseMatchesRegistryPhase(registry, launchCreate.get(), now);
|
||||
}
|
||||
|
@ -288,8 +269,6 @@ public class DomainCreateFlow implements TransactionalFlow {
|
|||
verifyClaimsNoticeIfAndOnlyIfNeeded(domainName, hasSignedMarks, hasClaimsNotice);
|
||||
}
|
||||
verifyPremiumNameIsNotBlocked(targetId, now, clientId);
|
||||
verifyNoOpenApplications(now);
|
||||
verifyIsGaOrIsSpecialCase(tldState, isAnchorTenant, hasSignedMarks);
|
||||
verifySignedMarkOnlyInSunrise(hasSignedMarks, tldState);
|
||||
}
|
||||
String signedMarkId = null;
|
||||
|
@ -418,7 +397,7 @@ public class DomainCreateFlow implements TransactionalFlow {
|
|||
*/
|
||||
static void verifySignedMarkOnlyInSunrise(boolean hasSignedMarks, TldState tldState)
|
||||
throws EppException {
|
||||
if (hasSignedMarks && !SUNRISE_STATES.contains(tldState)) {
|
||||
if (hasSignedMarks && tldState != START_DATE_SUNRISE) {
|
||||
throw new SignedMarksOnlyDuringSunriseException();
|
||||
}
|
||||
}
|
||||
|
@ -434,15 +413,6 @@ public class DomainCreateFlow implements TransactionalFlow {
|
|||
}
|
||||
}
|
||||
|
||||
/** 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.
|
||||
*
|
||||
|
@ -465,7 +435,7 @@ public class DomainCreateFlow implements TransactionalFlow {
|
|||
return;
|
||||
}
|
||||
|
||||
// During START_DATE_SUNRISE, only allow registration with a signed marks.
|
||||
// During START_DATE_SUNRISE, only allow registration with signed marks.
|
||||
if (START_DATE_SUNRISE.equals(tldState)) {
|
||||
if (!hasSignedMarks) {
|
||||
throw new MustHaveSignedMarksInCurrentPhaseException();
|
||||
|
@ -626,13 +596,6 @@ public class DomainCreateFlow implements TransactionalFlow {
|
|||
}
|
||||
}
|
||||
|
||||
/** There is an open application for this domain. */
|
||||
static class DomainHasOpenApplicationsException extends StatusProhibitsOperationException {
|
||||
public DomainHasOpenApplicationsException() {
|
||||
super("There is an open application for this domain");
|
||||
}
|
||||
}
|
||||
|
||||
/** The current registry phase does not allow for general registrations. */
|
||||
static class NoGeneralRegistrationsInCurrentPhaseException extends CommandUseErrorException {
|
||||
public NoGeneralRegistrationsInCurrentPhaseException() {
|
||||
|
|
|
@ -26,6 +26,8 @@ import static google.registry.model.domain.DomainResource.MAX_REGISTRATION_YEARS
|
|||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.model.registry.Registries.findTldForName;
|
||||
import static google.registry.model.registry.Registries.getTlds;
|
||||
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.label.ReservationType.FULLY_BLOCKED;
|
||||
import static google.registry.model.registry.label.ReservationType.NAMESERVER_RESTRICTED;
|
||||
import static google.registry.model.registry.label.ReservationType.RESERVED_FOR_ANCHOR_TENANT;
|
||||
|
@ -69,7 +71,6 @@ import google.registry.model.billing.BillingEvent.Recurring;
|
|||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.DesignatedContact.Type;
|
||||
import google.registry.model.domain.DomainApplication;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.DomainCommand.Create;
|
||||
import google.registry.model.domain.DomainCommand.CreateOrUpdate;
|
||||
|
@ -132,14 +133,11 @@ import org.joda.time.Duration;
|
|||
public class DomainFlowUtils {
|
||||
|
||||
/** Map from launch phases to the allowed tld states. */
|
||||
private static final ImmutableMultimap<LaunchPhase, TldState> LAUNCH_PHASE_TO_TLD_STATES =
|
||||
new ImmutableMultimap.Builder<LaunchPhase, TldState>()
|
||||
.put(LaunchPhase.SUNRISE, TldState.SUNRISE)
|
||||
.put(LaunchPhase.SUNRUSH, TldState.SUNRUSH)
|
||||
.put(LaunchPhase.LANDRUSH, TldState.LANDRUSH)
|
||||
.put(LaunchPhase.CLAIMS, TldState.GENERAL_AVAILABILITY)
|
||||
.put(LaunchPhase.SUNRISE, TldState.START_DATE_SUNRISE)
|
||||
.put(LaunchPhase.OPEN, TldState.GENERAL_AVAILABILITY)
|
||||
private static final ImmutableMap<LaunchPhase, TldState> LAUNCH_PHASE_TO_TLD_STATES =
|
||||
new ImmutableMap.Builder<LaunchPhase, TldState>()
|
||||
.put(LaunchPhase.CLAIMS, GENERAL_AVAILABILITY)
|
||||
.put(LaunchPhase.SUNRISE, START_DATE_SUNRISE)
|
||||
.put(LaunchPhase.OPEN, GENERAL_AVAILABILITY)
|
||||
.build();
|
||||
|
||||
/** Reservation types that are only allowed in sunrise by policy. */
|
||||
|
@ -154,14 +152,6 @@ public class DomainFlowUtils {
|
|||
+ "delegated. Please visit https://www.icann.org/namecollision for more information on "
|
||||
+ "name collision.";
|
||||
|
||||
/** Non-sunrise tld states. */
|
||||
private static final ImmutableSet<TldState> DISALLOWED_TLD_STATES_FOR_APPLICATION_FLOWS =
|
||||
Sets.immutableEnumSet(
|
||||
TldState.PREDELEGATION,
|
||||
TldState.QUIET_PERIOD,
|
||||
TldState.START_DATE_SUNRISE,
|
||||
TldState.GENERAL_AVAILABILITY);
|
||||
|
||||
/** Strict validator for ascii lowercase letters, digits, and "-", allowing "." as a separator */
|
||||
private static final CharMatcher ALLOWED_CHARS =
|
||||
CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('0', '9').or(CharMatcher.anyOf("-.")));
|
||||
|
@ -474,21 +464,14 @@ public class DomainFlowUtils {
|
|||
/** Verifies that a launch extension's specified phase matches the specified registry's phase. */
|
||||
static void verifyLaunchPhaseMatchesRegistryPhase(
|
||||
Registry registry, LaunchExtension launchExtension, DateTime now) throws EppException {
|
||||
if (!LAUNCH_PHASE_TO_TLD_STATES.containsEntry(
|
||||
launchExtension.getPhase(), registry.getTldState(now))) {
|
||||
if (!LAUNCH_PHASE_TO_TLD_STATES.containsKey(launchExtension.getPhase())
|
||||
|| LAUNCH_PHASE_TO_TLD_STATES.get(launchExtension.getPhase())
|
||||
!= registry.getTldState(now)) {
|
||||
// No launch operations are allowed during the quiet period or predelegation.
|
||||
throw new LaunchPhaseMismatchException();
|
||||
}
|
||||
}
|
||||
|
||||
/** Verifies that an application's domain name matches the target id (from a command). */
|
||||
static void verifyApplicationDomainMatchesTargetId(DomainApplication application, String targetId)
|
||||
throws EppException {
|
||||
if (!application.getFullyQualifiedDomainName().equals(targetId)) {
|
||||
throw new ApplicationDomainNameMismatchException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that a domain name is allowed to be delegated to the given client id. The only case
|
||||
* where it would not be allowed is if domain name is premium, and premium names are blocked by
|
||||
|
@ -601,7 +584,7 @@ public class DomainFlowUtils {
|
|||
String domainNameString = domain.toString();
|
||||
Registry registry = Registry.get(domain.parent().toString());
|
||||
int years = verifyUnitIsYears(feeRequest.getPeriod()).getValue();
|
||||
boolean isSunrise = registry.getTldState(now).equals(TldState.SUNRISE);
|
||||
boolean isSunrise = (registry.getTldState(now) == START_DATE_SUNRISE);
|
||||
|
||||
if (feeRequest.getPhase() != null || feeRequest.getSubphase() != null) {
|
||||
throw new FeeChecksDontSupportPhasesException();
|
||||
|
@ -822,9 +805,8 @@ public class DomainFlowUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* 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}).
|
||||
* Check that a new registration period (via a 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
|
||||
*/
|
||||
|
@ -879,7 +861,7 @@ public class DomainFlowUtils {
|
|||
return ImmutableSet.copyOf(union(difference(oldDsData, toRemove), toAdd));
|
||||
}
|
||||
|
||||
/** If a domain or application has "clientUpdateProhibited" set, updates must clear it or fail. */
|
||||
/** If a domain "clientUpdateProhibited" set, updates must clear it or fail. */
|
||||
static void verifyClientUpdateNotProhibited(Update command, DomainBase existingResource)
|
||||
throws ResourceHasClientUpdateProhibitedException {
|
||||
if (existingResource.getStatusValues().contains(StatusValue.CLIENT_UPDATE_PROHIBITED)
|
||||
|
@ -905,14 +887,6 @@ public class DomainFlowUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/** Check that the registry phase is not incompatible with launch extension flows. */
|
||||
static void verifyRegistryStateAllowsApplicationFlows(Registry registry, DateTime now)
|
||||
throws BadCommandForRegistryPhaseException {
|
||||
if (DISALLOWED_TLD_STATES_FOR_APPLICATION_FLOWS.contains(registry.getTldState(now))) {
|
||||
throw new BadCommandForRegistryPhaseException();
|
||||
}
|
||||
}
|
||||
|
||||
/** Check that the registry phase is not predelegation, during which some flows are forbidden. */
|
||||
public static void verifyNotInPredelegation(Registry registry, DateTime now)
|
||||
throws BadCommandForRegistryPhaseException {
|
||||
|
@ -921,7 +895,7 @@ public class DomainFlowUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/** Validate the contacts and nameservers specified in a domain or application create command. */
|
||||
/** Validate the contacts and nameservers specified in a domain create command. */
|
||||
static void validateCreateCommandContactsAndNameservers(
|
||||
Create command, Registry registry, InternetDomainName domainName) throws EppException {
|
||||
verifyNotInPendingDelete(
|
||||
|
@ -993,7 +967,7 @@ public class DomainFlowUtils {
|
|||
|
||||
/**
|
||||
* Check that if there's a claims notice it's on the claims list, and that if there's not one it's
|
||||
* not on the claims list and is a sunrise application.
|
||||
* not on the claims list.
|
||||
*/
|
||||
static void verifyClaimsNoticeIfAndOnlyIfNeeded(
|
||||
InternetDomainName domainName, boolean hasSignedMarks, boolean hasClaimsNotice)
|
||||
|
@ -1016,7 +990,7 @@ public class DomainFlowUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/** Create a response extension listing the fees on a domain or application create. */
|
||||
/** Create a response extension listing the fees on a domain create. */
|
||||
static FeeTransformResponseExtension createFeeCreateResponse(
|
||||
FeeTransformCommandExtension feeCreate, FeesAndCredits feesAndCredits) {
|
||||
return feeCreate
|
||||
|
@ -1185,13 +1159,6 @@ public class DomainFlowUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/** Application referenced does not match specified domain name. */
|
||||
static class ApplicationDomainNameMismatchException extends ParameterValuePolicyErrorException {
|
||||
public ApplicationDomainNameMismatchException() {
|
||||
super("Application referenced does not match specified domain name");
|
||||
}
|
||||
}
|
||||
|
||||
/** Too many DS records set on a domain. */
|
||||
static class TooManyDsRecordsException extends ParameterValuePolicyErrorException {
|
||||
public TooManyDsRecordsException(String message) {
|
||||
|
|
|
@ -22,13 +22,11 @@ import com.google.common.net.InternetDomainName;
|
|||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.FlowScope;
|
||||
import google.registry.flows.custom.DomainPricingCustomLogic;
|
||||
import google.registry.flows.custom.DomainPricingCustomLogic.ApplicationUpdatePriceParameters;
|
||||
import google.registry.flows.custom.DomainPricingCustomLogic.CreatePriceParameters;
|
||||
import google.registry.flows.custom.DomainPricingCustomLogic.RenewPriceParameters;
|
||||
import google.registry.flows.custom.DomainPricingCustomLogic.RestorePriceParameters;
|
||||
import google.registry.flows.custom.DomainPricingCustomLogic.TransferPriceParameters;
|
||||
import google.registry.flows.custom.DomainPricingCustomLogic.UpdatePriceParameters;
|
||||
import google.registry.model.domain.DomainApplication;
|
||||
import google.registry.model.domain.fee.BaseFee;
|
||||
import google.registry.model.domain.fee.BaseFee.FeeType;
|
||||
import google.registry.model.domain.fee.Fee;
|
||||
|
@ -162,25 +160,6 @@ public final class DomainPricingLogic {
|
|||
.build());
|
||||
}
|
||||
|
||||
/** Returns a new domain application update price for the pricer. */
|
||||
@SuppressWarnings("unused")
|
||||
public FeesAndCredits getApplicationUpdatePrice(
|
||||
Registry registry, DomainApplication application, DateTime date) throws EppException {
|
||||
BaseFee feeOrCredit =
|
||||
Fee.create(Money.zero(registry.getCurrency()).getAmount(), FeeType.UPDATE);
|
||||
return customLogic.customizeApplicationUpdatePrice(
|
||||
ApplicationUpdatePriceParameters.newBuilder()
|
||||
.setFeesAndCredits(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(registry.getCurrency())
|
||||
.setFeesAndCredits(feeOrCredit)
|
||||
.build())
|
||||
.setRegistry(registry)
|
||||
.setDomainApplication(application)
|
||||
.setAsOfDate(date)
|
||||
.build());
|
||||
}
|
||||
|
||||
/** Returns the fee class for a given domain and date. */
|
||||
public Optional<String> getFeeClass(String domainName, DateTime date) {
|
||||
return getDomainFeeClass(domainName, date);
|
||||
|
|
|
@ -42,7 +42,6 @@ import static google.registry.flows.domain.DomainFlowUtils.verifyClientUpdateNot
|
|||
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.util.CollectionUtils.nullToEmpty;
|
||||
import static google.registry.util.DateTimeUtils.earliestOf;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
|
@ -67,10 +66,8 @@ import google.registry.model.domain.DomainCommand.Update;
|
|||
import google.registry.model.domain.DomainCommand.Update.AddRemove;
|
||||
import google.registry.model.domain.DomainCommand.Update.Change;
|
||||
import google.registry.model.domain.DomainResource;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
import google.registry.model.domain.fee.FeeUpdateCommandExtension;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.secdns.SecDnsUpdateExtension;
|
||||
import google.registry.model.eppcommon.AuthInfo;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
|
@ -94,12 +91,6 @@ import org.joda.time.DateTime;
|
|||
* superuser. As such, adding or removing these statuses incurs a billing event. There will be only
|
||||
* one charge per update, even if several such statuses are updated at once.
|
||||
*
|
||||
* <p>If a domain was created during the sunrise or landrush phases of a TLD, is still within the
|
||||
* sunrushAddGracePeriod and has not yet been delegated in DNS, then it will not yet have been
|
||||
* billed for. Any update that causes the name to be delegated (such * as adding nameservers or
|
||||
* removing a hold status) will cause the domain to convert to a normal create and be billed for
|
||||
* accordingly.
|
||||
*
|
||||
* @error {@link google.registry.flows.EppException.UnimplementedExtensionException}
|
||||
* @error {@link google.registry.flows.ResourceFlowUtils.AddRemoveSameValueException}
|
||||
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException}
|
||||
|
@ -177,16 +168,6 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
|||
AfterValidationParameters.newBuilder().setExistingDomain(existingDomain).build());
|
||||
HistoryEntry historyEntry = buildHistoryEntry(existingDomain, now);
|
||||
DomainResource newDomain = performUpdate(command, existingDomain, now);
|
||||
// If the new domain is in the sunrush add grace period and is now publishable to DNS because we
|
||||
// have added nameserver or removed holds, we have to convert it to a standard add grace period.
|
||||
if (newDomain.shouldPublishToDns()) {
|
||||
for (GracePeriod gracePeriod : newDomain.getGracePeriods()) {
|
||||
if (gracePeriod.isSunrushAddGracePeriod()) {
|
||||
newDomain = convertSunrushAddToAdd(newDomain, gracePeriod, historyEntry, now);
|
||||
break; // There can only be one sunrush add grace period.
|
||||
}
|
||||
}
|
||||
}
|
||||
validateNewState(newDomain);
|
||||
dnsQueue.addDomainRefreshTask(targetId);
|
||||
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
|
||||
|
@ -289,50 +270,6 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
|||
return domainBuilder.build();
|
||||
}
|
||||
|
||||
private DomainResource convertSunrushAddToAdd(
|
||||
DomainResource newDomain, GracePeriod gracePeriod, HistoryEntry historyEntry, DateTime now) {
|
||||
// Cancel the billing event for the sunrush add and replace it with a new billing event.
|
||||
BillingEvent.Cancellation billingEventCancellation =
|
||||
BillingEvent.Cancellation.forGracePeriod(gracePeriod, historyEntry, targetId);
|
||||
BillingEvent.OneTime billingEvent =
|
||||
createBillingEventForSunrushConversion(newDomain, historyEntry, gracePeriod, now);
|
||||
ofy().save().entities(billingEvent, billingEventCancellation);
|
||||
// Modify the grace periods on the domain.
|
||||
return newDomain.asBuilder()
|
||||
.removeGracePeriod(gracePeriod)
|
||||
.addGracePeriod(GracePeriod.forBillingEvent(GracePeriodStatus.ADD, billingEvent))
|
||||
.build();
|
||||
}
|
||||
|
||||
private BillingEvent.OneTime createBillingEventForSunrushConversion(
|
||||
DomainResource existingDomain,
|
||||
HistoryEntry historyEntry,
|
||||
GracePeriod sunrushAddGracePeriod,
|
||||
DateTime now) {
|
||||
// Compute the expiration time of the add grace period. We will not allow it to be after the
|
||||
// sunrush add grace period expiration time (i.e. you can't get extra add grace period by
|
||||
// setting a nameserver).
|
||||
DateTime addGracePeriodExpirationTime = earliestOf(
|
||||
now.plus(Registry.get(existingDomain.getTld()).getAddGracePeriodLength()),
|
||||
sunrushAddGracePeriod.getExpirationTime());
|
||||
// Create a new billing event for the add grace period. Note that we do this even if it would
|
||||
// occur at the same time as the sunrush add grace period, as the event time will differ
|
||||
// between them.
|
||||
BillingEvent.OneTime originalAddEvent =
|
||||
ofy().load().key(sunrushAddGracePeriod.getOneTimeBillingEvent()).now();
|
||||
return new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.CREATE)
|
||||
.setTargetId(targetId)
|
||||
.setFlags(originalAddEvent.getFlags())
|
||||
.setClientId(sunrushAddGracePeriod.getClientId())
|
||||
.setCost(originalAddEvent.getCost())
|
||||
.setPeriodYears(originalAddEvent.getPeriodYears())
|
||||
.setEventTime(now)
|
||||
.setBillingTime(addGracePeriodExpirationTime)
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void validateRegistrantIsntBeingRemoved(Change change) throws EppException {
|
||||
if (change.getRegistrantContactId() != null && change.getRegistrantContactId().isEmpty()) {
|
||||
throw new MissingRegistrantException();
|
||||
|
|
|
@ -14,11 +14,8 @@
|
|||
|
||||
package google.registry.flows.picker;
|
||||
|
||||
import static google.registry.model.domain.launch.LaunchCreateExtension.CreateType.APPLICATION;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableTable;
|
||||
import com.google.common.collect.Table;
|
||||
import google.registry.flows.EppException;
|
||||
|
@ -35,11 +32,6 @@ import google.registry.flows.contact.ContactTransferQueryFlow;
|
|||
import google.registry.flows.contact.ContactTransferRejectFlow;
|
||||
import google.registry.flows.contact.ContactTransferRequestFlow;
|
||||
import google.registry.flows.contact.ContactUpdateFlow;
|
||||
import google.registry.flows.domain.DomainAllocateFlow;
|
||||
import google.registry.flows.domain.DomainApplicationCreateFlow;
|
||||
import google.registry.flows.domain.DomainApplicationDeleteFlow;
|
||||
import google.registry.flows.domain.DomainApplicationInfoFlow;
|
||||
import google.registry.flows.domain.DomainApplicationUpdateFlow;
|
||||
import google.registry.flows.domain.DomainCheckFlow;
|
||||
import google.registry.flows.domain.DomainClaimsCheckFlow;
|
||||
import google.registry.flows.domain.DomainCreateFlow;
|
||||
|
@ -65,11 +57,8 @@ import google.registry.flows.session.LoginFlow;
|
|||
import google.registry.flows.session.LogoutFlow;
|
||||
import google.registry.model.contact.ContactCommand;
|
||||
import google.registry.model.domain.DomainCommand;
|
||||
import google.registry.model.domain.allocate.AllocateCreateExtension;
|
||||
import google.registry.model.domain.launch.ApplicationIdTargetExtension;
|
||||
import google.registry.model.domain.launch.LaunchCheckExtension;
|
||||
import google.registry.model.domain.launch.LaunchCheckExtension.CheckType;
|
||||
import google.registry.model.domain.launch.LaunchCreateExtension;
|
||||
import google.registry.model.domain.launch.LaunchPhase;
|
||||
import google.registry.model.domain.rgp.RestoreCommand.RestoreOp;
|
||||
import google.registry.model.domain.rgp.RgpUpdateExtension;
|
||||
|
@ -229,74 +218,6 @@ public class FlowPicker {
|
|||
return resourceCommand == null ? null : resourceCrudFlows.get(resourceCommand.getClass());
|
||||
}};
|
||||
|
||||
/** The domain allocate flow has a specific extension. */
|
||||
private static final FlowProvider ALLOCATE_FLOW_PROVIDER =
|
||||
new FlowProvider() {
|
||||
@Override
|
||||
Class<? extends Flow> get(
|
||||
EppInput eppInput, InnerCommand innerCommand, ResourceCommand resourceCommand) {
|
||||
return (resourceCommand instanceof DomainCommand.Create
|
||||
&& eppInput.getSingleExtension(AllocateCreateExtension.class).isPresent())
|
||||
? DomainAllocateFlow.class
|
||||
: null;
|
||||
}
|
||||
};
|
||||
|
||||
private static final ImmutableSet<LaunchPhase> LAUNCH_PHASES_DEFAULTING_TO_APPLICATION =
|
||||
ImmutableSet.of(LaunchPhase.SUNRUSH, LaunchPhase.LANDRUSH);
|
||||
|
||||
/**
|
||||
* Application CRUD flows have an extension and are keyed on the type of their {@link
|
||||
* ResourceCommand}.
|
||||
*/
|
||||
private static final FlowProvider APPLICATION_CRUD_FLOW_PROVIDER =
|
||||
new FlowProvider() {
|
||||
|
||||
private final Map<Class<? extends ResourceCommand>, Class<? extends Flow>>
|
||||
applicationFlows =
|
||||
ImmutableMap.of(
|
||||
DomainCommand.Create.class, DomainApplicationCreateFlow.class,
|
||||
DomainCommand.Delete.class, DomainApplicationDeleteFlow.class,
|
||||
DomainCommand.Info.class, DomainApplicationInfoFlow.class,
|
||||
DomainCommand.Update.class, DomainApplicationUpdateFlow.class);
|
||||
|
||||
@Override
|
||||
Class<? extends Flow> get(
|
||||
EppInput eppInput, InnerCommand innerCommand, ResourceCommand resourceCommand) {
|
||||
if (eppInput.getSingleExtension(ApplicationIdTargetExtension.class).isPresent()) {
|
||||
return applicationFlows.get(resourceCommand.getClass());
|
||||
}
|
||||
Optional<LaunchCreateExtension> createExtension =
|
||||
eppInput.getSingleExtension(LaunchCreateExtension.class);
|
||||
// Return a flow if the type is APPLICATION. If the type is REGISTRATION, return null.
|
||||
if (createExtension.isPresent()) {
|
||||
LaunchPhase launchPhase = createExtension.get().getPhase();
|
||||
// <launch:create> has an optional type argument, that can take either "application" or
|
||||
// "registration".
|
||||
// https://tools.ietf.org/html/rfc8334#section-3.3.1
|
||||
// We get that type via createExtension.get().getCreateType()
|
||||
// If it isn't given, the function returns null.
|
||||
// In that case, we need to decide based on the TLD. For now we can't do that - so we
|
||||
// TEMPORARILY decide as follows:
|
||||
// landrush and sunrush phases will default to APPLICATION, because there's no possible
|
||||
// registration for it.
|
||||
// sunrise defaults to REGISTRATION because we're currenly launching start-date sunrise
|
||||
// that uses direct registration.
|
||||
//
|
||||
// TODO(b/76095570): if createExtension.get().getCreateType() isn't explicitly given,
|
||||
// we need to set it according to the TldState (which means we need to know the TLD and
|
||||
// load the Registry - which will probably result in a big refactoring since we can use
|
||||
// TldState information to pick the flow)
|
||||
if (APPLICATION.equals(createExtension.get().getCreateType())
|
||||
|| (createExtension.get().getCreateType() == null
|
||||
&& LAUNCH_PHASES_DEFAULTING_TO_APPLICATION.contains(launchPhase))) {
|
||||
return applicationFlows.get(resourceCommand.getClass());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/** Transfer flows have an {@link InnerCommand} of type {@link Transfer}. */
|
||||
private static final FlowProvider TRANSFER_FLOW_PROVIDER = new FlowProvider() {
|
||||
private final Table<Class<?>, TransferOp, Class<? extends Flow>> transferFlows = ImmutableTable
|
||||
|
@ -327,8 +248,6 @@ public class FlowPicker {
|
|||
SESSION_FLOW_PROVIDER,
|
||||
POLL_FLOW_PROVIDER,
|
||||
DOMAIN_RESTORE_FLOW_PROVIDER,
|
||||
ALLOCATE_FLOW_PROVIDER,
|
||||
APPLICATION_CRUD_FLOW_PROVIDER,
|
||||
DOMAIN_CHECK_FLOW_PROVIDER,
|
||||
RESOURCE_CRUD_FLOW_PROVIDER,
|
||||
TRANSFER_FLOW_PROVIDER);
|
||||
|
|
|
@ -76,7 +76,6 @@ public class PollRequestFlow implements Flow {
|
|||
.setMessageId(makePollMessageExternalId(pollMessage))
|
||||
.build())
|
||||
.setMultipleResData(pollMessage.getResponseData())
|
||||
.setExtensions(pollMessage.getResponseExtensions())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue