mirror of
https://github.com/google/nomulus.git
synced 2025-05-13 07:57:13 +02:00
Flatten the domain and application create flows
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=136601151
This commit is contained in:
parent
43821f650e
commit
475203532e
31 changed files with 1151 additions and 1410 deletions
|
@ -622,16 +622,16 @@ An EPP flow that creates a new domain resource.
|
|||
* The acceptance time specified in the claim notice is more than 48 hours
|
||||
in the past.
|
||||
* The expiration time specified in the claim notice has elapsed.
|
||||
* The checksum in the specified TCNID does not validate.
|
||||
* The fees passed in the transform command do not match the fees that will
|
||||
be charged.
|
||||
* Domain label is not allowed by IDN table.
|
||||
* The checksum in the specified TCNID does not validate.
|
||||
* Domain name is under tld which doesn't exist.
|
||||
* 2005
|
||||
* The specified TCNID is invalid.
|
||||
* Domain name must have exactly one part above the tld.
|
||||
* The requested fee is expressed in a scale that is invalid for the given
|
||||
currency.
|
||||
* The specified TCNID is invalid.
|
||||
* 2102
|
||||
* The 'maxSigLife' setting is not supported.
|
||||
* The 'grace-period', 'applied' and 'refundable' fields are disallowed by
|
||||
|
@ -647,19 +647,17 @@ An EPP flow that creates a new domain resource.
|
|||
* Resource linked to this domain does not exist.
|
||||
* 2304
|
||||
* The claims period for this TLD has ended.
|
||||
* Requested domain requires a claims notice.
|
||||
* Requested domain does not require a claims notice.
|
||||
* Requested domain is reserved.
|
||||
* Linked resource in pending delete prohibits operation.
|
||||
* Requested domain requires a claims notice.
|
||||
* Nameservers are not whitelisted for this TLD.
|
||||
* Nameservers not specified for this TLD with whitelist.
|
||||
* The requested domain name is on the premium price list, and this
|
||||
registrar has blocked premium registrations.
|
||||
* Registrant is not whitelisted for this TLD.
|
||||
* Requested domain does not require a claims notice.
|
||||
* There is an open application for this domain.
|
||||
* 2306
|
||||
* The specified trademark validator is not supported.
|
||||
* Only encoded signed marks are supported.
|
||||
* Domain names can only contain a-z, 0-9, '.' and '-'.
|
||||
* Periods for domain registrations must be specified in years.
|
||||
* The requested fees cannot be provided in the requested currency.
|
||||
|
@ -669,11 +667,13 @@ An EPP flow that creates a new domain resource.
|
|||
* More than one contact for a given role is not allowed.
|
||||
* No part of a domain name can be empty.
|
||||
* Domain name starts with xn-- but is not a valid IDN.
|
||||
* The specified trademark validator is not supported.
|
||||
* Domain labels cannot begin with a dash.
|
||||
* Missing type attribute for contact.
|
||||
* Too many DS records set on a domain.
|
||||
* Too many nameservers set on this domain.
|
||||
* Domain labels cannot end with a dash.
|
||||
* Only encoded signed marks are supported.
|
||||
|
||||
## DomainCheckFlow
|
||||
|
||||
|
@ -828,16 +828,16 @@ An EPP flow that creates a new application for a domain resource.
|
|||
* The acceptance time specified in the claim notice is more than 48 hours
|
||||
in the past.
|
||||
* The expiration time specified in the claim notice has elapsed.
|
||||
* The checksum in the specified TCNID does not validate.
|
||||
* The fees passed in the transform command do not match the fees that will
|
||||
be charged.
|
||||
* Domain label is not allowed by IDN table.
|
||||
* The checksum in the specified TCNID does not validate.
|
||||
* Domain name is under tld which doesn't exist.
|
||||
* 2005
|
||||
* The specified TCNID is invalid.
|
||||
* Domain name must have exactly one part above the tld.
|
||||
* The requested fee is expressed in a scale that is invalid for the given
|
||||
currency.
|
||||
* The specified TCNID is invalid.
|
||||
* Signed mark data is improperly encoded.
|
||||
* Error while parsing encoded signed mark data.
|
||||
* 2102
|
||||
|
@ -849,7 +849,7 @@ An EPP flow that creates a new application for a domain resource.
|
|||
* 2201
|
||||
* Registrar is not authorized to access this TLD.
|
||||
* 2202
|
||||
* Authorization information for accessing resource is invalid.
|
||||
* Invalid limited registration period token.
|
||||
* 2302
|
||||
* Resource with this id already exists.
|
||||
* This name has already been claimed by a sunrise applicant.
|
||||
|
@ -857,17 +857,15 @@ An EPP flow that creates a new application for a domain resource.
|
|||
* Resource linked to this domain does not exist.
|
||||
* 2304
|
||||
* The claims period for this TLD has ended.
|
||||
* Requested domain requires a claims notice.
|
||||
* Requested domain does not require a claims notice.
|
||||
* Requested domain is reserved.
|
||||
* Requested domain requires a claims notice.
|
||||
* Nameservers are not whitelisted for this TLD.
|
||||
* Nameservers not specified for this TLD with whitelist.
|
||||
* The requested domain name is on the premium price list, and this
|
||||
registrar has blocked premium registrations.
|
||||
* Registrant is not whitelisted for this TLD.
|
||||
* Requested domain does not require a claims notice.
|
||||
* 2306
|
||||
* The specified trademark validator is not supported.
|
||||
* Only encoded signed marks are supported.
|
||||
* Domain names can only contain a-z, 0-9, '.' and '-'.
|
||||
* Periods for domain registrations must be specified in years.
|
||||
* Encoded signed marks must use base64 encoding.
|
||||
|
@ -878,6 +876,7 @@ An EPP flow that creates a new application for a domain resource.
|
|||
* More than one contact for a given role is not allowed.
|
||||
* No part of a domain name can be empty.
|
||||
* Domain name starts with xn-- but is not a valid IDN.
|
||||
* The specified trademark validator is not supported.
|
||||
* Declared launch extension phase does not match the current registry
|
||||
phase.
|
||||
* Domain labels cannot begin with a dash.
|
||||
|
@ -894,6 +893,7 @@ An EPP flow that creates a new application for a domain resource.
|
|||
* Too many nameservers set on this domain.
|
||||
* Only one signed mark is allowed per application.
|
||||
* Domain labels cannot end with a dash.
|
||||
* Only encoded signed marks are supported.
|
||||
|
||||
## DomainAllocateFlow
|
||||
|
||||
|
@ -905,7 +905,6 @@ An EPP flow that allocates a new domain resource from a domain application.
|
|||
### Errors
|
||||
|
||||
* 2201
|
||||
* Registrar is not authorized to access this TLD.
|
||||
* Only a superuser can allocate domains.
|
||||
* 2302
|
||||
* Resource with this id already exists.
|
||||
|
@ -926,11 +925,12 @@ An EPP flow that checks whether strings are trademarked.
|
|||
* 2002
|
||||
* Command is not allowed in the current registry phase.
|
||||
* Claims checks are not allowed during sunrise.
|
||||
* The claims period has ended.
|
||||
* 2004
|
||||
* Domain name is under tld which doesn't exist.
|
||||
* 2201
|
||||
* Registrar is not authorized to access this TLD.
|
||||
* 2304
|
||||
* The claims period for this TLD has ended.
|
||||
* 2306
|
||||
* Too many resource checks requested in one check command.
|
||||
|
||||
|
|
|
@ -59,10 +59,6 @@ public abstract class Flow {
|
|||
/** Execute the business logic for this flow. */
|
||||
protected abstract EppOutput run() throws EppException;
|
||||
|
||||
protected String getClientId() {
|
||||
return sessionMetadata.getClientId();
|
||||
}
|
||||
|
||||
protected EppOutput createOutput(Result.Code code) {
|
||||
return createOutput(code, null);
|
||||
}
|
||||
|
|
|
@ -185,7 +185,13 @@ public class FlowModule {
|
|||
|
||||
@Provides
|
||||
@FlowScope
|
||||
static Optional<AuthInfo> provideAuthInfo(ResourceCommand resourceCommand) {
|
||||
static AuthInfo provideAuthInfo(ResourceCommand resourceCommand) {
|
||||
return ((SingleResourceCommand) resourceCommand).getAuthInfo();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FlowScope
|
||||
static Optional<AuthInfo> provideOptionalAuthInfo(ResourceCommand resourceCommand) {
|
||||
return Optional.fromNullable(((SingleResourceCommand) resourceCommand).getAuthInfo());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,122 +0,0 @@
|
|||
// Copyright 2016 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;
|
||||
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.flows.EppException.ObjectAlreadyExistsException;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.EppResource.Builder;
|
||||
import google.registry.model.EppResource.ForeignKeyedEppResource;
|
||||
import google.registry.model.domain.DomainApplication;
|
||||
import google.registry.model.eppinput.ResourceCommand.ResourceCreateOrChange;
|
||||
import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand;
|
||||
import google.registry.model.index.DomainApplicationIndex;
|
||||
import google.registry.model.index.EppResourceIndex;
|
||||
import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.util.TypeUtils.TypeInstantiator;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An EPP flow that creates a storable resource.
|
||||
*
|
||||
* @param <R> the resource type being changed
|
||||
* @param <B> a builder for the resource
|
||||
* @param <C> the command type, marshalled directly from the epp xml
|
||||
*/
|
||||
public abstract class ResourceCreateFlow
|
||||
<R extends EppResource,
|
||||
B extends Builder<R, ?>,
|
||||
C extends ResourceCreateOrChange<? super B> & SingleResourceCommand>
|
||||
extends ResourceCreateOrMutateFlow<R, C> {
|
||||
|
||||
@Override
|
||||
protected void initRepoId() {
|
||||
repoId = createFlowRepoId();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected abstract String createFlowRepoId();
|
||||
|
||||
@Override
|
||||
protected final void verifyIsAllowed() throws EppException {
|
||||
if (existingResource != null) {
|
||||
throw new ResourceAlreadyExistsException(targetId);
|
||||
}
|
||||
verifyCreateIsAllowed();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final R createOrMutateResource() throws EppException {
|
||||
B builder = new TypeInstantiator<B>(getClass()){}.instantiate();
|
||||
command.applyTo(builder);
|
||||
builder
|
||||
.setCreationClientId(getClientId())
|
||||
.setCurrentSponsorClientId(getClientId())
|
||||
.setRepoId(getResourceKey().getName());
|
||||
setCreateProperties(builder);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a new or updated {@link ForeignKeyIndex} and {@link EppResourceIndex} pointing to what we
|
||||
* created.
|
||||
*/
|
||||
@Override
|
||||
protected final void modifyRelatedResources() {
|
||||
if (newResource instanceof ForeignKeyedEppResource) {
|
||||
ofy().save().entity(ForeignKeyIndex.create(newResource, newResource.getDeletionTime()));
|
||||
} else if (newResource instanceof DomainApplication) {
|
||||
ofy().save().entity(
|
||||
DomainApplicationIndex.createUpdatedInstance((DomainApplication) newResource));
|
||||
}
|
||||
ofy().save().entity(EppResourceIndex.create(Key.create(newResource)));
|
||||
modifyCreateRelatedResources();
|
||||
}
|
||||
|
||||
/** Modify any other resources that need to be informed of this create. */
|
||||
protected void modifyCreateRelatedResources() {}
|
||||
|
||||
/** Check resource-specific invariants before allowing the create to proceed. */
|
||||
@SuppressWarnings("unused")
|
||||
protected void verifyCreateIsAllowed() throws EppException {}
|
||||
|
||||
/** Set any resource-specific properties before creating. */
|
||||
@SuppressWarnings("unused")
|
||||
protected void setCreateProperties(B builder) throws EppException {}
|
||||
|
||||
/** Resource with this id already exists. */
|
||||
public static class ResourceAlreadyExistsException extends ObjectAlreadyExistsException {
|
||||
|
||||
/** Whether this was thrown from a "failfast" context. Useful for testing. */
|
||||
final boolean failfast;
|
||||
|
||||
public ResourceAlreadyExistsException(String resourceId, boolean failfast) {
|
||||
super(String.format("Object with given ID (%s) already exists", resourceId));
|
||||
this.failfast = failfast;
|
||||
}
|
||||
|
||||
public ResourceAlreadyExistsException(String resourceId) {
|
||||
this(resourceId, false);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public boolean isFailfast() {
|
||||
return failfast;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,171 +0,0 @@
|
|||
// Copyright 2016 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.flows.EppException.AuthorizationErrorException;
|
||||
import google.registry.flows.FlowModule.InputXml;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand;
|
||||
import google.registry.model.eppoutput.EppOutput;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.util.TypeUtils.TypeInstantiator;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* An EPP flow that creates or mutates a single stored resource.
|
||||
*
|
||||
* @param <R> the resource type being changed
|
||||
* @param <C> the command type, marshalled directly from the epp xml
|
||||
*
|
||||
* @error {@link OnlyToolCanPassMetadataException}
|
||||
*/
|
||||
public abstract class ResourceCreateOrMutateFlow
|
||||
<R extends EppResource, C extends SingleResourceCommand> extends SingleResourceFlow<R, C>
|
||||
implements TransactionalFlow {
|
||||
|
||||
@Inject EppRequestSource eppRequestSource;
|
||||
@Inject @InputXml byte[] inputXmlBytes;
|
||||
|
||||
String repoId;
|
||||
protected R newResource;
|
||||
protected HistoryEntry historyEntry;
|
||||
protected MetadataExtension metadataExtension;
|
||||
|
||||
@Override
|
||||
protected final void initSingleResourceFlow() throws EppException {
|
||||
registerExtensions(MetadataExtension.class);
|
||||
metadataExtension = eppInput.getSingleExtension(MetadataExtension.class);
|
||||
initRepoId();
|
||||
initHistoryEntry();
|
||||
initResourceCreateOrMutateFlow();
|
||||
}
|
||||
|
||||
/** Subclasses can optionally override this for further initialization. */
|
||||
@SuppressWarnings("unused")
|
||||
protected void initResourceCreateOrMutateFlow() throws EppException {}
|
||||
|
||||
/**
|
||||
* Initializes the repoId on the flow. For mutate flows, the repoId is the same as that of the
|
||||
* existing resource. For create flows, a new repoId is allocated for the appropriate class.
|
||||
*/
|
||||
protected abstract void initRepoId();
|
||||
|
||||
/**
|
||||
* Create the history entry associated with this resource create or mutate flow.
|
||||
*/
|
||||
private void initHistoryEntry() {
|
||||
// Don't try to create a historyEntry for mutate flows that are failing because the
|
||||
// existingResource doesn't actually exist.
|
||||
historyEntry = (repoId == null) ? null : new HistoryEntry.Builder()
|
||||
.setType(getHistoryEntryType())
|
||||
.setPeriod(getCommandPeriod())
|
||||
.setClientId(getClientId())
|
||||
.setTrid(trid)
|
||||
.setModificationTime(now)
|
||||
.setXmlBytes(storeXmlInHistoryEntry() ? inputXmlBytes : null)
|
||||
.setBySuperuser(isSuperuser)
|
||||
.setReason(getHistoryEntryReason())
|
||||
.setRequestedByRegistrar(getHistoryEntryRequestedByRegistrar())
|
||||
.setParent(getResourceKey())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Key pointing to this resource, even if this resource hasn't been initialized or
|
||||
* persisted yet.
|
||||
*/
|
||||
protected Key<EppResource> getResourceKey() {
|
||||
checkState(repoId != null,
|
||||
"RepoId hasn't been initialized yet; getResourceKey() called too early");
|
||||
Class<R> resourceClazz = new TypeInstantiator<R>(getClass()){}.getExactType();
|
||||
return Key.<EppResource>create(null, resourceClazz, repoId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final EppOutput runResourceFlow() throws EppException {
|
||||
newResource = createOrMutateResource();
|
||||
verifyNewStateIsAllowed();
|
||||
validateMetadataExtension();
|
||||
modifyRelatedResources();
|
||||
enqueueTasks();
|
||||
ofy().save().<Object>entities(newResource, historyEntry);
|
||||
return getOutput();
|
||||
}
|
||||
|
||||
/** Execute the inner core of the command and returned the created or mutated resource. */
|
||||
protected abstract R createOrMutateResource() throws EppException;
|
||||
|
||||
/** Check the new state before writing it. */
|
||||
@SuppressWarnings("unused")
|
||||
protected void verifyNewStateIsAllowed() throws EppException {}
|
||||
|
||||
/** Kick off any tasks that need to happen asynchronously. */
|
||||
@SuppressWarnings("unused")
|
||||
protected void enqueueTasks() throws EppException {}
|
||||
|
||||
/** Modify any other resources that need to be informed of this change. */
|
||||
@SuppressWarnings("unused")
|
||||
protected void modifyRelatedResources() throws EppException {}
|
||||
|
||||
/** Ensure that, if a metadata command exists, it is being passed from a tool-created session. */
|
||||
void validateMetadataExtension() throws EppException {
|
||||
if (!(metadataExtension == null || eppRequestSource.equals(EppRequestSource.TOOL))) {
|
||||
throw new OnlyToolCanPassMetadataException();
|
||||
}
|
||||
}
|
||||
|
||||
/** Subclasses must override this to specify the type set on the history entry. */
|
||||
protected abstract HistoryEntry.Type getHistoryEntryType();
|
||||
|
||||
/** Subclasses may override this if they do not wish to store the XML of a command. */
|
||||
protected boolean storeXmlInHistoryEntry() { return true; }
|
||||
|
||||
/** Retrieve the reason for the history entry. */
|
||||
protected String getHistoryEntryReason() {
|
||||
return metadataExtension != null
|
||||
? metadataExtension.getReason()
|
||||
: null;
|
||||
}
|
||||
|
||||
/** Retrieve the requested by registrar flag for the history entry. */
|
||||
protected Boolean getHistoryEntryRequestedByRegistrar() {
|
||||
return metadataExtension != null
|
||||
? metadataExtension.getRequestedByRegistrar()
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses that have a specified period for their command should override this to so that the
|
||||
* history entry contains the correct data.
|
||||
*/
|
||||
protected Period getCommandPeriod() { return null; }
|
||||
|
||||
/** Get the {@link EppOutput} to return. */
|
||||
protected abstract EppOutput getOutput() throws EppException;
|
||||
|
||||
/** Only a tool can pass a metadata extension. */
|
||||
public static class OnlyToolCanPassMetadataException extends AuthorizationErrorException {
|
||||
public OnlyToolCanPassMetadataException() {
|
||||
super("Metadata extensions can only be passed by tools.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
// Copyright 2016 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;
|
||||
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.eppinput.EppInput.ResourceCommandWrapper;
|
||||
import google.registry.model.eppinput.ResourceCommand;
|
||||
import google.registry.model.eppoutput.EppOutput;
|
||||
import google.registry.util.TypeUtils.TypeInstantiator;
|
||||
|
||||
/**
|
||||
* An EPP flow that addresses a stored resource.
|
||||
*
|
||||
* @param <R> the resource type being manipulated
|
||||
* @param <C> the command type doing the manipulation.
|
||||
*/
|
||||
public abstract class ResourceFlow<R extends EppResource, C extends ResourceCommand>
|
||||
extends LoggedInFlow {
|
||||
|
||||
protected C command;
|
||||
protected Class<R> resourceClass;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected final void initLoggedInFlow() throws EppException {
|
||||
this.command = (C) ((ResourceCommandWrapper) eppInput.getCommandWrapper().getCommand())
|
||||
.getResourceCommand();
|
||||
this.resourceClass = new TypeInstantiator<R>(getClass()){}.getExactType();
|
||||
initResourceFlow();
|
||||
}
|
||||
|
||||
/** Resource flows can override this for custom initialization.*/
|
||||
protected abstract void initResourceFlow() throws EppException;
|
||||
|
||||
/**
|
||||
* Loads the target resource and performs authorization and state allowance checks on it before
|
||||
* delegating to {@link #runResourceFlow()}.
|
||||
*
|
||||
* @throws EppException If an error occurred while manipulating the resource.
|
||||
*/
|
||||
@Override
|
||||
public final EppOutput run() throws EppException {
|
||||
verifyIsAllowed();
|
||||
return runResourceFlow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the command is allowed on the target resource.
|
||||
*
|
||||
* @throws EppException If the command is not allowed on this resource.
|
||||
*/
|
||||
protected abstract void verifyIsAllowed() throws EppException;
|
||||
|
||||
/**
|
||||
* Run the flow.
|
||||
*
|
||||
* @throws EppException If something fails while manipulating the resource.
|
||||
*/
|
||||
protected abstract EppOutput runResourceFlow() throws EppException;
|
||||
}
|
|
@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkState;
|
|||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.EppResourceUtils.queryDomainsUsingResource;
|
||||
import static google.registry.model.domain.DomainResource.extendRegistrationWithCap;
|
||||
import static google.registry.model.index.ForeignKeyIndex.loadAndGetKey;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
@ -300,7 +301,7 @@ public class ResourceFlowUtils {
|
|||
|
||||
public static <R extends EppResource> void verifyResourceDoesNotExist(
|
||||
Class<R> clazz, String targetId, DateTime now) throws EppException {
|
||||
if (loadByForeignKey(clazz, targetId, now) != null) {
|
||||
if (loadAndGetKey(clazz, targetId, now) != null) {
|
||||
throw new ResourceAlreadyExistsException(targetId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,111 +0,0 @@
|
|||
// Copyright 2016 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;
|
||||
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.EppResourceUtils.loadDomainApplication;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.domain.DomainApplication;
|
||||
import google.registry.model.domain.launch.ApplicationIdTargetExtension;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An EPP flow that manipulates a single stored resource.
|
||||
*
|
||||
* @param <R> the resource type being manipulated
|
||||
* @param <C> the command type doing the manipulation.
|
||||
*/
|
||||
public abstract class SingleResourceFlow<R extends EppResource, C extends SingleResourceCommand>
|
||||
extends ResourceFlow<R, C> {
|
||||
|
||||
protected R existingResource;
|
||||
protected String targetId;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected final void initResourceFlow() throws EppException {
|
||||
targetId = getTargetId();
|
||||
// In a transactional flow, loading the resource will be expensive because it can't be cached.
|
||||
// Allow flows to optionally fail fast here before loading.
|
||||
failfast();
|
||||
// Loads the target resource if it exists
|
||||
// Some flows such as DomainApplicationInfoFlow have the id marked as optional in the schema.
|
||||
// We require it by policy in the relevant flow, but here we need to make sure not to NPE when
|
||||
// initializing the (obviously nonexistent) existing resource.
|
||||
if (targetId != null && tryToLoadExisting()) {
|
||||
// This ugliness goes away once domain application flows are flattened.
|
||||
if (resourceClass == DomainApplication.class) {
|
||||
existingResource = (R) loadDomainApplication(targetId, now);
|
||||
} else {
|
||||
existingResource = loadByForeignKey(resourceClass, targetId, now);
|
||||
}
|
||||
}
|
||||
if (existingResource != null) {
|
||||
Set<StatusValue> problems = Sets.intersection(
|
||||
existingResource.getStatusValues(), getDisallowedStatuses());
|
||||
if (!problems.isEmpty()) {
|
||||
throw new ResourceStatusProhibitsOperationException(problems);
|
||||
}
|
||||
}
|
||||
initSingleResourceFlow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the resource flow should attempt to load an existing resource with the
|
||||
* matching targetId. Defaults to true, but overriding flows can set to false to bypass loading
|
||||
* of existing resources.
|
||||
*/
|
||||
protected boolean tryToLoadExisting() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the target id from {@link SingleResourceCommand}. If there is a launch extension present,
|
||||
* it overrides that target id with its application id, so return that instead. There will never
|
||||
* be more than one launch extension.
|
||||
*/
|
||||
protected final String getTargetId() {
|
||||
ApplicationIdTargetExtension extension =
|
||||
eppInput.getSingleExtension(ApplicationIdTargetExtension.class);
|
||||
return extension == null ? command.getTargetId() : extension.getApplicationId();
|
||||
}
|
||||
|
||||
/** Subclasses can optionally override this to fail before loading {@link #existingResource}. */
|
||||
@SuppressWarnings("unused")
|
||||
protected void failfast() throws EppException {}
|
||||
|
||||
/** Subclasses can optionally override this for further initialization. */
|
||||
@SuppressWarnings("unused")
|
||||
protected void initSingleResourceFlow() throws EppException {}
|
||||
|
||||
protected Set<StatusValue> getDisallowedStatuses() {
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
|
||||
/** Resource status prohibits this operation. */
|
||||
public static class ResourceStatusProhibitsOperationException
|
||||
extends StatusProhibitsOperationException {
|
||||
public ResourceStatusProhibitsOperationException(Set<StatusValue> status) {
|
||||
super("Operation disallowed by status: " + Joiner.on(", ").join(status));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,429 +0,0 @@
|
|||
// Copyright 2016 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.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateContactsHaveTypes;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateDomainName;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateDomainNameWithIdnTables;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateDsData;
|
||||
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.verifyLaunchPhase;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyNotReserved;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyPremiumNameIsNotBlocked;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyRegistryStateAllowsLaunchFlows;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifySignedMarks;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears;
|
||||
import static google.registry.model.EppResourceUtils.createDomainRoid;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.domain.fee.Fee.FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.model.registry.Registries.findTldForName;
|
||||
import static google.registry.model.registry.label.ReservedList.matchesAnchorTenantReservation;
|
||||
import static google.registry.util.CollectionUtils.nullToEmpty;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.Work;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.ParameterValuePolicyErrorException;
|
||||
import google.registry.flows.EppException.ParameterValueRangeErrorException;
|
||||
import google.registry.flows.EppException.ParameterValueSyntaxErrorException;
|
||||
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
import google.registry.flows.EppException.UnimplementedOptionException;
|
||||
import google.registry.flows.ResourceCreateFlow;
|
||||
import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
|
||||
import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.DomainBase.Builder;
|
||||
import google.registry.model.domain.DomainCommand.Create;
|
||||
import google.registry.model.domain.DomainResource;
|
||||
import google.registry.model.domain.LrpTokenEntity;
|
||||
import google.registry.model.domain.fee.FeeTransformCommandExtension;
|
||||
import google.registry.model.domain.flags.FlagsCreateCommandExtension;
|
||||
import google.registry.model.domain.launch.LaunchCreateExtension;
|
||||
import google.registry.model.domain.launch.LaunchNotice;
|
||||
import google.registry.model.domain.launch.LaunchNotice.InvalidChecksumException;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.secdns.SecDnsCreateExtension;
|
||||
import google.registry.model.ofy.ObjectifyService;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.Registry.TldState;
|
||||
import google.registry.model.smd.SignedMark;
|
||||
import google.registry.model.tmch.ClaimsListShard;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An EPP flow that creates a new domain resource or application.
|
||||
*
|
||||
* @param <R> the resource type being created
|
||||
* @param <B> a builder for the resource
|
||||
*/
|
||||
public abstract class BaseDomainCreateFlow<R extends DomainBase, B extends Builder<R, ?>>
|
||||
extends ResourceCreateFlow<R, B, Create> {
|
||||
|
||||
private SecDnsCreateExtension secDnsCreate;
|
||||
|
||||
protected LaunchCreateExtension launchCreate;
|
||||
protected String domainLabel;
|
||||
protected InternetDomainName domainName;
|
||||
protected String idnTableName;
|
||||
protected FeeTransformCommandExtension feeCreate;
|
||||
protected EppCommandOperations commandOperations;
|
||||
protected boolean hasSignedMarks;
|
||||
protected SignedMark signedMark;
|
||||
protected boolean isAnchorTenantViaReservation;
|
||||
protected TldState tldState;
|
||||
protected Optional<LrpTokenEntity> lrpToken;
|
||||
|
||||
protected Optional<RegistryExtraFlowLogic> extraFlowLogic;
|
||||
|
||||
@Override
|
||||
public final void initResourceCreateOrMutateFlow() throws EppException {
|
||||
command = cloneAndLinkReferences(command, now);
|
||||
registerExtensions(SecDnsCreateExtension.class, FlagsCreateCommandExtension.class);
|
||||
registerExtensions(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
|
||||
secDnsCreate = eppInput.getSingleExtension(SecDnsCreateExtension.class);
|
||||
launchCreate = eppInput.getSingleExtension(LaunchCreateExtension.class);
|
||||
feeCreate =
|
||||
eppInput.getFirstExtensionOfClasses(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
|
||||
hasSignedMarks = launchCreate != null && !launchCreate.getSignedMarks().isEmpty();
|
||||
initDomainCreateFlow();
|
||||
// We can't initialize extraFlowLogic here, because the TLD has not been checked yet.
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected String createFlowRepoId() {
|
||||
// The domain name hasn't been validated yet, so if it's invalid, instead of throwing an error,
|
||||
// simply leave the repoId blank (it won't be needed anyway as the flow will fail when
|
||||
// validation fails later).
|
||||
if (!InternetDomainName.isValid(command.getFullyQualifiedDomainName())) {
|
||||
return null;
|
||||
}
|
||||
Optional<InternetDomainName> tldParsed =
|
||||
findTldForName(InternetDomainName.from(command.getFullyQualifiedDomainName()));
|
||||
return tldParsed.isPresent()
|
||||
? createDomainRoid(ObjectifyService.allocateId(), tldParsed.get().toString())
|
||||
: null;
|
||||
}
|
||||
|
||||
/** Subclasses may override this to do more specific initialization. */
|
||||
protected void initDomainCreateFlow() {}
|
||||
|
||||
/**
|
||||
* Returns the tld of the domain being created.
|
||||
*
|
||||
* <p>Update/delete domain-related flows can simply grab the tld using existingResource.getTld(),
|
||||
* but in the create flows, the resource doesn't exist yet. So we grab it off the domain name
|
||||
* that the flow is attempting to create.
|
||||
*
|
||||
* <p>Note that it's not always safe to call this until after the domain name has been validated
|
||||
* in verifyCreateIsAllowed().
|
||||
*/
|
||||
protected String getTld() {
|
||||
return domainName.parent().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fail the domain or application create very fast if the domain is already registered.
|
||||
*
|
||||
* <p>Try to load the domain non-transactionally, since this can hit memcache. If we succeed, and
|
||||
* the domain is not in the ADD grace period (the only state that allows instantaneous transition
|
||||
* to being deleted), we can assume that the domain will not be deleted (and therefore won't be
|
||||
* creatable) until its deletion time. For repeated failed creates this means we can avoid the
|
||||
* datastore lookup, which is very expensive (and first-seen failed creates are no worse than they
|
||||
* otherwise would be). This comes at the cost of the extra lookup for successful creates (or
|
||||
* rather, those that don't fail due to the domain existing) and also for failed creates within
|
||||
* the existing domain's ADD grace period.
|
||||
*/
|
||||
@Override
|
||||
protected final void failfast() throws EppException {
|
||||
// Enter a transactionless context briefly.
|
||||
DomainResource domain = ofy().doTransactionless(new Work<DomainResource>() {
|
||||
@Override
|
||||
public DomainResource run() {
|
||||
// This is cacheable because we are outside of a transaction.
|
||||
return loadByForeignKey(DomainResource.class, targetId, now);
|
||||
}});
|
||||
// If the domain exists already and isn't in the ADD grace period then there is no way it will
|
||||
// be suddenly deleted and therefore the create must fail.
|
||||
if (domain != null
|
||||
&& !domain.getGracePeriodStatuses().contains(GracePeriodStatus.ADD)) {
|
||||
throw new ResourceAlreadyExistsException(targetId, true);
|
||||
}
|
||||
}
|
||||
|
||||
/** Fail if the create command is somehow invalid. */
|
||||
@Override
|
||||
protected final void verifyCreateIsAllowed() throws EppException {
|
||||
// Validate that this is actually a legal domain name on a TLD that the registrar has access to.
|
||||
domainName = validateDomainName(command.getFullyQualifiedDomainName());
|
||||
idnTableName = validateDomainNameWithIdnTables(domainName);
|
||||
String tld = getTld();
|
||||
checkAllowedAccessToTld(getAllowedTlds(), tld);
|
||||
Registry registry = Registry.get(tld);
|
||||
tldState = registry.getTldState(now);
|
||||
// Now that the TLD has been verified, we can go ahead and initialize extraFlowLogic. The
|
||||
// initialization and matching commit are done at the topmost possible level in the flow
|
||||
// hierarchy, but the actual processing takes place only when needed in the children, e.g.
|
||||
// DomainCreateFlow.
|
||||
extraFlowLogic = RegistryExtraFlowLogicProxy.newInstanceForTld(tld);
|
||||
domainLabel = domainName.parts().get(0);
|
||||
commandOperations = TldSpecificLogicProxy.getCreatePrice(
|
||||
registry,
|
||||
domainName.toString(),
|
||||
getClientId(),
|
||||
now,
|
||||
command.getPeriod().getValue(),
|
||||
eppInput);
|
||||
// The TLD should always be the parent of the requested domain name.
|
||||
isAnchorTenantViaReservation = matchesAnchorTenantReservation(
|
||||
domainLabel, tld, command.getAuthInfo().getPw().getValue());
|
||||
boolean isLrpApplication =
|
||||
registry.getLrpPeriod().contains(now)
|
||||
&& !command.getAuthInfo().getPw().getValue().isEmpty()
|
||||
&& !isAnchorTenantViaReservation;
|
||||
lrpToken = isLrpApplication
|
||||
? TldSpecificLogicProxy.getMatchingLrpToken(command, tld)
|
||||
: Optional.<LrpTokenEntity>absent();
|
||||
// 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) {
|
||||
boolean isSunriseApplication =
|
||||
launchCreate != null && !launchCreate.getSignedMarks().isEmpty();
|
||||
if (!isAnchorTenantViaReservation) {
|
||||
verifyNotReserved(domainName, isSunriseApplication);
|
||||
}
|
||||
if (isLrpApplication && !lrpToken.isPresent()) {
|
||||
throw new BadAuthInfoForResourceException();
|
||||
}
|
||||
boolean isClaimsPeriod = now.isBefore(registry.getClaimsPeriodEnd());
|
||||
boolean isClaimsCreate = launchCreate != null && launchCreate.getNotice() != null;
|
||||
if (isClaimsPeriod) {
|
||||
boolean labelOnClaimsList = ClaimsListShard.get().getClaimKey(domainLabel) != null;
|
||||
if (labelOnClaimsList && !isSunriseApplication && !isClaimsCreate) {
|
||||
throw new MissingClaimsNoticeException(domainName.toString());
|
||||
}
|
||||
if (!labelOnClaimsList && isClaimsCreate) {
|
||||
throw new UnexpectedClaimsNoticeException(domainName.toString());
|
||||
}
|
||||
} else if (isClaimsCreate) {
|
||||
throw new ClaimsPeriodEndedException(tld);
|
||||
}
|
||||
verifyPremiumNameIsNotBlocked(targetId, now, getClientId());
|
||||
}
|
||||
verifyUnitIsYears(command.getPeriod());
|
||||
verifyNotInPendingDelete(
|
||||
command.getContacts(),
|
||||
command.getRegistrant(),
|
||||
command.getNameservers());
|
||||
validateContactsHaveTypes(command.getContacts());
|
||||
validateRegistrantAllowedOnTld(tld, command.getRegistrantContactId());
|
||||
validateNoDuplicateContacts(command.getContacts());
|
||||
validateRequiredContactsPresent(command.getRegistrant(), command.getContacts());
|
||||
Set<String> fullyQualifiedHostNames =
|
||||
nullToEmpty(command.getNameserverFullyQualifiedHostNames());
|
||||
validateNameserversCountForTld(tld, fullyQualifiedHostNames.size());
|
||||
validateNameserversAllowedOnTld(tld, fullyQualifiedHostNames);
|
||||
// This check is a vile hack that will survive for only a day or so, as I work to flatten the
|
||||
// domain and application create flows. Without it, the ordering of checks fails lots of tests.
|
||||
if (!isSuperuser && this instanceof DomainApplicationCreateFlow) {
|
||||
verifyRegistryStateAllowsLaunchFlows(Registry.get(getTld()), now);
|
||||
}
|
||||
validateLaunchCreateExtension();
|
||||
// If a signed mark was provided, then it must match the desired domain label.
|
||||
// We do this after validating the launch create extension so that flows which don't allow any
|
||||
// signed marks throw a more useful error message rather than complaining about specific issues
|
||||
// with the signed marks.
|
||||
if (hasSignedMarks) {
|
||||
signedMark = verifySignedMarks(launchCreate.getSignedMarks(), domainLabel, now);
|
||||
}
|
||||
validateSecDnsExtension();
|
||||
verifyDomainCreateIsAllowed();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void modifyCreateRelatedResources() {
|
||||
if (lrpToken.isPresent()) {
|
||||
ofy().save().entity(lrpToken.get().asBuilder()
|
||||
.setRedemptionHistoryEntry(Key.create(historyEntry))
|
||||
.build());
|
||||
}
|
||||
if (extraFlowLogic.isPresent()) {
|
||||
extraFlowLogic.get().commitAdditionalLogicChanges();
|
||||
}
|
||||
}
|
||||
|
||||
/** Validate the secDNS extension, if present. */
|
||||
private void validateSecDnsExtension() throws EppException {
|
||||
if (secDnsCreate != null) {
|
||||
if (secDnsCreate.getDsData() == null) {
|
||||
throw new DsDataRequiredException();
|
||||
}
|
||||
if (secDnsCreate.getMaxSigLife() != null) {
|
||||
throw new MaxSigLifeNotSupportedException();
|
||||
}
|
||||
validateDsData(secDnsCreate.getDsData());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If a launch create extension was given (always present for application creates, optional for
|
||||
* domain creates) then validate it.
|
||||
*/
|
||||
private void validateLaunchCreateExtension() throws EppException {
|
||||
if (launchCreate == null) {
|
||||
return;
|
||||
}
|
||||
if (!isSuperuser) { // Superusers can ignore the phase.
|
||||
verifyLaunchPhase(getTld(), launchCreate, now);
|
||||
}
|
||||
if (launchCreate.hasCodeMarks()) {
|
||||
throw new UnsupportedMarkTypeException();
|
||||
}
|
||||
validateDomainLaunchCreateExtension();
|
||||
LaunchNotice notice = launchCreate.getNotice();
|
||||
if (notice == null) {
|
||||
return;
|
||||
}
|
||||
if (!notice.getNoticeId().getValidatorId().equals("tmch")) {
|
||||
throw new InvalidTrademarkValidatorException();
|
||||
}
|
||||
// Superuser can force domain creations regardless of the current date.
|
||||
if (!isSuperuser) {
|
||||
if (notice.getExpirationTime().isBefore(now)) {
|
||||
throw new ExpiredClaimException();
|
||||
}
|
||||
// An acceptance within the past 48 hours is mandated by the TMCH Functional Spec.
|
||||
if (notice.getAcceptedTime().isBefore(now.minusHours(48))) {
|
||||
throw new AcceptedTooLongAgoException();
|
||||
}
|
||||
}
|
||||
try {
|
||||
notice.validate(domainLabel);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new MalformedTcnIdException();
|
||||
} catch (InvalidChecksumException e) {
|
||||
throw new InvalidTcnIdChecksumException();
|
||||
}
|
||||
}
|
||||
|
||||
/** Subclasses may override this to do more specific checks. */
|
||||
@SuppressWarnings("unused")
|
||||
protected void verifyDomainCreateIsAllowed() throws EppException {}
|
||||
|
||||
/** Subclasses may override this to do more specific validation of the launchCreate extension. */
|
||||
@SuppressWarnings("unused")
|
||||
protected void validateDomainLaunchCreateExtension() throws EppException {}
|
||||
|
||||
/** Handle the secDNS extension */
|
||||
@Override
|
||||
protected final void setCreateProperties(B builder) throws EppException {
|
||||
if (secDnsCreate != null) {
|
||||
builder.setDsData(secDnsCreate.getDsData());
|
||||
}
|
||||
builder.setLaunchNotice(launchCreate == null ? null : launchCreate.getNotice());
|
||||
setDomainCreateProperties(builder);
|
||||
builder.setIdnTableName(idnTableName);
|
||||
}
|
||||
|
||||
protected abstract void setDomainCreateProperties(B builder) throws EppException;
|
||||
|
||||
/** Requested domain requires a claims notice. */
|
||||
static class MissingClaimsNoticeException extends StatusProhibitsOperationException {
|
||||
public MissingClaimsNoticeException(String domainName) {
|
||||
super(String.format("%s requires a claims notice", domainName));
|
||||
}
|
||||
}
|
||||
|
||||
/** Requested domain does not require a claims notice. */
|
||||
static class UnexpectedClaimsNoticeException extends StatusProhibitsOperationException {
|
||||
public UnexpectedClaimsNoticeException(String domainName) {
|
||||
super(String.format("%s does not require a claims notice", domainName));
|
||||
}
|
||||
}
|
||||
|
||||
/** The claims period for this TLD has ended. */
|
||||
static class ClaimsPeriodEndedException extends StatusProhibitsOperationException {
|
||||
public ClaimsPeriodEndedException(String tld) {
|
||||
super(String.format("The claims period for %s has ended", tld));
|
||||
}
|
||||
}
|
||||
|
||||
/** The specified trademark validator is not supported. */
|
||||
static class InvalidTrademarkValidatorException extends ParameterValuePolicyErrorException {
|
||||
public InvalidTrademarkValidatorException() {
|
||||
super("The only supported validationID is 'tmch' for the ICANN Trademark Clearinghouse.");
|
||||
}
|
||||
}
|
||||
|
||||
/** At least one dsData is required when using the secDNS extension. */
|
||||
static class DsDataRequiredException extends ParameterValuePolicyErrorException {
|
||||
public DsDataRequiredException() {
|
||||
super("At least one dsData is required when using the secDNS extension");
|
||||
}
|
||||
}
|
||||
|
||||
/** Only encoded signed marks are supported. */
|
||||
static class UnsupportedMarkTypeException extends ParameterValuePolicyErrorException {
|
||||
public UnsupportedMarkTypeException() {
|
||||
super("Only encoded signed marks are supported");
|
||||
}
|
||||
}
|
||||
|
||||
/** The 'maxSigLife' setting is not supported. */
|
||||
static class MaxSigLifeNotSupportedException extends UnimplementedOptionException {
|
||||
public MaxSigLifeNotSupportedException() {
|
||||
super("The 'maxSigLife' setting is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
/** The expiration time specified in the claim notice has elapsed. */
|
||||
static class ExpiredClaimException extends ParameterValueRangeErrorException {
|
||||
public ExpiredClaimException() {
|
||||
super("The expiration time specified in the claim notice has elapsed");
|
||||
}
|
||||
}
|
||||
|
||||
/** The acceptance time specified in the claim notice is more than 48 hours in the past. */
|
||||
static class AcceptedTooLongAgoException extends ParameterValueRangeErrorException {
|
||||
public AcceptedTooLongAgoException() {
|
||||
super("The acceptance time specified in the claim notice is more than 48 hours in the past");
|
||||
}
|
||||
}
|
||||
|
||||
/** The specified TCNID is invalid. */
|
||||
static class MalformedTcnIdException extends ParameterValueSyntaxErrorException {
|
||||
public MalformedTcnIdException() {
|
||||
super("The specified TCNID is malformed");
|
||||
}
|
||||
}
|
||||
|
||||
/** The checksum in the specified TCNID does not validate. */
|
||||
static class InvalidTcnIdChecksumException extends ParameterValueRangeErrorException {
|
||||
public InvalidTcnIdChecksumException() {
|
||||
super("The checksum in the specified TCNID does not validate");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,10 +18,10 @@ import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount;
|
|||
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateDomainName;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateDomainNameWithIdnTables;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyClaimsPeriodNotEnded;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPredelegation;
|
||||
import static google.registry.model.domain.launch.LaunchPhase.CLAIMS;
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
|
||||
import static google.registry.util.DateTimeUtils.isAtOrAfter;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
@ -48,12 +48,12 @@ import javax.inject.Inject;
|
|||
/**
|
||||
* An EPP flow that checks whether strings are trademarked.
|
||||
*
|
||||
* @error {@link google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link google.registry.flows.exceptions.TooManyResourceChecksException}
|
||||
* @error {@link DomainFlowUtils.BadCommandForRegistryPhaseException}
|
||||
* @error {@link DomainFlowUtils.ClaimsPeriodEndedException}
|
||||
* @error {@link DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link DomainFlowUtils.TldDoesNotExistException}
|
||||
* @error {@link ClaimsCheckNotAllowedInSunrise}
|
||||
* @error {@link ClaimsPeriodEndedException}
|
||||
*/
|
||||
public final class ClaimsCheckFlow extends LoggedInFlow {
|
||||
|
||||
|
@ -85,9 +85,7 @@ public final class ClaimsCheckFlow extends LoggedInFlow {
|
|||
if (registry.getTldState(now) == TldState.SUNRISE) {
|
||||
throw new ClaimsCheckNotAllowedInSunrise();
|
||||
}
|
||||
if (isAtOrAfter(now, registry.getClaimsPeriodEnd())) {
|
||||
throw new ClaimsPeriodEndedException();
|
||||
}
|
||||
verifyClaimsPeriodNotEnded(registry, now);
|
||||
}
|
||||
}
|
||||
String claimKey = ClaimsListShard.get().getClaimKey(domainName.parts().get(0));
|
||||
|
@ -107,11 +105,4 @@ public final class ClaimsCheckFlow extends LoggedInFlow {
|
|||
super("Claims checks are not allowed during sunrise");
|
||||
}
|
||||
}
|
||||
|
||||
/** The claims period has ended. */
|
||||
static class ClaimsPeriodEndedException extends CommandUseErrorException {
|
||||
public ClaimsPeriodEndedException() {
|
||||
super("The claims period has ended");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,30 +14,70 @@
|
|||
|
||||
package google.registry.flows.domain;
|
||||
|
||||
import static com.google.common.collect.Iterables.filter;
|
||||
import static com.google.common.collect.Iterables.getOnlyElement;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.createFeeCreateResponse;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.failfastForCreate;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.getReservationType;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.prepareMarkedLrpTokenEntity;
|
||||
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.validateSecDnsExtension;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears;
|
||||
import static google.registry.model.EppResourceUtils.createDomainRoid;
|
||||
import static google.registry.model.EppResourceUtils.loadDomainApplication;
|
||||
import static google.registry.model.domain.fee.Fee.FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.model.registry.label.ReservedList.matchesAnchorTenantReservation;
|
||||
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.net.InternetDomainName;
|
||||
import com.googlecode.objectify.Key;
|
||||
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.FlowModule.ClientId;
|
||||
import google.registry.flows.FlowModule.TargetId;
|
||||
import google.registry.flows.LoggedInFlow;
|
||||
import google.registry.flows.TransactionalFlow;
|
||||
import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations;
|
||||
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.DomainResource.Builder;
|
||||
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.FeeTransformCommandExtension;
|
||||
import google.registry.model.domain.fee.FeeTransformResponseExtension;
|
||||
import google.registry.model.domain.flags.FlagsCreateCommandExtension;
|
||||
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.ResourceCommand;
|
||||
import google.registry.model.eppoutput.CreateData.DomainCreateData;
|
||||
import google.registry.model.eppoutput.EppOutput;
|
||||
import google.registry.model.eppoutput.Result;
|
||||
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;
|
||||
|
@ -45,140 +85,285 @@ import google.registry.model.registry.label.ReservationType;
|
|||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.tmch.LordnTask;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* An EPP flow that allocates a new domain resource from a domain application.
|
||||
*
|
||||
* @error {@link google.registry.flows.ResourceCreateFlow.ResourceAlreadyExistsException}
|
||||
* @error {@link google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link google.registry.flows.exceptions.ResourceAlreadyExistsException}
|
||||
* @error {@link DomainAllocateFlow.HasFinalStatusException}
|
||||
* @error {@link DomainAllocateFlow.MissingApplicationException}
|
||||
* @error {@link DomainAllocateFlow.OnlySuperuserCanAllocateException}
|
||||
*/
|
||||
public class DomainAllocateFlow extends DomainCreateOrAllocateFlow {
|
||||
public class DomainAllocateFlow extends LoggedInFlow implements TransactionalFlow {
|
||||
|
||||
protected AllocateCreateExtension allocateCreate;
|
||||
protected DomainApplication application;
|
||||
private static final String COLLISION_MESSAGE =
|
||||
"Domain on the name collision list was allocated. But by policy, the domain will not be "
|
||||
+ "delegated. Please visit https://www.icann.org/namecollision for more information on name "
|
||||
+ "collision.";
|
||||
|
||||
@Inject AuthInfo authInfo;
|
||||
@Inject ResourceCommand resourceCommand;
|
||||
@Inject @ClientId String clientId;
|
||||
@Inject @TargetId String targetId;
|
||||
@Inject HistoryEntry.Builder historyBuilder;
|
||||
@Inject DomainAllocateFlow() {}
|
||||
|
||||
@Override
|
||||
protected final void initDomainCreateOrAllocateFlow() {
|
||||
registerExtensions(AllocateCreateExtension.class);
|
||||
allocateCreate = eppInput.getSingleExtension(AllocateCreateExtension.class);
|
||||
protected final void initLoggedInFlow() throws EppException {
|
||||
registerExtensions(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
|
||||
registerExtensions(
|
||||
SecDnsCreateExtension.class,
|
||||
FlagsCreateCommandExtension.class,
|
||||
MetadataExtension.class,
|
||||
AllocateCreateExtension.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void verifyDomainCreateIsAllowed() throws EppException {
|
||||
public final EppOutput run() throws EppException {
|
||||
verifyIsSuperuser();
|
||||
Create command = cloneAndLinkReferences((Create) resourceCommand, now);
|
||||
failfastForCreate(targetId, now);
|
||||
verifyResourceDoesNotExist(DomainResource.class, targetId, now);
|
||||
InternetDomainName domainName = validateDomainName(command.getFullyQualifiedDomainName());
|
||||
Registry registry = Registry.get(domainName.parent().toString());
|
||||
Period period = command.getPeriod();
|
||||
Integer years = period.getValue();
|
||||
verifyUnitIsYears(period);
|
||||
validateCreateCommandContactsAndNameservers(command, registry.getTldStr());
|
||||
SecDnsCreateExtension secDnsCreate =
|
||||
validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class));
|
||||
boolean isSunrushAddGracePeriod = isNullOrEmpty(command.getNameservers());
|
||||
AllocateCreateExtension allocateCreate =
|
||||
eppInput.getSingleExtension(AllocateCreateExtension.class);
|
||||
DomainApplication application = loadAndValidateApplication(allocateCreate.getApplicationRoid());
|
||||
String repoId = createDomainRoid(ObjectifyService.allocateId(), registry.getTldStr());
|
||||
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
|
||||
HistoryEntry historyEntry = buildHistory(repoId, period);
|
||||
entitiesToSave.add(historyEntry);
|
||||
ImmutableSet<? extends ImmutableObject> billsAndPolls = createBillingEventsAndPollMessages(
|
||||
domainName, application, historyEntry, isSunrushAddGracePeriod, registry, years);
|
||||
entitiesToSave.addAll(billsAndPolls);
|
||||
DateTime registrationExpirationTime = leapSafeAddYears(now, years);
|
||||
DomainResource.Builder domainBuilder = new DomainResource.Builder();
|
||||
command.applyTo(domainBuilder);
|
||||
DomainResource newDomain = domainBuilder
|
||||
.setCreationClientId(clientId)
|
||||
.setCurrentSponsorClientId(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 == null ? application.getDsData() : secDnsCreate.getDsData())
|
||||
.addGracePeriod(createGracePeriod(
|
||||
isSunrushAddGracePeriod, getOnly(billsAndPolls, BillingEvent.OneTime.class)))
|
||||
// Names on the collision list will not be delegated. Set server hold.
|
||||
.setStatusValues(ReservationType.NAME_COLLISION == getReservationType(domainName)
|
||||
? ImmutableSet.of(StatusValue.SERVER_HOLD)
|
||||
: ImmutableSet.<StatusValue>of())
|
||||
.build();
|
||||
entitiesToSave.add(
|
||||
newDomain,
|
||||
buildApplicationHistory(application),
|
||||
updateApplication(application),
|
||||
ForeignKeyIndex.create(newDomain, newDomain.getDeletionTime()),
|
||||
EppResourceIndex.create(Key.create(newDomain)));
|
||||
// Anchor tenant registrations override LRP.
|
||||
String authInfoToken = authInfo.getPw().getValue();
|
||||
if (hasLrpToken(domainName, registry, authInfoToken)) {
|
||||
entitiesToSave.add(prepareMarkedLrpTokenEntity(authInfoToken, domainName, historyEntry));
|
||||
}
|
||||
ofy().save().entities(entitiesToSave.build());
|
||||
enqueueTasks(allocateCreate, newDomain);
|
||||
return createOutput(
|
||||
Result.Code.SUCCESS,
|
||||
DomainCreateData.create(targetId, now, registrationExpirationTime),
|
||||
createResponseExtensions(registry, years));
|
||||
}
|
||||
|
||||
private <T extends ImmutableObject> T getOnly(
|
||||
Iterable<? extends ImmutableObject> objects, Class<T> clazz) {
|
||||
return getOnlyElement(filter(objects, clazz));
|
||||
}
|
||||
|
||||
private void verifyIsSuperuser() throws OnlySuperuserCanAllocateException {
|
||||
if (!isSuperuser) {
|
||||
throw new OnlySuperuserCanAllocateException();
|
||||
}
|
||||
String applicationRoid = allocateCreate.getApplicationRoid();
|
||||
application = loadDomainApplication(applicationRoid, now);
|
||||
}
|
||||
|
||||
private DomainApplication loadAndValidateApplication(String applicationRoid) throws EppException {
|
||||
DomainApplication application = loadDomainApplication(applicationRoid, now);
|
||||
if (application == null) {
|
||||
throw new MissingApplicationException(applicationRoid);
|
||||
}
|
||||
if (application.getApplicationStatus().isFinalStatus()) {
|
||||
throw new HasFinalStatusException();
|
||||
}
|
||||
return application;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void setDomainCreateOrAllocateProperties(Builder builder) {
|
||||
boolean sunrushAddGracePeriod = isNullOrEmpty(command.getNameservers());
|
||||
Registry registry = Registry.get(getTld());
|
||||
ImmutableSet.Builder<Flag> billingFlagsBuilder = new ImmutableSet.Builder<>();
|
||||
if (!application.getEncodedSignedMarks().isEmpty()) {
|
||||
billingFlagsBuilder.add(Flag.SUNRISE);
|
||||
} else {
|
||||
billingFlagsBuilder.add(Flag.LANDRUSH);
|
||||
}
|
||||
BillingEvent.OneTime billingEvent = new BillingEvent.OneTime.Builder()
|
||||
private HistoryEntry buildHistory(String repoId, Period period) {
|
||||
return historyBuilder
|
||||
.setType(HistoryEntry.Type.DOMAIN_ALLOCATE)
|
||||
.setPeriod(period)
|
||||
.setModificationTime(now)
|
||||
.setParent(Key.create(DomainResource.class, repoId))
|
||||
.build();
|
||||
}
|
||||
|
||||
private ImmutableSet<? extends ImmutableObject> createBillingEventsAndPollMessages(
|
||||
InternetDomainName domainName,
|
||||
DomainApplication application,
|
||||
HistoryEntry historyEntry,
|
||||
boolean isSunrushAddGracePeriod,
|
||||
Registry registry,
|
||||
int years) {
|
||||
DateTime registrationExpirationTime = leapSafeAddYears(now, years);
|
||||
BillingEvent.OneTime oneTimeBillingEvent = createOneTimeBillingEvent(
|
||||
application, historyEntry, isSunrushAddGracePeriod, registry, years);
|
||||
PollMessage.OneTime oneTimePollMessage =
|
||||
createOneTimePollMessage(application, historyEntry, getReservationType(domainName));
|
||||
// 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,
|
||||
int years) {
|
||||
return new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.CREATE)
|
||||
.setFlags(billingFlagsBuilder.add(Flag.ALLOCATION).build())
|
||||
.setFlags(ImmutableSet.of(
|
||||
Flag.ALLOCATION,
|
||||
application.getEncodedSignedMarks().isEmpty() ? Flag.LANDRUSH : Flag.SUNRISE))
|
||||
.setTargetId(targetId)
|
||||
.setClientId(getClientId())
|
||||
.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, command.getPeriod().getValue()))
|
||||
.setPeriodYears(command.getPeriod().getValue())
|
||||
.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(
|
||||
sunrushAddGracePeriod
|
||||
? registry.getSunrushAddGracePeriodLength()
|
||||
: registry.getAddGracePeriodLength()))
|
||||
.setBillingTime(now.plus(isSunrushAddGracePeriod
|
||||
? registry.getSunrushAddGracePeriodLength()
|
||||
: registry.getAddGracePeriodLength()))
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
ReservationType reservationType = getReservationType(domainName);
|
||||
ofy().save().<Object>entities(
|
||||
// Save the billing event
|
||||
billingEvent,
|
||||
// Update the application itself.
|
||||
application.asBuilder()
|
||||
.setApplicationStatus(ApplicationStatus.ALLOCATED)
|
||||
.removeStatusValue(StatusValue.PENDING_CREATE)
|
||||
.build(),
|
||||
// Create a poll message informing the registrar that the application status was updated.
|
||||
new PollMessage.OneTime.Builder()
|
||||
.setClientId(application.getCurrentSponsorClientId())
|
||||
.setEventTime(ofy().getTransactionTime())
|
||||
.setMsg(reservationType == ReservationType.NAME_COLLISION
|
||||
// Change the poll message to remind the registrar of the name collision policy.
|
||||
? "Domain on the name collision list was allocated. "
|
||||
+ "But by policy, the domain will not be delegated. "
|
||||
+ "Please visit https://www.icann.org/namecollision "
|
||||
+ "for more information on name collision."
|
||||
: "Domain was allocated")
|
||||
.setResponseData(ImmutableList.of(
|
||||
DomainPendingActionNotificationResponse.create(
|
||||
application.getFullyQualifiedDomainName(),
|
||||
true,
|
||||
application.getCreationTrid(),
|
||||
now)))
|
||||
.setResponseExtensions(ImmutableList.of(
|
||||
new LaunchInfoResponseExtension.Builder()
|
||||
.setApplicationId(application.getForeignKey())
|
||||
.setPhase(application.getPhase())
|
||||
.setApplicationStatus(ApplicationStatus.ALLOCATED)
|
||||
.build()))
|
||||
.setParent(historyEntry)
|
||||
.build(),
|
||||
// Create a history entry (with no xml or trid) to record that we updated the application.
|
||||
new HistoryEntry.Builder()
|
||||
.setType(HistoryEntry.Type.DOMAIN_APPLICATION_STATUS_UPDATE)
|
||||
.setParent(application)
|
||||
.setModificationTime(now)
|
||||
.setClientId(application.getCurrentSponsorClientId())
|
||||
.setBySuperuser(true)
|
||||
.build());
|
||||
// Set the properties on the new domain.
|
||||
builder
|
||||
.addGracePeriod(GracePeriod.forBillingEvent(
|
||||
sunrushAddGracePeriod ? GracePeriodStatus.SUNRUSH_ADD : GracePeriodStatus.ADD,
|
||||
billingEvent))
|
||||
.setApplicationTime(allocateCreate.getApplicationTime())
|
||||
.setApplication(Key.create(application))
|
||||
.setSmdId(allocateCreate.getSmdId())
|
||||
.setLaunchNotice(allocateCreate.getNotice());
|
||||
// Names on the collision list will not be delegated. Set server hold.
|
||||
if (ReservationType.NAME_COLLISION == reservationType) {
|
||||
builder.addStatusValue(StatusValue.SERVER_HOLD);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enqueueLordnTaskIfNeeded() {
|
||||
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 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 HistoryEntry buildApplicationHistory(DomainApplication application) {
|
||||
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, ReservationType reservationType) {
|
||||
return new PollMessage.OneTime.Builder()
|
||||
.setClientId(historyEntry.getClientId())
|
||||
.setEventTime(now)
|
||||
.setMsg(reservationType == 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 boolean hasLrpToken(
|
||||
InternetDomainName domainName, Registry registry, String authInfoToken) {
|
||||
return registry.getLrpPeriod().contains(now)
|
||||
&& !matchesAnchorTenantReservation(domainName, authInfoToken);
|
||||
}
|
||||
|
||||
private void enqueueTasks(AllocateCreateExtension allocateCreate, DomainResource newDomain) {
|
||||
if (newDomain.shouldPublishToDns()) {
|
||||
DnsQueue.create().addDomainRefreshTask(newDomain.getFullyQualifiedDomainName());
|
||||
}
|
||||
if (allocateCreate.getSmdId() != null || allocateCreate.getNotice() != null) {
|
||||
LordnTask.enqueueDomainResourceTask(newResource);
|
||||
LordnTask.enqueueDomainResourceTask(newDomain);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final HistoryEntry.Type getHistoryEntryType() {
|
||||
return HistoryEntry.Type.DOMAIN_ALLOCATE;
|
||||
private ImmutableList<FeeTransformResponseExtension> createResponseExtensions(
|
||||
Registry registry, int years) throws EppException {
|
||||
EppCommandOperations commandOperations = TldSpecificLogicProxy.getCreatePrice(
|
||||
registry, targetId, clientId, now, years, eppInput);
|
||||
FeeTransformCommandExtension feeCreate =
|
||||
eppInput.getFirstExtensionOfClasses(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
|
||||
return (feeCreate == null)
|
||||
? null
|
||||
: ImmutableList.of(createFeeCreateResponse(feeCreate, commandOperations));
|
||||
}
|
||||
|
||||
/** Domain application with specific ROID does not exist. */
|
||||
|
|
|
@ -15,31 +15,72 @@
|
|||
package google.registry.flows.domain;
|
||||
|
||||
import static com.google.common.collect.Iterables.getOnlyElement;
|
||||
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.failfastForCreate;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.prepareMarkedLrpTokenEntity;
|
||||
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.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.verifyRegistryStateAllowsLaunchFlows;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifySignedMarks;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears;
|
||||
import static google.registry.model.EppResourceUtils.createDomainRoid;
|
||||
import static google.registry.model.domain.fee.Fee.FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER;
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
|
||||
import static google.registry.model.index.DomainApplicationIndex.loadActiveApplicationsByDomainName;
|
||||
import static google.registry.model.index.ForeignKeyIndex.loadAndGetKey;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.model.registry.label.ReservedList.matchesAnchorTenantReservation;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
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.FlowModule.ClientId;
|
||||
import google.registry.flows.FlowModule.TargetId;
|
||||
import google.registry.flows.LoggedInFlow;
|
||||
import google.registry.flows.TransactionalFlow;
|
||||
import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.domain.DomainApplication;
|
||||
import google.registry.model.domain.DomainApplication.Builder;
|
||||
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.FeeTransformCommandExtension;
|
||||
import google.registry.model.domain.flags.FlagsCreateCommandExtension;
|
||||
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.eppinput.ResourceCommand;
|
||||
import google.registry.model.eppoutput.CreateData.DomainCreateData;
|
||||
import google.registry.model.eppoutput.EppOutput;
|
||||
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.smd.AbstractSignedMark;
|
||||
|
@ -49,28 +90,19 @@ import javax.inject.Inject;
|
|||
/**
|
||||
* 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 google.registry.flows.ResourceCreateFlow.ResourceAlreadyExistsException}
|
||||
* @error {@link google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException}
|
||||
* @error {@link BaseDomainCreateFlow.AcceptedTooLongAgoException}
|
||||
* @error {@link BaseDomainCreateFlow.ClaimsPeriodEndedException}
|
||||
* @error {@link BaseDomainCreateFlow.ExpiredClaimException}
|
||||
* @error {@link BaseDomainCreateFlow.InvalidTcnIdChecksumException}
|
||||
* @error {@link BaseDomainCreateFlow.InvalidTrademarkValidatorException}
|
||||
* @error {@link BaseDomainCreateFlow.MalformedTcnIdException}
|
||||
* @error {@link BaseDomainCreateFlow.MaxSigLifeNotSupportedException}
|
||||
* @error {@link BaseDomainCreateFlow.MissingClaimsNoticeException}
|
||||
* @error {@link BaseDomainCreateFlow.UnexpectedClaimsNoticeException}
|
||||
* @error {@link BaseDomainCreateFlow.UnsupportedMarkTypeException}
|
||||
* @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.BadPeriodUnitException}
|
||||
* @error {@link DomainFlowUtils.Base64RequiredForEncodedSignedMarksException}
|
||||
* @error {@link DomainFlowUtils.ClaimsPeriodEndedException}
|
||||
* @error {@link DomainFlowUtils.CurrencyUnitMismatchException}
|
||||
* @error {@link DomainFlowUtils.CurrencyValueScaleException}
|
||||
* @error {@link DomainFlowUtils.DashesInThirdAndFourthException}
|
||||
|
@ -78,13 +110,20 @@ import javax.inject.Inject;
|
|||
* @error {@link DomainFlowUtils.DomainReservedException}
|
||||
* @error {@link DomainFlowUtils.DuplicateContactForRoleException}
|
||||
* @error {@link DomainFlowUtils.EmptyDomainNamePartException}
|
||||
* @error {@link DomainFlowUtils.ExpiredClaimException}
|
||||
* @error {@link DomainFlowUtils.FeesMismatchException}
|
||||
* @error {@link DomainFlowUtils.FeesRequiredForPremiumNameException}
|
||||
* @error {@link DomainFlowUtils.InvalidIdnDomainLabelException}
|
||||
* @error {@link DomainFlowUtils.InvalidLrpTokenException}
|
||||
* @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.NameserversNotAllowedException}
|
||||
* @error {@link DomainFlowUtils.NameserversNotSpecifiedException}
|
||||
|
@ -107,19 +146,131 @@ import javax.inject.Inject;
|
|||
* @error {@link DomainFlowUtils.TooManyNameserversException}
|
||||
* @error {@link DomainFlowUtils.TooManySignedMarksException}
|
||||
* @error {@link DomainFlowUtils.TrailingDashException}
|
||||
* @error {@link DomainFlowUtils.UnexpectedClaimsNoticeException}
|
||||
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
|
||||
* @error {@link DomainFlowUtils.UnsupportedMarkTypeException}
|
||||
*/
|
||||
public class DomainApplicationCreateFlow extends BaseDomainCreateFlow<DomainApplication, Builder> {
|
||||
public final class DomainApplicationCreateFlow extends LoggedInFlow implements TransactionalFlow {
|
||||
|
||||
@Inject AuthInfo authInfo;
|
||||
@Inject ResourceCommand resourceCommand;
|
||||
@Inject @ClientId String clientId;
|
||||
@Inject @TargetId String targetId;
|
||||
@Inject HistoryEntry.Builder historyBuilder;
|
||||
@Inject DomainApplicationCreateFlow() {}
|
||||
|
||||
@Override
|
||||
protected void initDomainCreateFlow() {
|
||||
registerExtensions(LaunchCreateExtension.class);
|
||||
protected final void initLoggedInFlow() throws EppException {
|
||||
registerExtensions(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
|
||||
registerExtensions(
|
||||
SecDnsCreateExtension.class,
|
||||
FlagsCreateCommandExtension.class,
|
||||
MetadataExtension.class,
|
||||
LaunchCreateExtension.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void validateDomainLaunchCreateExtension() throws EppException {
|
||||
public final EppOutput run() throws EppException {
|
||||
Create command = cloneAndLinkReferences((Create) resourceCommand, now);
|
||||
failfastForCreate(targetId, 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();
|
||||
checkAllowedAccessToTld(getAllowedTlds(), tld);
|
||||
Registry registry = Registry.get(tld);
|
||||
EppCommandOperations commandOperations = TldSpecificLogicProxy.getCreatePrice(
|
||||
registry, targetId, clientId, now, command.getPeriod().getValue(), eppInput);
|
||||
// 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.
|
||||
verifyUnitIsYears(command.getPeriod());
|
||||
validateCreateCommandContactsAndNameservers(command, tld);
|
||||
LaunchCreateExtension launchCreate = eppInput.getSingleExtension(LaunchCreateExtension.class);
|
||||
if (launchCreate != null) {
|
||||
validateLaunchCreateExtension(launchCreate, registry, domainName);
|
||||
}
|
||||
boolean isAnchorTenant =
|
||||
matchesAnchorTenantReservation(domainName, authInfo.getPw().getValue());
|
||||
if (!isSuperuser) {
|
||||
verifyPremiumNameIsNotBlocked(targetId, now, clientId);
|
||||
prohibitLandrushIfExactlyOneSunrise(registry);
|
||||
if (!isAnchorTenant) {
|
||||
boolean isSunriseApplication = !launchCreate.getSignedMarks().isEmpty();
|
||||
verifyNotReserved(domainName, isSunriseApplication);
|
||||
}
|
||||
}
|
||||
FeeTransformCommandExtension feeCreate =
|
||||
eppInput.getFirstExtensionOfClasses(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
|
||||
validateFeeChallenge(targetId, tld, now, feeCreate, commandOperations.getTotalCost());
|
||||
SecDnsCreateExtension secDnsCreate =
|
||||
validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class));
|
||||
DomainApplication.Builder applicationBuilder = new DomainApplication.Builder();
|
||||
command.applyTo(applicationBuilder);
|
||||
applicationBuilder
|
||||
.setCreationTrid(trid)
|
||||
.setCreationClientId(clientId)
|
||||
.setCurrentSponsorClientId(clientId)
|
||||
.setRepoId(createDomainRoid(ObjectifyService.allocateId(), tld))
|
||||
.setLaunchNotice(launchCreate == null ? null : launchCreate.getNotice())
|
||||
.setIdnTableName(idnTableName)
|
||||
.setPhase(launchCreate.getPhase())
|
||||
.setApplicationStatus(ApplicationStatus.VALIDATED)
|
||||
.addStatusValue(StatusValue.PENDING_CREATE)
|
||||
.setDsData(secDnsCreate == null ? null : secDnsCreate.getDsData())
|
||||
.setEncodedSignedMarks(FluentIterable
|
||||
.from(launchCreate.getSignedMarks())
|
||||
.transform(new Function<AbstractSignedMark, EncodedSignedMark>() {
|
||||
@Override
|
||||
public EncodedSignedMark apply(AbstractSignedMark abstractSignedMark) {
|
||||
return (EncodedSignedMark) abstractSignedMark;
|
||||
}})
|
||||
.toList());
|
||||
DomainApplication newApplication = applicationBuilder.build();
|
||||
HistoryEntry historyEntry = buildHistory(newApplication.getRepoId(), command.getPeriod());
|
||||
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
|
||||
entitiesToSave.add(
|
||||
newApplication,
|
||||
historyEntry,
|
||||
DomainApplicationIndex.createUpdatedInstance(newApplication),
|
||||
EppResourceIndex.create(Key.create(newApplication)));
|
||||
// Anchor tenant registrations override LRP, and landrush applications can skip it.
|
||||
if (registry.getLrpPeriod().contains(now) && !isAnchorTenant) {
|
||||
// TODO(b/32059212): This is a bug: empty tokens should still fail. Preserving to fix in a
|
||||
// separate targeted change.
|
||||
if (!authInfo.getPw().getValue().isEmpty()) {
|
||||
entitiesToSave.add(
|
||||
prepareMarkedLrpTokenEntity(authInfo.getPw().getValue(), domainName, historyEntry));
|
||||
}
|
||||
}
|
||||
ofy().save().entities(entitiesToSave.build());
|
||||
return createOutput(
|
||||
SUCCESS,
|
||||
DomainCreateData.create(targetId, now, null),
|
||||
createResponseExtensions(
|
||||
newApplication.getForeignKey(), launchCreate.getPhase(), feeCreate, commandOperations));
|
||||
}
|
||||
|
||||
private void validateLaunchCreateExtension(
|
||||
LaunchCreateExtension launchCreate, Registry registry, InternetDomainName domainName)
|
||||
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.
|
||||
verifyRegistryStateAllowsLaunchFlows(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).
|
||||
|
@ -127,22 +278,28 @@ public class DomainApplicationCreateFlow extends BaseDomainCreateFlow<DomainAppl
|
|||
throw new LandrushApplicationDisallowedDuringSunriseException();
|
||||
}
|
||||
} else {
|
||||
if (launchCreate.getNotice() != null) { // Can't use a claims notice id with a signed mark.
|
||||
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()) {
|
||||
verifySignedMarks(launchCreate.getSignedMarks(), domainLabel, now);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void verifyDomainCreateIsAllowed() throws EppException {
|
||||
validateFeeChallenge(
|
||||
targetId, getTld(), now, feeCreate, commandOperations.getTotalCost());
|
||||
if (!isSuperuser && tldState == TldState.LANDRUSH) {
|
||||
// Prohibit creating a landrush application in LANDRUSH (but not in SUNRUSH) if there is
|
||||
// exactly one sunrise application for the same name.
|
||||
/**
|
||||
* 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)
|
||||
throws UncontestedSunriseApplicationBlockedInLandrushException {
|
||||
if (registry.getTldState(now) == TldState.LANDRUSH) {
|
||||
ImmutableSet<DomainApplication> applications =
|
||||
loadActiveApplicationsByDomainName(targetId, now);
|
||||
if (applications.size() == 1
|
||||
|
@ -150,70 +307,32 @@ public class DomainApplicationCreateFlow extends BaseDomainCreateFlow<DomainAppl
|
|||
throw new UncontestedSunriseApplicationBlockedInLandrushException();
|
||||
}
|
||||
}
|
||||
// Fail if the domain is already registered (e.g. this is a landrush application but the domain
|
||||
// was awarded at the end of sunrise).
|
||||
if (loadAndGetKey(DomainResource.class, targetId, now) != null) {
|
||||
throw new ResourceAlreadyExistsException(targetId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDomainCreateProperties(Builder builder) {
|
||||
builder
|
||||
.setCreationTrid(trid)
|
||||
.setPhase(launchCreate.getPhase())
|
||||
.setApplicationStatus(ApplicationStatus.VALIDATED)
|
||||
.addStatusValue(StatusValue.PENDING_CREATE);
|
||||
if (!launchCreate.getSignedMarks().isEmpty()) {
|
||||
builder.setEncodedSignedMarks(FluentIterable
|
||||
.from(launchCreate.getSignedMarks())
|
||||
.transform(new Function<AbstractSignedMark, EncodedSignedMark>() {
|
||||
@Override
|
||||
public EncodedSignedMark apply(AbstractSignedMark abstractSignedMark) {
|
||||
// We verified that this is the case in verifyDomainCreateIsAllowed().
|
||||
return (EncodedSignedMark) abstractSignedMark;
|
||||
}})
|
||||
.toList());
|
||||
}
|
||||
private HistoryEntry buildHistory(String repoId, Period period) {
|
||||
return historyBuilder
|
||||
.setType(HistoryEntry.Type.DOMAIN_APPLICATION_CREATE)
|
||||
.setPeriod(period)
|
||||
.setModificationTime(now)
|
||||
.setParent(Key.create(DomainApplication.class, repoId))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final HistoryEntry.Type getHistoryEntryType() {
|
||||
return HistoryEntry.Type.DOMAIN_APPLICATION_CREATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final Period getCommandPeriod() {
|
||||
return command.getPeriod();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean tryToLoadExisting() {
|
||||
// Multiple domain applications can be created for the same targetId (which is the fully
|
||||
// qualified domain name), so don't try to load an existing resource with the same target id.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final EppOutput getOutput() {
|
||||
private ImmutableList<ResponseExtension> createResponseExtensions(
|
||||
String applicationId,
|
||||
LaunchPhase launchPhase,
|
||||
FeeTransformCommandExtension feeCreate,
|
||||
EppCommandOperations commandOperations) {
|
||||
ImmutableList.Builder<ResponseExtension> responseExtensionsBuilder =
|
||||
new ImmutableList.Builder<>();
|
||||
responseExtensionsBuilder.add(new LaunchCreateResponseExtension.Builder()
|
||||
.setPhase(launchCreate.getPhase())
|
||||
.setApplicationId(newResource.getForeignKey())
|
||||
.setPhase(launchPhase)
|
||||
.setApplicationId(applicationId)
|
||||
.build());
|
||||
if (feeCreate != null) {
|
||||
responseExtensionsBuilder.add(feeCreate.createResponseBuilder()
|
||||
.setCurrency(commandOperations.getCurrency())
|
||||
.setFees(commandOperations.getFees())
|
||||
.setCredits(commandOperations.getCredits())
|
||||
.build());
|
||||
responseExtensionsBuilder.add(createFeeCreateResponse(feeCreate, commandOperations));
|
||||
}
|
||||
|
||||
return createOutput(
|
||||
SUCCESS,
|
||||
DomainCreateData.create(newResource.getFullyQualifiedDomainName(), now, null),
|
||||
responseExtensionsBuilder.build());
|
||||
return responseExtensionsBuilder.build();
|
||||
}
|
||||
|
||||
/** Landrush applications are disallowed during sunrise. */
|
||||
|
|
|
@ -22,7 +22,7 @@ import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForR
|
|||
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.verifyLaunchPhase;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyLaunchPhaseMatchesRegistryPhase;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyRegistryStateAllowsLaunchFlows;
|
||||
import static google.registry.model.EppResourceUtils.loadDomainApplication;
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
|
||||
|
@ -84,12 +84,14 @@ public final class DomainApplicationDeleteFlow extends LoggedInFlow implements T
|
|||
String tld = existingApplication.getTld();
|
||||
checkAllowedAccessToTld(getAllowedTlds(), tld);
|
||||
if (!isSuperuser) {
|
||||
verifyRegistryStateAllowsLaunchFlows(Registry.get(tld), now);
|
||||
verifyLaunchPhase(tld, eppInput.getSingleExtension(LaunchDeleteExtension.class), now);
|
||||
Registry registry = Registry.get(tld);
|
||||
verifyRegistryStateAllowsLaunchFlows(registry, now);
|
||||
verifyLaunchPhaseMatchesRegistryPhase(
|
||||
registry, eppInput.getSingleExtension(LaunchDeleteExtension.class), now);
|
||||
verifyResourceOwnership(clientId, existingApplication);
|
||||
// Don't allow deleting a sunrise application during landrush.
|
||||
if (existingApplication.getPhase().equals(LaunchPhase.SUNRISE)
|
||||
&& Registry.get(tld).getTldState(now).equals(TldState.LANDRUSH)) {
|
||||
&& registry.getTldState(now).equals(TldState.LANDRUSH)) {
|
||||
throw new SunriseApplicationCannotBeDeletedInLandrushException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,51 +14,99 @@
|
|||
|
||||
package google.registry.flows.domain;
|
||||
|
||||
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.failfastForCreate;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.prepareMarkedLrpTokenEntity;
|
||||
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.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.verifySignedMarks;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears;
|
||||
import static google.registry.model.EppResourceUtils.createDomainRoid;
|
||||
import static google.registry.model.domain.fee.Fee.FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER;
|
||||
import static google.registry.model.index.DomainApplicationIndex.loadActiveApplicationsByDomainName;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.model.registry.label.ReservedList.matchesAnchorTenantReservation;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.leapSafeAddYears;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
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.StatusProhibitsOperationException;
|
||||
import google.registry.flows.FlowModule.ClientId;
|
||||
import google.registry.flows.FlowModule.TargetId;
|
||||
import google.registry.flows.LoggedInFlow;
|
||||
import google.registry.flows.TransactionalFlow;
|
||||
import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Flag;
|
||||
import google.registry.model.billing.BillingEvent.OneTime;
|
||||
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.DomainResource.Builder;
|
||||
import google.registry.model.domain.DomainCommand.Create;
|
||||
import google.registry.model.domain.DomainResource;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.domain.fee.FeeTransformCommandExtension;
|
||||
import google.registry.model.domain.fee.FeeTransformResponseExtension;
|
||||
import google.registry.model.domain.flags.FlagsCreateCommandExtension;
|
||||
import google.registry.model.domain.launch.LaunchCreateExtension;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.secdns.SecDnsCreateExtension;
|
||||
import google.registry.model.eppcommon.AuthInfo;
|
||||
import google.registry.model.eppinput.ResourceCommand;
|
||||
import google.registry.model.eppoutput.CreateData.DomainCreateData;
|
||||
import google.registry.model.eppoutput.EppOutput;
|
||||
import google.registry.model.eppoutput.Result;
|
||||
import google.registry.model.index.EppResourceIndex;
|
||||
import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.model.ofy.ObjectifyService;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.poll.PollMessage.Autorenew;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.Registry.TldState;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.tmch.LordnTask;
|
||||
import java.util.Set;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* An EPP flow that creates a new domain resource.
|
||||
*
|
||||
* @error {@link google.registry.flows.exceptions.OnlyToolCanPassMetadataException}
|
||||
* @error {@link google.registry.flows.exceptions.ResourceAlreadyExistsException}
|
||||
* @error {@link google.registry.flows.EppException.UnimplementedExtensionException}
|
||||
* @error {@link google.registry.flows.LoggedInFlow.UndeclaredServiceExtensionException}
|
||||
* @error {@link google.registry.flows.ResourceCreateFlow.ResourceAlreadyExistsException}
|
||||
* @error {@link google.registry.flows.ResourceCreateOrMutateFlow.OnlyToolCanPassMetadataException}
|
||||
* @error {@link google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link BaseDomainCreateFlow.AcceptedTooLongAgoException}
|
||||
* @error {@link BaseDomainCreateFlow.ClaimsPeriodEndedException}
|
||||
* @error {@link BaseDomainCreateFlow.ExpiredClaimException}
|
||||
* @error {@link BaseDomainCreateFlow.InvalidTcnIdChecksumException}
|
||||
* @error {@link BaseDomainCreateFlow.InvalidTrademarkValidatorException}
|
||||
* @error {@link BaseDomainCreateFlow.MalformedTcnIdException}
|
||||
* @error {@link BaseDomainCreateFlow.MaxSigLifeNotSupportedException}
|
||||
* @error {@link BaseDomainCreateFlow.MissingClaimsNoticeException}
|
||||
* @error {@link BaseDomainCreateFlow.UnexpectedClaimsNoticeException}
|
||||
* @error {@link BaseDomainCreateFlow.UnsupportedMarkTypeException}
|
||||
* @error {@link DomainCreateFlow.SignedMarksNotAcceptedInCurrentPhaseException}
|
||||
* @error {@link DomainFlowUtils.AcceptedTooLongAgoException}
|
||||
* @error {@link DomainFlowUtils.BadDomainNameCharacterException}
|
||||
* @error {@link DomainFlowUtils.BadDomainNamePartsCountException}
|
||||
* @error {@link DomainFlowUtils.BadPeriodUnitException}
|
||||
* @error {@link DomainFlowUtils.ClaimsPeriodEndedException}
|
||||
* @error {@link DomainFlowUtils.CurrencyUnitMismatchException}
|
||||
* @error {@link DomainFlowUtils.CurrencyValueScaleException}
|
||||
* @error {@link DomainFlowUtils.DashesInThirdAndFourthException}
|
||||
|
@ -66,14 +114,20 @@ import javax.inject.Inject;
|
|||
* @error {@link DomainFlowUtils.DomainReservedException}
|
||||
* @error {@link DomainFlowUtils.DuplicateContactForRoleException}
|
||||
* @error {@link DomainFlowUtils.EmptyDomainNamePartException}
|
||||
* @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.LeadingDashException}
|
||||
* @error {@link DomainFlowUtils.LinkedResourcesDoNotExistException}
|
||||
* @error {@link DomainFlowUtils.LinkedResourceInPendingDeleteProhibitsOperationException}
|
||||
* @error {@link DomainFlowUtils.MalformedTcnIdException}
|
||||
* @error {@link DomainFlowUtils.MaxSigLifeNotSupportedException}
|
||||
* @error {@link DomainFlowUtils.MissingAdminContactException}
|
||||
* @error {@link DomainFlowUtils.MissingClaimsNoticeException}
|
||||
* @error {@link DomainFlowUtils.MissingContactTypeException}
|
||||
* @error {@link DomainFlowUtils.MissingRegistrantException}
|
||||
* @error {@link DomainFlowUtils.MissingTechnicalContactException}
|
||||
|
@ -85,127 +139,287 @@ import javax.inject.Inject;
|
|||
* @error {@link DomainFlowUtils.TooManyDsRecordsException}
|
||||
* @error {@link DomainFlowUtils.TooManyNameserversException}
|
||||
* @error {@link DomainFlowUtils.TrailingDashException}
|
||||
* @error {@link DomainFlowUtils.UnexpectedClaimsNoticeException}
|
||||
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
|
||||
* @error {@link DomainFlowUtils.UnsupportedMarkTypeException}
|
||||
* @error {@link DomainCreateFlow.DomainHasOpenApplicationsException}
|
||||
* @error {@link DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException}
|
||||
*/
|
||||
|
||||
public class DomainCreateFlow extends DomainCreateOrAllocateFlow {
|
||||
public class DomainCreateFlow extends LoggedInFlow implements TransactionalFlow {
|
||||
|
||||
private static final Set<TldState> QLP_SMD_ALLOWED_STATES =
|
||||
Sets.immutableEnumSet(TldState.SUNRISE, TldState.SUNRUSH);
|
||||
|
||||
@Inject AuthInfo authInfo;
|
||||
@Inject ResourceCommand resourceCommand;
|
||||
@Inject @ClientId String clientId;
|
||||
@Inject @TargetId String targetId;
|
||||
@Inject HistoryEntry.Builder historyBuilder;
|
||||
@Inject DomainCreateFlow() {}
|
||||
|
||||
private boolean isAnchorTenant() {
|
||||
return isAnchorTenantViaReservation || isAnchorTenantViaExtension;
|
||||
@Override
|
||||
protected final void initLoggedInFlow() throws EppException {
|
||||
registerExtensions(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
|
||||
registerExtensions(
|
||||
SecDnsCreateExtension.class,
|
||||
FlagsCreateCommandExtension.class,
|
||||
MetadataExtension.class,
|
||||
LaunchCreateExtension.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void verifyDomainCreateIsAllowed() throws EppException {
|
||||
String tld = getTld();
|
||||
validateFeeChallenge(targetId, tld, now, feeCreate, commandOperations.getTotalCost());
|
||||
public final EppOutput run() throws EppException {
|
||||
Create command = cloneAndLinkReferences((Create) resourceCommand, now);
|
||||
Period period = command.getPeriod();
|
||||
verifyUnitIsYears(period);
|
||||
int years = period.getValue();
|
||||
failfastForCreate(targetId, now);
|
||||
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(command.getFullyQualifiedDomainName());
|
||||
String domainLabel = domainName.parts().get(0);
|
||||
Registry registry = Registry.get(domainName.parent().toString());
|
||||
validateCreateCommandContactsAndNameservers(command, registry.getTldStr());
|
||||
TldState tldState = registry.getTldState(now);
|
||||
boolean isAnchorTenant = isAnchorTenant(domainName);
|
||||
LaunchCreateExtension launchCreate = eppInput.getSingleExtension(LaunchCreateExtension.class);
|
||||
boolean hasSignedMarks = launchCreate != null && !launchCreate.getSignedMarks().isEmpty();
|
||||
boolean hasClaimsNotice = launchCreate != null && launchCreate.getNotice() != null;
|
||||
if (launchCreate != null) {
|
||||
verifyNoCodeMarks(launchCreate);
|
||||
validateLaunchCreateNotice(launchCreate.getNotice(), domainLabel, isSuperuser, now);
|
||||
}
|
||||
if (hasSignedMarks) {
|
||||
verifySignedMarksAllowed(tldState, isAnchorTenant);
|
||||
}
|
||||
FeeTransformCommandExtension feeCreate =
|
||||
eppInput.getFirstExtensionOfClasses(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
|
||||
EppCommandOperations commandOperations = TldSpecificLogicProxy.getCreatePrice(
|
||||
registry, targetId, clientId, now, years, eppInput);
|
||||
validateFeeChallenge(
|
||||
targetId, registry.getTldStr(), now, feeCreate, commandOperations.getTotalCost());
|
||||
// Superusers can create reserved domains, force creations on domains that require a claims
|
||||
// notice without specifying a claims key, ignore the registry phase, and override blocks on
|
||||
// registering premium domains.
|
||||
if (!isSuperuser) {
|
||||
// Prohibit creating a domain if there is an open application for the same name.
|
||||
for (DomainApplication application : loadActiveApplicationsByDomainName(targetId, now)) {
|
||||
if (!application.getApplicationStatus().isFinalStatus()) {
|
||||
throw new DomainHasOpenApplicationsException();
|
||||
}
|
||||
checkAllowedAccessToTld(getAllowedTlds(), registry.getTldStr());
|
||||
if (launchCreate != null) {
|
||||
verifyLaunchPhaseMatchesRegistryPhase(registry, launchCreate, now);
|
||||
}
|
||||
// Prohibit registrations for non-qlp and non-superuser outside of GA.
|
||||
if (!isAnchorTenant()
|
||||
&& Registry.get(tld).getTldState(now) != TldState.GENERAL_AVAILABILITY) {
|
||||
throw new NoGeneralRegistrationsInCurrentPhaseException();
|
||||
if (!isAnchorTenant) {
|
||||
verifyNotReserved(domainName, hasSignedMarks);
|
||||
}
|
||||
if (hasClaimsNotice) {
|
||||
verifyClaimsPeriodNotEnded(registry, now);
|
||||
}
|
||||
if (now.isBefore(registry.getClaimsPeriodEnd())) {
|
||||
verifyClaimsNoticeIfAndOnlyIfNeeded(domainName, hasSignedMarks, hasClaimsNotice);
|
||||
}
|
||||
verifyPremiumNameIsNotBlocked(targetId, now, clientId);
|
||||
verifyNoOpenApplications();
|
||||
verifyIsGaOrIsSpecialCase(tldState, isAnchorTenant);
|
||||
}
|
||||
SecDnsCreateExtension secDnsCreate =
|
||||
validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class));
|
||||
String repoId = createDomainRoid(ObjectifyService.allocateId(), registry.getTldStr());
|
||||
DateTime registrationExpirationTime = leapSafeAddYears(now, years);
|
||||
HistoryEntry historyEntry = buildHistory(repoId, period);
|
||||
// Bill for the create.
|
||||
BillingEvent.OneTime createBillingEvent =
|
||||
createOneTimeBillingEvent(registry, isAnchorTenant, years, commandOperations, historyEntry);
|
||||
// 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);
|
||||
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
|
||||
entitiesToSave.add(
|
||||
historyEntry,
|
||||
createBillingEvent,
|
||||
autorenewBillingEvent,
|
||||
autorenewPollMessage);
|
||||
// Bill for EAP cost, if any.
|
||||
if (!commandOperations.getEapCost().isZero()) {
|
||||
entitiesToSave.add(createEapBillingEvent(commandOperations, createBillingEvent));
|
||||
}
|
||||
DomainResource.Builder domainBuilder = new DomainResource.Builder();
|
||||
command.applyTo(domainBuilder);
|
||||
DomainResource newDomain = domainBuilder
|
||||
.setCreationClientId(clientId)
|
||||
.setCurrentSponsorClientId(clientId)
|
||||
.setRepoId(repoId)
|
||||
.setIdnTableName(validateDomainNameWithIdnTables(domainName))
|
||||
.setRegistrationExpirationTime(registrationExpirationTime)
|
||||
.setAutorenewBillingEvent(Key.create(autorenewBillingEvent))
|
||||
.setAutorenewPollMessage(Key.create(autorenewPollMessage))
|
||||
.setLaunchNotice(hasClaimsNotice ? launchCreate.getNotice() : null)
|
||||
.setSmdId(hasSignedMarks
|
||||
// If a signed mark was provided, then it must match the desired domain label.
|
||||
? verifySignedMarks(launchCreate.getSignedMarks(), domainLabel, now).getId()
|
||||
: null)
|
||||
.setDsData(secDnsCreate == null ? null : secDnsCreate.getDsData())
|
||||
.addGracePeriod(GracePeriod.forBillingEvent(GracePeriodStatus.ADD, createBillingEvent))
|
||||
.build();
|
||||
handleExtraFlowLogic(registry.getTldStr(), years, historyEntry, newDomain);
|
||||
entitiesToSave.add(
|
||||
newDomain,
|
||||
ForeignKeyIndex.create(newDomain, newDomain.getDeletionTime()),
|
||||
EppResourceIndex.create(Key.create(newDomain)));
|
||||
// Anchor tenant registrations override LRP, and landrush applications can skip it.
|
||||
if (hasLrpToken(registry, isAnchorTenant)) {
|
||||
// TODO(b/32059212): This is a bug: empty tokens should still fail. Preserving to fix in a
|
||||
// separate targeted change.
|
||||
if (!authInfo.getPw().getValue().isEmpty()) {
|
||||
entitiesToSave.add(
|
||||
prepareMarkedLrpTokenEntity(authInfo.getPw().getValue(), domainName, historyEntry));
|
||||
}
|
||||
}
|
||||
enqueueTasks(hasSignedMarks, hasClaimsNotice, newDomain);
|
||||
ofy().save().entities(entitiesToSave.build());
|
||||
return createOutput(
|
||||
Result.Code.SUCCESS,
|
||||
DomainCreateData.create(targetId, now, registrationExpirationTime),
|
||||
createResponseExtensions(feeCreate, commandOperations));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void initDomainCreateOrAllocateFlow() {
|
||||
registerExtensions(LaunchCreateExtension.class);
|
||||
private boolean isAnchorTenant(InternetDomainName domainName) {
|
||||
MetadataExtension metadataExtension = eppInput.getSingleExtension(MetadataExtension.class);
|
||||
return matchesAnchorTenantReservation(domainName, authInfo.getPw().getValue())
|
||||
|| (metadataExtension != null && metadataExtension.getIsAnchorTenant());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void validateDomainLaunchCreateExtension() throws EppException {
|
||||
// We can assume launchCreate is not null here.
|
||||
// Only QLP domains can have a signed mark on a domain create, and only in sunrise or sunrush.
|
||||
if (hasSignedMarks) {
|
||||
if (isAnchorTenant() && QLP_SMD_ALLOWED_STATES.contains(
|
||||
Registry.get(getTld()).getTldState(now))) {
|
||||
return;
|
||||
}
|
||||
/** Only QLP domains can have a signed mark on a domain create, and only in sunrise or sunrush. */
|
||||
private void verifySignedMarksAllowed(TldState tldState, boolean isAnchorTenant)
|
||||
throws SignedMarksNotAcceptedInCurrentPhaseException {
|
||||
if (!isAnchorTenant || !QLP_SMD_ALLOWED_STATES.contains(tldState)) {
|
||||
throw new SignedMarksNotAcceptedInCurrentPhaseException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void setDomainCreateOrAllocateProperties(Builder builder) throws EppException {
|
||||
Registry registry = Registry.get(getTld());
|
||||
|
||||
// Bill for the create.
|
||||
BillingEvent.OneTime createEvent =
|
||||
new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.CREATE)
|
||||
.setTargetId(targetId)
|
||||
.setClientId(getClientId())
|
||||
.setPeriodYears(command.getPeriod().getValue())
|
||||
.setCost(commandOperations.getCreateCost())
|
||||
.setEventTime(now)
|
||||
.setBillingTime(now.plus(isAnchorTenant()
|
||||
? registry.getAnchorTenantAddGracePeriodLength()
|
||||
: registry.getAddGracePeriodLength()))
|
||||
.setFlags(isAnchorTenant()
|
||||
? ImmutableSet.of(BillingEvent.Flag.ANCHOR_TENANT)
|
||||
: ImmutableSet.<BillingEvent.Flag>of())
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
ofy().save().entity(createEvent);
|
||||
|
||||
// Bill for EAP cost, if any.
|
||||
if (!commandOperations.getEapCost().isZero()) {
|
||||
BillingEvent.OneTime eapEvent =
|
||||
new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.FEE_EARLY_ACCESS)
|
||||
.setTargetId(createEvent.getTargetId())
|
||||
.setClientId(createEvent.getClientId())
|
||||
.setCost(commandOperations.getEapCost())
|
||||
.setEventTime(createEvent.getEventTime())
|
||||
.setBillingTime(createEvent.getBillingTime())
|
||||
.setFlags(createEvent.getFlags())
|
||||
.setParent(createEvent.getParentKey())
|
||||
.build();
|
||||
ofy().save().entity(eapEvent);
|
||||
/** Prohibit creating a domain if there is an open application for the same name. */
|
||||
private void verifyNoOpenApplications() throws DomainHasOpenApplicationsException {
|
||||
for (DomainApplication application : loadActiveApplicationsByDomainName(targetId, now)) {
|
||||
if (!application.getApplicationStatus().isFinalStatus()) {
|
||||
throw new DomainHasOpenApplicationsException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
builder.addGracePeriod(GracePeriod.forBillingEvent(GracePeriodStatus.ADD, createEvent));
|
||||
if (launchCreate != null && (launchCreate.getNotice() != null || hasSignedMarks)) {
|
||||
builder
|
||||
.setLaunchNotice(launchCreate.getNotice())
|
||||
.setSmdId(signedMark == null ? null : signedMark.getId());
|
||||
/** Prohibit registrations for non-qlp and non-superuser outside of GA. **/
|
||||
private void verifyIsGaOrIsSpecialCase(TldState tldState, boolean isAnchorTenant)
|
||||
throws NoGeneralRegistrationsInCurrentPhaseException {
|
||||
if (!isAnchorTenant && tldState != TldState.GENERAL_AVAILABILITY) {
|
||||
throw new NoGeneralRegistrationsInCurrentPhaseException();
|
||||
}
|
||||
// Handle extra flow logic, if any. The initialization and commit are performed higher up in the
|
||||
// flow hierarchy, in BaseDomainCreateFlow.
|
||||
}
|
||||
|
||||
private HistoryEntry buildHistory(String repoId, Period period) {
|
||||
return historyBuilder
|
||||
.setType(HistoryEntry.Type.DOMAIN_CREATE)
|
||||
.setPeriod(period)
|
||||
.setModificationTime(now)
|
||||
.setParent(Key.create(DomainResource.class, repoId))
|
||||
.build();
|
||||
}
|
||||
|
||||
private OneTime createOneTimeBillingEvent(
|
||||
Registry registry,
|
||||
boolean isAnchorTenant,
|
||||
int years,
|
||||
EppCommandOperations commandOperations,
|
||||
HistoryEntry historyEntry) {
|
||||
return new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.CREATE)
|
||||
.setTargetId(targetId)
|
||||
.setClientId(clientId)
|
||||
.setPeriodYears(years)
|
||||
.setCost(commandOperations.getCreateCost())
|
||||
.setEventTime(now)
|
||||
.setBillingTime(now.plus(isAnchorTenant
|
||||
? registry.getAnchorTenantAddGracePeriodLength()
|
||||
: registry.getAddGracePeriodLength()))
|
||||
.setFlags(isAnchorTenant
|
||||
? ImmutableSet.of(BillingEvent.Flag.ANCHOR_TENANT)
|
||||
: ImmutableSet.<BillingEvent.Flag>of())
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
}
|
||||
|
||||
private 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 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 OneTime createEapBillingEvent(EppCommandOperations commandOperations,
|
||||
BillingEvent.OneTime createBillingEvent) {
|
||||
return new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.FEE_EARLY_ACCESS)
|
||||
.setTargetId(createBillingEvent.getTargetId())
|
||||
.setClientId(createBillingEvent.getClientId())
|
||||
.setCost(commandOperations.getEapCost())
|
||||
.setEventTime(createBillingEvent.getEventTime())
|
||||
.setBillingTime(createBillingEvent.getBillingTime())
|
||||
.setFlags(createBillingEvent.getFlags())
|
||||
.setParent(createBillingEvent.getParentKey())
|
||||
.build();
|
||||
}
|
||||
|
||||
private boolean hasLrpToken(Registry registry, boolean isAnchorTenant) {
|
||||
return registry.getLrpPeriod().contains(now) && !isAnchorTenant;
|
||||
}
|
||||
|
||||
private void handleExtraFlowLogic(
|
||||
String tld, int years, HistoryEntry historyEntry, DomainResource newDomain)
|
||||
throws EppException {
|
||||
Optional<RegistryExtraFlowLogic> extraFlowLogic =
|
||||
RegistryExtraFlowLogicProxy.newInstanceForTld(tld);
|
||||
if (extraFlowLogic.isPresent()) {
|
||||
extraFlowLogic.get().performAdditionalDomainCreateLogic(
|
||||
existingResource,
|
||||
getClientId(),
|
||||
newDomain,
|
||||
clientId,
|
||||
now,
|
||||
command.getPeriod().getValue(),
|
||||
years,
|
||||
eppInput,
|
||||
historyEntry);
|
||||
extraFlowLogic.get().commitAdditionalLogicChanges();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enqueueLordnTaskIfNeeded() {
|
||||
if (launchCreate != null && (launchCreate.getNotice() != null || hasSignedMarks)) {
|
||||
LordnTask.enqueueDomainResourceTask(newResource);
|
||||
private void enqueueTasks(
|
||||
boolean hasSignedMarks, boolean hasClaimsNotice, DomainResource newDomain) {
|
||||
if (newDomain.shouldPublishToDns()) {
|
||||
DnsQueue.create().addDomainRefreshTask(newDomain.getFullyQualifiedDomainName());
|
||||
}
|
||||
if (hasClaimsNotice || hasSignedMarks) {
|
||||
LordnTask.enqueueDomainResourceTask(newDomain);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final HistoryEntry.Type getHistoryEntryType() {
|
||||
return HistoryEntry.Type.DOMAIN_CREATE;
|
||||
private ImmutableList<FeeTransformResponseExtension> createResponseExtensions(
|
||||
FeeTransformCommandExtension feeCreate, EppCommandOperations commandOperations) {
|
||||
return (feeCreate == null)
|
||||
? null
|
||||
: ImmutableList.of(createFeeCreateResponse(feeCreate, commandOperations));
|
||||
}
|
||||
|
||||
/** There is an open application for this domain. */
|
||||
|
|
|
@ -1,119 +0,0 @@
|
|||
// Copyright 2016 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.model.ofy.ObjectifyService.ofy;
|
||||
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.googlecode.objectify.Key;
|
||||
import google.registry.dns.DnsQueue;
|
||||
import google.registry.flows.EppException;
|
||||
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.DomainResource;
|
||||
import google.registry.model.domain.DomainResource.Builder;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.eppoutput.CreateData.DomainCreateData;
|
||||
import google.registry.model.eppoutput.EppOutput;
|
||||
import google.registry.model.eppoutput.Result;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** An EPP flow that creates or allocates a new domain resource. */
|
||||
public abstract class DomainCreateOrAllocateFlow
|
||||
extends BaseDomainCreateFlow<DomainResource, Builder> {
|
||||
|
||||
protected boolean isAnchorTenantViaExtension;
|
||||
|
||||
@Inject DnsQueue dnsQueue;
|
||||
|
||||
@Override
|
||||
protected final void initDomainCreateFlow() {
|
||||
isAnchorTenantViaExtension =
|
||||
(metadataExtension != null && metadataExtension.getIsAnchorTenant());
|
||||
initDomainCreateOrAllocateFlow();
|
||||
}
|
||||
|
||||
protected abstract void initDomainCreateOrAllocateFlow();
|
||||
|
||||
@Override
|
||||
protected final void setDomainCreateProperties(Builder builder) throws EppException {
|
||||
DateTime registrationExpirationTime = leapSafeAddYears(now, command.getPeriod().getValue());
|
||||
// Create a new autorenew billing event and poll message starting at the expiration time.
|
||||
BillingEvent.Recurring autorenewEvent = new BillingEvent.Recurring.Builder()
|
||||
.setReason(Reason.RENEW)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setTargetId(targetId)
|
||||
.setClientId(getClientId())
|
||||
.setEventTime(registrationExpirationTime)
|
||||
.setRecurrenceEndTime(END_OF_TIME)
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
PollMessage.Autorenew autorenewPollMessage = new PollMessage.Autorenew.Builder()
|
||||
.setTargetId(targetId)
|
||||
.setClientId(getClientId())
|
||||
.setEventTime(registrationExpirationTime)
|
||||
.setMsg("Domain was auto-renewed.")
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
ofy().save().<Object>entities(autorenewEvent, autorenewPollMessage);
|
||||
|
||||
builder
|
||||
.setRegistrationExpirationTime(registrationExpirationTime)
|
||||
.setAutorenewBillingEvent(Key.create(autorenewEvent))
|
||||
.setAutorenewPollMessage(Key.create(autorenewPollMessage));
|
||||
setDomainCreateOrAllocateProperties(builder);
|
||||
}
|
||||
|
||||
/** Subclasses must override this to set more fields, like any grace period. */
|
||||
protected abstract void setDomainCreateOrAllocateProperties(Builder builder) throws EppException;
|
||||
|
||||
@Override
|
||||
protected final void enqueueTasks() {
|
||||
if (newResource.shouldPublishToDns()) {
|
||||
dnsQueue.addDomainRefreshTask(newResource.getFullyQualifiedDomainName());
|
||||
}
|
||||
enqueueLordnTaskIfNeeded();
|
||||
}
|
||||
|
||||
/** Subclasses must override this to enqueue any additional tasks. */
|
||||
protected abstract void enqueueLordnTaskIfNeeded();
|
||||
|
||||
@Override
|
||||
protected final Period getCommandPeriod() {
|
||||
return command.getPeriod();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final EppOutput getOutput() {
|
||||
return createOutput(
|
||||
Result.Code.SUCCESS,
|
||||
DomainCreateData.create(
|
||||
newResource.getFullyQualifiedDomainName(),
|
||||
now,
|
||||
newResource.getRegistrationExpirationTime()),
|
||||
(feeCreate == null) ? null : ImmutableList.of(
|
||||
feeCreate.createResponseBuilder()
|
||||
.setCurrency(commandOperations.getCurrency())
|
||||
.setFees(commandOperations.getFees())
|
||||
.setCredits(commandOperations.getCredits())
|
||||
.build()));
|
||||
}
|
||||
}
|
|
@ -22,6 +22,8 @@ import static com.google.common.collect.Iterables.concat;
|
|||
import static com.google.common.collect.Sets.difference;
|
||||
import static com.google.common.collect.Sets.union;
|
||||
import static google.registry.flows.EppXmlTransformer.unmarshal;
|
||||
import static google.registry.flows.domain.TldSpecificLogicProxy.getMatchingLrpToken;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.model.registry.Registries.findTldForName;
|
||||
import static google.registry.model.registry.label.ReservedList.getReservation;
|
||||
|
@ -42,9 +44,11 @@ import com.google.common.collect.ImmutableSet;
|
|||
import com.google.common.collect.Sets;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.Work;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.AuthorizationErrorException;
|
||||
import google.registry.flows.EppException.CommandUseErrorException;
|
||||
import google.registry.flows.EppException.InvalidAuthorizationInformationErrorException;
|
||||
import google.registry.flows.EppException.ObjectDoesNotExistException;
|
||||
import google.registry.flows.EppException.ParameterValuePolicyErrorException;
|
||||
import google.registry.flows.EppException.ParameterValueRangeErrorException;
|
||||
|
@ -52,6 +56,8 @@ import google.registry.flows.EppException.ParameterValueSyntaxErrorException;
|
|||
import google.registry.flows.EppException.RequiredParameterMissingException;
|
||||
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
import google.registry.flows.EppException.UnimplementedOptionException;
|
||||
import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations;
|
||||
import google.registry.flows.exceptions.ResourceAlreadyExistsException;
|
||||
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException;
|
||||
import google.registry.flows.exceptions.StatusNotClientSettableException;
|
||||
import google.registry.model.EppResource;
|
||||
|
@ -63,19 +69,27 @@ 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;
|
||||
import google.registry.model.domain.DomainCommand.InvalidReferencesException;
|
||||
import google.registry.model.domain.DomainCommand.Update;
|
||||
import google.registry.model.domain.DomainResource;
|
||||
import google.registry.model.domain.LrpTokenEntity;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.domain.fee.Credit;
|
||||
import google.registry.model.domain.fee.Fee;
|
||||
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem;
|
||||
import google.registry.model.domain.fee.FeeQueryResponseExtensionItem;
|
||||
import google.registry.model.domain.fee.FeeTransformCommandExtension;
|
||||
import google.registry.model.domain.fee.FeeTransformResponseExtension;
|
||||
import google.registry.model.domain.launch.LaunchCreateExtension;
|
||||
import google.registry.model.domain.launch.LaunchExtension;
|
||||
import google.registry.model.domain.launch.LaunchNotice;
|
||||
import google.registry.model.domain.launch.LaunchNotice.InvalidChecksumException;
|
||||
import google.registry.model.domain.launch.LaunchPhase;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.domain.secdns.SecDnsCreateExtension;
|
||||
import google.registry.model.domain.secdns.SecDnsInfoExtension;
|
||||
import google.registry.model.domain.secdns.SecDnsUpdateExtension;
|
||||
import google.registry.model.domain.secdns.SecDnsUpdateExtension.Add;
|
||||
|
@ -98,6 +112,7 @@ import google.registry.model.smd.AbstractSignedMark;
|
|||
import google.registry.model.smd.EncodedSignedMark;
|
||||
import google.registry.model.smd.SignedMark;
|
||||
import google.registry.model.smd.SignedMarkRevocationList;
|
||||
import google.registry.model.tmch.ClaimsListShard;
|
||||
import google.registry.model.transfer.TransferData;
|
||||
import google.registry.model.transfer.TransferResponse.DomainTransferResponse;
|
||||
import google.registry.tmch.TmchXmlSignature;
|
||||
|
@ -373,11 +388,12 @@ public class DomainFlowUtils {
|
|||
}
|
||||
|
||||
/** Verifies that a launch extension's specified phase matches the specified registry's phase. */
|
||||
static void verifyLaunchPhase(
|
||||
String tld, LaunchExtension launchExtension, DateTime now) throws EppException {
|
||||
static void verifyLaunchPhaseMatchesRegistryPhase(
|
||||
Registry registry, LaunchExtension launchExtension, DateTime now) throws EppException {
|
||||
if (!Objects.equals(
|
||||
Registry.get(tld).getTldState(now),
|
||||
registry.getTldState(now),
|
||||
LAUNCH_PHASE_TO_TLD_STATE.get(launchExtension.getPhase()))) {
|
||||
// No launch operations are allowed during the quiet period or predelegation.
|
||||
throw new LaunchPhaseMismatchException();
|
||||
}
|
||||
}
|
||||
|
@ -835,6 +851,7 @@ public class DomainFlowUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/** Check that the registry phase is not incompatible with launch extension flows. */
|
||||
static void verifyRegistryStateAllowsLaunchFlows(Registry registry, DateTime now)
|
||||
throws BadCommandForRegistryPhaseException {
|
||||
if (DISALLOWED_TLD_STATES_FOR_LAUNCH_FLOWS.contains(registry.getTldState(now))) {
|
||||
|
@ -842,6 +859,7 @@ public class DomainFlowUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/** Check that the registry phase is not predelegation, during which some flows are forbidden. */
|
||||
static void verifyNotInPredelegation(Registry registry, DateTime now)
|
||||
throws BadCommandForRegistryPhaseException {
|
||||
if (registry.getTldState(now) == TldState.PREDELEGATION) {
|
||||
|
@ -849,6 +867,154 @@ public class DomainFlowUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/** Validate the contacts and nameservers specified in a domain or application create command. */
|
||||
static void validateCreateCommandContactsAndNameservers(Create command, String tld)
|
||||
throws EppException {
|
||||
verifyNotInPendingDelete(
|
||||
command.getContacts(),
|
||||
command.getRegistrant(),
|
||||
command.getNameservers());
|
||||
validateContactsHaveTypes(command.getContacts());
|
||||
validateRegistrantAllowedOnTld(tld, command.getRegistrantContactId());
|
||||
validateNoDuplicateContacts(command.getContacts());
|
||||
validateRequiredContactsPresent(command.getRegistrant(), command.getContacts());
|
||||
Set<String> fullyQualifiedHostNames =
|
||||
nullToEmpty(command.getNameserverFullyQualifiedHostNames());
|
||||
validateNameserversCountForTld(tld, fullyQualifiedHostNames.size());
|
||||
validateNameserversAllowedOnTld(tld, fullyQualifiedHostNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fail a domain or application create very fast if the domain is already registered.
|
||||
*
|
||||
* <p>Try to load the domain non-transactionally, since this can hit memcache. If we succeed, and
|
||||
* the domain is not in the add grace period (the only state that allows instantaneous transition
|
||||
* to being deleted), we can assume that the domain will not be deleted (and therefore won't be
|
||||
* creatable) until its deletion time. For repeated failed creates this means we can avoid the
|
||||
* Datastore lookup, which is very expensive (and first-seen failed creates are no worse than they
|
||||
* otherwise would be). This comes at the cost of the extra lookup for successful creates (or
|
||||
* rather, those that don't fail due to the domain existing) and also for failed creates within
|
||||
* the existing domain's add grace period.
|
||||
*/
|
||||
static void failfastForCreate(final String targetId, final DateTime now) throws EppException {
|
||||
// Enter a transactionless context briefly.
|
||||
DomainResource domain = ofy().doTransactionless(new Work<DomainResource>() {
|
||||
@Override
|
||||
public DomainResource run() {
|
||||
// This is cacheable because we are outside of a transaction.
|
||||
return loadByForeignKey(DomainResource.class, targetId, now);
|
||||
}});
|
||||
// If the domain exists already and isn't in the add grace period then there is no way it will
|
||||
// be suddenly deleted and therefore the create must fail.
|
||||
if (domain != null && !domain.getGracePeriodStatuses().contains(GracePeriodStatus.ADD)) {
|
||||
throw new ResourceAlreadyExistsException(targetId, true);
|
||||
}
|
||||
}
|
||||
|
||||
/** Validate the secDNS extension, if present. */
|
||||
static SecDnsCreateExtension validateSecDnsExtension(SecDnsCreateExtension secDnsCreate)
|
||||
throws EppException {
|
||||
if (secDnsCreate == null) {
|
||||
return null;
|
||||
}
|
||||
if (secDnsCreate.getDsData() == null) {
|
||||
throw new DsDataRequiredException();
|
||||
}
|
||||
if (secDnsCreate.getMaxSigLife() != null) {
|
||||
throw new MaxSigLifeNotSupportedException();
|
||||
}
|
||||
validateDsData(secDnsCreate.getDsData());
|
||||
return secDnsCreate;
|
||||
}
|
||||
|
||||
/** Validate the notice from a launch create extension, allowing null as a valid notice. */
|
||||
static void validateLaunchCreateNotice(
|
||||
@Nullable LaunchNotice notice,
|
||||
String domainLabel,
|
||||
boolean isSuperuser,
|
||||
DateTime now) throws EppException {
|
||||
if (notice == null) {
|
||||
return;
|
||||
}
|
||||
if (!notice.getNoticeId().getValidatorId().equals("tmch")) {
|
||||
throw new InvalidTrademarkValidatorException();
|
||||
}
|
||||
// Superuser can force domain creations regardless of the current date.
|
||||
if (!isSuperuser) {
|
||||
if (notice.getExpirationTime().isBefore(now)) {
|
||||
throw new ExpiredClaimException();
|
||||
}
|
||||
// An acceptance within the past 48 hours is mandated by the TMCH Functional Spec.
|
||||
if (notice.getAcceptedTime().isBefore(now.minusHours(48))) {
|
||||
throw new AcceptedTooLongAgoException();
|
||||
}
|
||||
}
|
||||
try {
|
||||
notice.validate(domainLabel);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new MalformedTcnIdException();
|
||||
} catch (InvalidChecksumException e) {
|
||||
throw new InvalidTcnIdChecksumException();
|
||||
}
|
||||
}
|
||||
|
||||
/** Check that the claims period hasn't ended. */
|
||||
static void verifyClaimsPeriodNotEnded(Registry registry, DateTime now)
|
||||
throws ClaimsPeriodEndedException {
|
||||
if (isAtOrAfter(now, registry.getClaimsPeriodEnd())) {
|
||||
throw new ClaimsPeriodEndedException(registry.getTldStr());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
static void verifyClaimsNoticeIfAndOnlyIfNeeded(
|
||||
InternetDomainName domainName,
|
||||
boolean hasSignedMarks,
|
||||
boolean hasClaimsNotice) throws EppException {
|
||||
boolean isInClaimsList = ClaimsListShard.get().getClaimKey(domainName.parts().get(0)) != null;
|
||||
if (hasClaimsNotice && !isInClaimsList) {
|
||||
throw new UnexpectedClaimsNoticeException(domainName.toString());
|
||||
}
|
||||
if (!hasClaimsNotice && isInClaimsList && !hasSignedMarks) {
|
||||
throw new MissingClaimsNoticeException(domainName.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/** Create a {@link LrpTokenEntity} object that records this LRP registration. */
|
||||
static LrpTokenEntity prepareMarkedLrpTokenEntity(
|
||||
String lrpTokenString, InternetDomainName domainName, HistoryEntry historyEntry)
|
||||
throws InvalidLrpTokenException {
|
||||
Optional<LrpTokenEntity> lrpToken = getMatchingLrpToken(lrpTokenString, domainName);
|
||||
if (!lrpToken.isPresent()) {
|
||||
throw new InvalidLrpTokenException();
|
||||
}
|
||||
return lrpToken.get().asBuilder()
|
||||
.setRedemptionHistoryEntry(Key.create(historyEntry))
|
||||
.build();
|
||||
}
|
||||
|
||||
/** Check that there are no code marks, which is a type of mark we don't support. */
|
||||
static void verifyNoCodeMarks(LaunchCreateExtension launchCreate)
|
||||
throws UnsupportedMarkTypeException {
|
||||
if (launchCreate.hasCodeMarks()) {
|
||||
throw new UnsupportedMarkTypeException();
|
||||
}
|
||||
}
|
||||
|
||||
/** Create a response extension listign the fees on a domain or application create. */
|
||||
static FeeTransformResponseExtension createFeeCreateResponse(
|
||||
FeeTransformCommandExtension feeCreate,
|
||||
EppCommandOperations commandOperations) {
|
||||
return feeCreate.createResponseBuilder()
|
||||
.setCurrency(commandOperations.getCurrency())
|
||||
.setFees(commandOperations.getFees())
|
||||
.setCredits(commandOperations.getCredits())
|
||||
.build();
|
||||
}
|
||||
|
||||
/** Encoded signed marks must use base64 encoding. */
|
||||
static class Base64RequiredForEncodedSignedMarksException
|
||||
extends ParameterValuePolicyErrorException {
|
||||
|
@ -1239,6 +1405,13 @@ public class DomainFlowUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/** At least one dsData is required when using the secDNS extension. */
|
||||
static class DsDataRequiredException extends ParameterValuePolicyErrorException {
|
||||
public DsDataRequiredException() {
|
||||
super("At least one dsData is required when using the secDNS extension");
|
||||
}
|
||||
}
|
||||
|
||||
/** The 'urgent' attribute is not supported. */
|
||||
static class UrgentAttributeNotSupportedException extends UnimplementedOptionException {
|
||||
public UrgentAttributeNotSupportedException() {
|
||||
|
@ -1246,10 +1419,88 @@ public class DomainFlowUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/** The 'maxSigLife' setting is not supported. */
|
||||
static class MaxSigLifeNotSupportedException extends UnimplementedOptionException {
|
||||
public MaxSigLifeNotSupportedException() {
|
||||
super("The 'maxSigLife' setting is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
/** Changing 'maxSigLife' is not supported. */
|
||||
static class MaxSigLifeChangeNotSupportedException extends UnimplementedOptionException {
|
||||
public MaxSigLifeChangeNotSupportedException() {
|
||||
super("Changing 'maxSigLife' is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
/** The specified trademark validator is not supported. */
|
||||
static class InvalidTrademarkValidatorException extends ParameterValuePolicyErrorException {
|
||||
public InvalidTrademarkValidatorException() {
|
||||
super("The only supported validationID is 'tmch' for the ICANN Trademark Clearinghouse.");
|
||||
}
|
||||
}
|
||||
|
||||
/** The expiration time specified in the claim notice has elapsed. */
|
||||
static class ExpiredClaimException extends ParameterValueRangeErrorException {
|
||||
public ExpiredClaimException() {
|
||||
super("The expiration time specified in the claim notice has elapsed");
|
||||
}
|
||||
}
|
||||
|
||||
/** The acceptance time specified in the claim notice is more than 48 hours in the past. */
|
||||
static class AcceptedTooLongAgoException extends ParameterValueRangeErrorException {
|
||||
public AcceptedTooLongAgoException() {
|
||||
super("The acceptance time specified in the claim notice is more than 48 hours in the past");
|
||||
}
|
||||
}
|
||||
|
||||
/** The specified TCNID is invalid. */
|
||||
static class MalformedTcnIdException extends ParameterValueSyntaxErrorException {
|
||||
public MalformedTcnIdException() {
|
||||
super("The specified TCNID is malformed");
|
||||
}
|
||||
}
|
||||
|
||||
/** The checksum in the specified TCNID does not validate. */
|
||||
static class InvalidTcnIdChecksumException extends ParameterValueRangeErrorException {
|
||||
public InvalidTcnIdChecksumException() {
|
||||
super("The checksum in the specified TCNID does not validate");
|
||||
}
|
||||
}
|
||||
|
||||
/** The claims period for this TLD has ended. */
|
||||
static class ClaimsPeriodEndedException extends StatusProhibitsOperationException {
|
||||
public ClaimsPeriodEndedException(String tld) {
|
||||
super(String.format("The claims period for %s has ended", tld));
|
||||
}
|
||||
}
|
||||
|
||||
/** Requested domain requires a claims notice. */
|
||||
static class MissingClaimsNoticeException extends StatusProhibitsOperationException {
|
||||
public MissingClaimsNoticeException(String domainName) {
|
||||
super(String.format("%s requires a claims notice", domainName));
|
||||
}
|
||||
}
|
||||
|
||||
/** Requested domain does not require a claims notice. */
|
||||
static class UnexpectedClaimsNoticeException extends StatusProhibitsOperationException {
|
||||
public UnexpectedClaimsNoticeException(String domainName) {
|
||||
super(String.format("%s does not require a claims notice", domainName));
|
||||
}
|
||||
}
|
||||
|
||||
/** Invalid limited registration period token. */
|
||||
static class InvalidLrpTokenException
|
||||
extends InvalidAuthorizationInformationErrorException {
|
||||
public InvalidLrpTokenException() {
|
||||
super("Invalid limited registration period token");
|
||||
}
|
||||
}
|
||||
|
||||
/** Only encoded signed marks are supported. */
|
||||
static class UnsupportedMarkTypeException extends ParameterValuePolicyErrorException {
|
||||
public UnsupportedMarkTypeException() {
|
||||
super("Only encoded signed marks are supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,11 +23,11 @@ import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
|||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.domain.DomainCommand.Create;
|
||||
import google.registry.model.domain.DomainResource;
|
||||
import google.registry.model.domain.LrpTokenEntity;
|
||||
import google.registry.model.domain.fee.BaseFee;
|
||||
|
@ -266,23 +266,25 @@ public final class TldSpecificLogicProxy {
|
|||
}
|
||||
|
||||
/**
|
||||
* Checks whether a {@link Create} command has a valid {@link LrpTokenEntity} for a particular
|
||||
* TLD, and return that token (wrapped in an {@link Optional}) if one exists.
|
||||
* Checks whether an LRP token String maps to a valid {@link LrpTokenEntity} for the domain name's
|
||||
* TLD, and return that entity (wrapped in an {@link Optional}) if one exists.
|
||||
*
|
||||
* <p>This method has no knowledge of whether or not an auth code (interpreted here as an LRP
|
||||
* token) has already been checked against the reserved list for QLP (anchor tenant), as auth
|
||||
* codes are used for both types of registrations.
|
||||
*/
|
||||
public static Optional<LrpTokenEntity> getMatchingLrpToken(Create createCommand, String tld) {
|
||||
public static Optional<LrpTokenEntity> getMatchingLrpToken(
|
||||
String lrpToken, InternetDomainName domainName) {
|
||||
// Note that until the actual per-TLD logic is built out, what's being done here is a basic
|
||||
// domain-name-to-assignee match.
|
||||
String lrpToken = createCommand.getAuthInfo().getPw().getValue();
|
||||
LrpTokenEntity token = ofy().load().key(Key.create(LrpTokenEntity.class, lrpToken)).now();
|
||||
if (token != null) {
|
||||
if (token.getAssignee().equalsIgnoreCase(createCommand.getFullyQualifiedDomainName())
|
||||
&& token.getRedemptionHistoryEntry() == null
|
||||
&& token.getValidTlds().contains(tld)) {
|
||||
return Optional.of(token);
|
||||
if (!lrpToken.isEmpty()) {
|
||||
LrpTokenEntity token = ofy().load().key(Key.create(LrpTokenEntity.class, lrpToken)).now();
|
||||
if (token != null) {
|
||||
if (token.getAssignee().equalsIgnoreCase(domainName.toString())
|
||||
&& token.getRedemptionHistoryEntry() == null
|
||||
&& token.getValidTlds().contains(domainName.parent().toString())) {
|
||||
return Optional.of(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Optional.<LrpTokenEntity>absent();
|
||||
|
|
|
@ -28,6 +28,7 @@ import google.registry.flows.EppException.UnimplementedExtensionException;
|
|||
import google.registry.flows.EppException.UnimplementedObjectServiceException;
|
||||
import google.registry.flows.EppException.UnimplementedOptionException;
|
||||
import google.registry.flows.Flow;
|
||||
import google.registry.flows.FlowModule.ClientId;
|
||||
import google.registry.model.eppcommon.ProtocolDefinition;
|
||||
import google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension;
|
||||
import google.registry.model.eppinput.EppInput.Login;
|
||||
|
@ -67,6 +68,7 @@ public class LoginFlow extends Flow {
|
|||
/** Maximum number of failed login attempts allowed per connection. */
|
||||
private static final int MAX_FAILED_LOGIN_ATTEMPTS_PER_CONNECTION = 3;
|
||||
|
||||
@Inject @ClientId String clientId;
|
||||
@Inject LoginFlow() {}
|
||||
|
||||
/** Run the flow and log errors. */
|
||||
|
@ -83,7 +85,7 @@ public class LoginFlow extends Flow {
|
|||
/** Run the flow without bothering to log errors. The {@link #run} method will do that for us. */
|
||||
public final EppOutput runWithoutLogging() throws EppException {
|
||||
Login login = (Login) eppInput.getCommandWrapper().getCommand();
|
||||
if (getClientId() != null) {
|
||||
if (!clientId.isEmpty()) {
|
||||
throw new AlreadyLoggedInException();
|
||||
}
|
||||
Options options = login.getOptions();
|
||||
|
|
|
@ -573,7 +573,7 @@ public class Registry extends ImmutableObject implements Buildable {
|
|||
|
||||
public Interval getLrpPeriod() {
|
||||
return (lrpPeriodStart == null && lrpPeriodEnd == null)
|
||||
? new Interval(START_OF_TIME, Duration.ZERO)
|
||||
? new Interval(START_OF_TIME, Duration.ZERO) // An empty duration.
|
||||
: new Interval(lrpPeriodStart, lrpPeriodEnd);
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ import com.google.common.cache.CacheLoader;
|
|||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.VoidWork;
|
||||
|
@ -174,8 +175,10 @@ public final class ReservedList
|
|||
* Returns true if the given label and TLD is reserved for an anchor tenant, and the given
|
||||
* auth code matches the one set on the reservation.
|
||||
*/
|
||||
public static boolean matchesAnchorTenantReservation(String label, String tld, String authCode) {
|
||||
ReservedListEntry entry = getReservedListEntry(label, tld);
|
||||
public static boolean matchesAnchorTenantReservation(
|
||||
InternetDomainName domainName, String authCode) {
|
||||
ReservedListEntry entry =
|
||||
getReservedListEntry(domainName.parts().get(0), domainName.parent().toString());
|
||||
return entry != null
|
||||
&& entry.reservationType == RESERVED_FOR_ANCHOR_TENANT
|
||||
&& Objects.equals(entry.getAuthCode(), authCode);
|
||||
|
|
|
@ -337,7 +337,7 @@ public class EppLifecycleDomainTest extends EppTestCase {
|
|||
DateTime.parse("2001-01-01T00:01:00Z"));
|
||||
assertCommandAndResponse(
|
||||
"poll_ack.xml",
|
||||
ImmutableMap.of("ID", "1-B-EXAMPLE-17-23"),
|
||||
ImmutableMap.of("ID", "1-C-EXAMPLE-17-23"),
|
||||
"poll_ack_response_empty.xml",
|
||||
null,
|
||||
DateTime.parse("2001-01-01T00:01:00Z"));
|
||||
|
@ -349,7 +349,7 @@ public class EppLifecycleDomainTest extends EppTestCase {
|
|||
DateTime.parse("2001-01-06T00:01:00Z"));
|
||||
assertCommandAndResponse(
|
||||
"poll_ack.xml",
|
||||
ImmutableMap.of("ID", "1-B-EXAMPLE-17-22"),
|
||||
ImmutableMap.of("ID", "1-C-EXAMPLE-17-22"),
|
||||
"poll_ack_response_empty.xml",
|
||||
null,
|
||||
DateTime.parse("2001-01-06T00:01:00Z"));
|
||||
|
@ -365,7 +365,7 @@ public class EppLifecycleDomainTest extends EppTestCase {
|
|||
DateTime.parse("2001-01-06T00:02:00Z"));
|
||||
assertCommandAndResponse(
|
||||
"poll_ack.xml",
|
||||
ImmutableMap.of("ID", "1-B-EXAMPLE-17-21"),
|
||||
ImmutableMap.of("ID", "1-C-EXAMPLE-17-21"),
|
||||
"poll_ack_response_empty.xml",
|
||||
null,
|
||||
DateTime.parse("2001-01-06T00:02:00Z"));
|
||||
|
|
|
@ -22,8 +22,8 @@ import com.google.common.collect.ImmutableMap;
|
|||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.flows.ResourceFlowTestCase;
|
||||
import google.registry.flows.domain.ClaimsCheckFlow.ClaimsCheckNotAllowedInSunrise;
|
||||
import google.registry.flows.domain.ClaimsCheckFlow.ClaimsPeriodEndedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.BadCommandForRegistryPhaseException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.ClaimsPeriodEndedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.TldDoesNotExistException;
|
||||
import google.registry.flows.exceptions.TooManyResourceChecksException;
|
||||
|
|
|
@ -41,12 +41,11 @@ import com.google.common.collect.ImmutableList;
|
|||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.flows.ResourceCreateFlow.ResourceAlreadyExistsException;
|
||||
import google.registry.flows.ResourceFlowTestCase;
|
||||
import google.registry.flows.domain.DomainAllocateFlow.HasFinalStatusException;
|
||||
import google.registry.flows.domain.DomainAllocateFlow.MissingApplicationException;
|
||||
import google.registry.flows.domain.DomainAllocateFlow.OnlySuperuserCanAllocateException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
|
||||
import google.registry.flows.exceptions.ResourceAlreadyExistsException;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Flag;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
|
@ -64,12 +63,10 @@ import google.registry.model.eppcommon.Trid;
|
|||
import google.registry.model.ofy.ObjectifyService;
|
||||
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.Registry.TldState;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.smd.EncodedSignedMark;
|
||||
import google.registry.testing.DatastoreHelper;
|
||||
import google.registry.testing.TaskQueueHelper.TaskMatcher;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
@ -109,6 +106,7 @@ public class DomainAllocateFlowTest
|
|||
application = persistResource(newDomainApplication(domainName).asBuilder()
|
||||
.setCreationTrid(TRID)
|
||||
.setEncodedSignedMarks(ImmutableList.of(EncodedSignedMark.create("base64", "abcdef")))
|
||||
.setCreationTrid(TRID)
|
||||
.build());
|
||||
for (int i = 1; i <= 14; ++i) {
|
||||
persistActiveHost(String.format("ns%d.example.net", i));
|
||||
|
@ -478,18 +476,6 @@ public class DomainAllocateFlowTest
|
|||
runFlowAsSuperuser();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_notAuthorizedForTld() throws Exception {
|
||||
setupDomainApplication("tld", TldState.QUIET_PERIOD);
|
||||
DatastoreHelper.persistResource(
|
||||
Registrar.loadByClientId("TheRegistrar")
|
||||
.asBuilder()
|
||||
.setAllowedTlds(ImmutableSet.<String>of())
|
||||
.build());
|
||||
thrown.expect(NotAuthorizedForTldException.class);
|
||||
runFlow();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_onlySuperuserCanAllocate() throws Exception {
|
||||
setupDomainApplication("tld", TldState.GENERAL_AVAILABILITY);
|
||||
|
|
|
@ -43,28 +43,18 @@ import com.google.common.collect.ImmutableSet;
|
|||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.flows.EppException.UnimplementedExtensionException;
|
||||
import google.registry.flows.ResourceCreateFlow.ResourceAlreadyExistsException;
|
||||
import google.registry.flows.ResourceFlowTestCase;
|
||||
import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
|
||||
import google.registry.flows.domain.BaseDomainCreateFlow.AcceptedTooLongAgoException;
|
||||
import google.registry.flows.domain.BaseDomainCreateFlow.ClaimsPeriodEndedException;
|
||||
import google.registry.flows.domain.BaseDomainCreateFlow.ExpiredClaimException;
|
||||
import google.registry.flows.domain.BaseDomainCreateFlow.InvalidTcnIdChecksumException;
|
||||
import google.registry.flows.domain.BaseDomainCreateFlow.InvalidTrademarkValidatorException;
|
||||
import google.registry.flows.domain.BaseDomainCreateFlow.MalformedTcnIdException;
|
||||
import google.registry.flows.domain.BaseDomainCreateFlow.MaxSigLifeNotSupportedException;
|
||||
import google.registry.flows.domain.BaseDomainCreateFlow.MissingClaimsNoticeException;
|
||||
import google.registry.flows.domain.BaseDomainCreateFlow.UnexpectedClaimsNoticeException;
|
||||
import google.registry.flows.domain.BaseDomainCreateFlow.UnsupportedMarkTypeException;
|
||||
import google.registry.flows.domain.DomainApplicationCreateFlow.LandrushApplicationDisallowedDuringSunriseException;
|
||||
import google.registry.flows.domain.DomainApplicationCreateFlow.NoticeCannotBeUsedWithSignedMarkException;
|
||||
import google.registry.flows.domain.DomainApplicationCreateFlow.SunriseApplicationDisallowedDuringLandrushException;
|
||||
import google.registry.flows.domain.DomainApplicationCreateFlow.UncontestedSunriseApplicationBlockedInLandrushException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.AcceptedTooLongAgoException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.BadCommandForRegistryPhaseException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.BadDomainNameCharacterException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.BadDomainNamePartsCountException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.BadPeriodUnitException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.Base64RequiredForEncodedSignedMarksException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.ClaimsPeriodEndedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.CurrencyUnitMismatchException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.CurrencyValueScaleException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.DashesInThirdAndFourthException;
|
||||
|
@ -72,13 +62,20 @@ import google.registry.flows.domain.DomainFlowUtils.DomainLabelTooLongException;
|
|||
import google.registry.flows.domain.DomainFlowUtils.DomainReservedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.DuplicateContactForRoleException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.EmptyDomainNamePartException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.ExpiredClaimException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.FeesMismatchException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.FeesRequiredForPremiumNameException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.InvalidIdnDomainLabelException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.InvalidLrpTokenException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.InvalidPunycodeException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.InvalidTcnIdChecksumException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.InvalidTrademarkValidatorException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.LaunchPhaseMismatchException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.LeadingDashException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.LinkedResourcesDoNotExistException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.MalformedTcnIdException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.MaxSigLifeNotSupportedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.MissingClaimsNoticeException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.MissingContactTypeException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.NameserversNotAllowedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedException;
|
||||
|
@ -101,7 +98,10 @@ import google.registry.flows.domain.DomainFlowUtils.TooManyDsRecordsException;
|
|||
import google.registry.flows.domain.DomainFlowUtils.TooManyNameserversException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.TooManySignedMarksException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.TrailingDashException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.UnexpectedClaimsNoticeException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.UnsupportedFeeAttributeException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.UnsupportedMarkTypeException;
|
||||
import google.registry.flows.exceptions.ResourceAlreadyExistsException;
|
||||
import google.registry.model.domain.DomainApplication;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
import google.registry.model.domain.LrpTokenEntity;
|
||||
|
@ -939,7 +939,7 @@ public class DomainApplicationCreateFlowTest
|
|||
setEppInput("domain_create_landrush_lrp.xml");
|
||||
persistContactsAndHosts();
|
||||
clock.advanceOneMilli();
|
||||
thrown.expect(BadAuthInfoForResourceException.class);
|
||||
thrown.expect(InvalidLrpTokenException.class);
|
||||
runFlow();
|
||||
}
|
||||
|
||||
|
@ -960,7 +960,7 @@ public class DomainApplicationCreateFlowTest
|
|||
setEppInput("domain_create_landrush_lrp.xml");
|
||||
persistContactsAndHosts();
|
||||
clock.advanceOneMilli();
|
||||
thrown.expect(BadAuthInfoForResourceException.class);
|
||||
thrown.expect(InvalidLrpTokenException.class);
|
||||
runFlow();
|
||||
}
|
||||
|
||||
|
@ -980,7 +980,7 @@ public class DomainApplicationCreateFlowTest
|
|||
setEppInput("domain_create_landrush_lrp.xml");
|
||||
persistContactsAndHosts();
|
||||
clock.advanceOneMilli();
|
||||
thrown.expect(BadAuthInfoForResourceException.class);
|
||||
thrown.expect(InvalidLrpTokenException.class);
|
||||
runFlow();
|
||||
}
|
||||
|
||||
|
|
|
@ -55,25 +55,15 @@ import com.google.common.collect.ImmutableSortedMap;
|
|||
import google.registry.flows.EppException.UnimplementedExtensionException;
|
||||
import google.registry.flows.EppRequestSource;
|
||||
import google.registry.flows.LoggedInFlow.UndeclaredServiceExtensionException;
|
||||
import google.registry.flows.ResourceCreateFlow.ResourceAlreadyExistsException;
|
||||
import google.registry.flows.ResourceCreateOrMutateFlow.OnlyToolCanPassMetadataException;
|
||||
import google.registry.flows.ResourceFlowTestCase;
|
||||
import google.registry.flows.domain.BaseDomainCreateFlow.AcceptedTooLongAgoException;
|
||||
import google.registry.flows.domain.BaseDomainCreateFlow.ClaimsPeriodEndedException;
|
||||
import google.registry.flows.domain.BaseDomainCreateFlow.ExpiredClaimException;
|
||||
import google.registry.flows.domain.BaseDomainCreateFlow.InvalidTcnIdChecksumException;
|
||||
import google.registry.flows.domain.BaseDomainCreateFlow.InvalidTrademarkValidatorException;
|
||||
import google.registry.flows.domain.BaseDomainCreateFlow.MalformedTcnIdException;
|
||||
import google.registry.flows.domain.BaseDomainCreateFlow.MaxSigLifeNotSupportedException;
|
||||
import google.registry.flows.domain.BaseDomainCreateFlow.MissingClaimsNoticeException;
|
||||
import google.registry.flows.domain.BaseDomainCreateFlow.UnexpectedClaimsNoticeException;
|
||||
import google.registry.flows.domain.BaseDomainCreateFlow.UnsupportedMarkTypeException;
|
||||
import google.registry.flows.domain.DomainCreateFlow.DomainHasOpenApplicationsException;
|
||||
import google.registry.flows.domain.DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException;
|
||||
import google.registry.flows.domain.DomainCreateFlow.SignedMarksNotAcceptedInCurrentPhaseException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.AcceptedTooLongAgoException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.BadDomainNameCharacterException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.BadDomainNamePartsCountException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.BadPeriodUnitException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.ClaimsPeriodEndedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.CurrencyUnitMismatchException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.CurrencyValueScaleException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.DashesInThirdAndFourthException;
|
||||
|
@ -81,14 +71,20 @@ import google.registry.flows.domain.DomainFlowUtils.DomainLabelTooLongException;
|
|||
import google.registry.flows.domain.DomainFlowUtils.DomainReservedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.DuplicateContactForRoleException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.EmptyDomainNamePartException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.ExpiredClaimException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.FeesMismatchException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.FeesRequiredForPremiumNameException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.InvalidIdnDomainLabelException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.InvalidPunycodeException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.InvalidTcnIdChecksumException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.InvalidTrademarkValidatorException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.LeadingDashException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.LinkedResourceInPendingDeleteProhibitsOperationException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.LinkedResourcesDoNotExistException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.MalformedTcnIdException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.MaxSigLifeNotSupportedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.MissingAdminContactException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.MissingClaimsNoticeException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.MissingContactTypeException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.MissingRegistrantException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.MissingTechnicalContactException;
|
||||
|
@ -101,7 +97,11 @@ import google.registry.flows.domain.DomainFlowUtils.TldDoesNotExistException;
|
|||
import google.registry.flows.domain.DomainFlowUtils.TooManyDsRecordsException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.TooManyNameserversException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.TrailingDashException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.UnexpectedClaimsNoticeException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.UnsupportedFeeAttributeException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.UnsupportedMarkTypeException;
|
||||
import google.registry.flows.exceptions.OnlyToolCanPassMetadataException;
|
||||
import google.registry.flows.exceptions.ResourceAlreadyExistsException;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Flag;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
|
@ -262,7 +262,8 @@ public class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow,
|
|||
.hasSmdId("0000001761376042759136-65535").and()
|
||||
.hasLaunchNotice(null);
|
||||
TaskMatcher task = new TaskMatcher().payload(
|
||||
"16-TLD,test-validate.tld,0000001761376042759136-65535,1,2014-09-09T09:09:09.001Z");
|
||||
reloadResourceByForeignKey().getRepoId()
|
||||
+ ",test-validate.tld,0000001761376042759136-65535,1,2014-09-09T09:09:09.001Z");
|
||||
assertTasksEnqueued(QUEUE_SUNRISE, task);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
</create>
|
||||
<extension>
|
||||
<allocate:create xmlns:allocate="urn:google:params:xml:ns:allocate-1.0">
|
||||
<allocate:applicationRoid>A-EXAMPLE</allocate:applicationRoid>
|
||||
<allocate:applicationRoid>B-EXAMPLE</allocate:applicationRoid>
|
||||
<allocate:applicationTime>2014-01-01T00:00:00Z</allocate:applicationTime>
|
||||
</allocate:create>
|
||||
</extension>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<launch:creData
|
||||
xmlns:launch="urn:ietf:params:xml:ns:launch-1.0">
|
||||
<launch:phase>sunrise</launch:phase>
|
||||
<launch:applicationID>A-EXAMPLE</launch:applicationID>
|
||||
<launch:applicationID>B-EXAMPLE</launch:applicationID>
|
||||
</launch:creData>
|
||||
</extension>
|
||||
<trID>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<result code="1301">
|
||||
<msg>Command completed successfully; ack to dequeue</msg>
|
||||
</result>
|
||||
<msgQ count="1" id="1-B-EXAMPLE-17-23">
|
||||
<msgQ count="1" id="1-C-EXAMPLE-17-23">
|
||||
<qDate>2001-01-01T00:00:00Z</qDate>
|
||||
<msg>Transfer requested.</msg>
|
||||
</msgQ>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<result code="1301">
|
||||
<msg>Command completed successfully; ack to dequeue</msg>
|
||||
</result>
|
||||
<msgQ count="1" id="1-B-EXAMPLE-17-22">
|
||||
<msgQ count="1" id="1-C-EXAMPLE-17-22">
|
||||
<qDate>2001-01-06T00:00:00Z</qDate>
|
||||
<msg>Transfer approved.</msg>
|
||||
</msgQ>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<result code="1301">
|
||||
<msg>Command completed successfully; ack to dequeue</msg>
|
||||
</result>
|
||||
<msgQ count="1" id="1-B-EXAMPLE-17-21">
|
||||
<msgQ count="1" id="1-C-EXAMPLE-17-21">
|
||||
<qDate>2001-01-06T00:00:00Z</qDate>
|
||||
<msg>Transfer approved.</msg>
|
||||
</msgQ>
|
||||
|
|
|
@ -29,6 +29,7 @@ import static google.registry.testing.DatastoreHelper.persistResource;
|
|||
import com.google.common.base.Function;
|
||||
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.model.ofy.Ofy;
|
||||
import google.registry.model.registry.Registry;
|
||||
|
@ -108,11 +109,16 @@ public class ReservedListTest {
|
|||
.build());
|
||||
assertThat(getReservation("lol", "tld")).isEqualTo(RESERVED_FOR_ANCHOR_TENANT);
|
||||
assertThat(getReservation("lol2", "tld")).isEqualTo(RESERVED_FOR_ANCHOR_TENANT);
|
||||
assertThat(matchesAnchorTenantReservation("lol", "tld", "foobar1")).isTrue();
|
||||
assertThat(matchesAnchorTenantReservation("lol", "tld", "foobar")).isFalse();
|
||||
assertThat(matchesAnchorTenantReservation("lol2", "tld", "abcdefg")).isTrue();
|
||||
assertThat(matchesAnchorTenantReservation("lol2", "tld", "abcdefg ")).isFalse();
|
||||
assertThat(matchesAnchorTenantReservation("random", "tld", "abcdefg ")).isFalse();
|
||||
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol.tld"), "foobar1"))
|
||||
.isTrue();
|
||||
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol.tld"), "foobar"))
|
||||
.isFalse();
|
||||
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol2.tld"), "abcdefg"))
|
||||
.isTrue();
|
||||
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol2.tld"), "abcdefg "))
|
||||
.isFalse();
|
||||
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("random.tld"), "abcdefg"))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -126,11 +132,11 @@ public class ReservedListTest {
|
|||
"lol3,MISTAKEN_PREMIUM",
|
||||
"lol4,ALLOWED_IN_SUNRISE")))
|
||||
.build());
|
||||
assertThat(matchesAnchorTenantReservation("lol", "tld", "")).isFalse();
|
||||
assertThat(matchesAnchorTenantReservation("lol2", "tld", "")).isFalse();
|
||||
assertThat(matchesAnchorTenantReservation("lol3", "tld", "")).isFalse();
|
||||
assertThat(matchesAnchorTenantReservation("lol4", "tld", "")).isFalse();
|
||||
assertThat(matchesAnchorTenantReservation("lol5", "tld", "")).isFalse();
|
||||
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol.tld"), "")).isFalse();
|
||||
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol2.tld"), "")).isFalse();
|
||||
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol3.tld"), "")).isFalse();
|
||||
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol4.tld"), "")).isFalse();
|
||||
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol5.tld"), "")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue