mirror of
https://github.com/google/nomulus.git
synced 2025-05-13 16:07:15 +02:00
Flatten the domain and domain application update flows
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=135282371
This commit is contained in:
parent
ad66f805cf
commit
886d6f8e17
7 changed files with 535 additions and 401 deletions
|
@ -209,7 +209,20 @@ This flows can check the existence of multiple contacts simultaneously.
|
||||||
|
|
||||||
### Description
|
### Description
|
||||||
|
|
||||||
An EPP flow that updates a domain resource.
|
An EPP flow that updates a domain.
|
||||||
|
|
||||||
|
Updates can change contacts, nameservers and delegation signer data of a domain. Updates
|
||||||
|
cannot change the domain's name.
|
||||||
|
|
||||||
|
Some status values (those of the form "serverSomethingProhibited") can only be applied by the
|
||||||
|
superuser. As such, adding or removing these statuses incurs a billing event. There will be only
|
||||||
|
one charge per update, even if several such statuses are updated at once.
|
||||||
|
|
||||||
|
If a domain was created during the sunrise or landrush phases of a TLD, is still within the
|
||||||
|
sunrushAddGracePeriod and has not yet been delegated in DNS, then it will not yet have been
|
||||||
|
billed for. Any update that causes the name to be delegated (such * as adding nameservers or
|
||||||
|
removing a hold status) will cause the domain to convert to a normal create and be billed for
|
||||||
|
accordingly.
|
||||||
|
|
||||||
### Errors
|
### Errors
|
||||||
|
|
||||||
|
@ -227,9 +240,9 @@ An EPP flow that updates a domain resource.
|
||||||
* 2103
|
* 2103
|
||||||
* Specified extension is not implemented.
|
* Specified extension is not implemented.
|
||||||
* 2201
|
* 2201
|
||||||
|
* The specified resource belongs to another client.
|
||||||
* Only a tool can pass a metadata extension.
|
* Only a tool can pass a metadata extension.
|
||||||
* Registrar is not authorized to access this TLD.
|
* Registrar is not authorized to access this TLD.
|
||||||
* The specified resource belongs to another client.
|
|
||||||
* 2303
|
* 2303
|
||||||
* Resource with this id does not exist.
|
* Resource with this id does not exist.
|
||||||
* Resource linked to this domain does not exist.
|
* Resource linked to this domain does not exist.
|
||||||
|
@ -242,9 +255,9 @@ An EPP flow that updates a domain resource.
|
||||||
* Registrant is not whitelisted for this TLD.
|
* Registrant is not whitelisted for this TLD.
|
||||||
* 2306
|
* 2306
|
||||||
* Cannot add and remove the same value.
|
* Cannot add and remove the same value.
|
||||||
* The secDNS:all element must have value 'true' if present.
|
|
||||||
* More than one contact for a given role is not allowed.
|
* More than one contact for a given role is not allowed.
|
||||||
* Missing type attribute for contact.
|
* Missing type attribute for contact.
|
||||||
|
* The secDNS:all element must have value 'true' if present.
|
||||||
* Too many DS records set on a domain.
|
* Too many DS records set on a domain.
|
||||||
* Too many nameservers set on this domain.
|
* Too many nameservers set on this domain.
|
||||||
|
|
||||||
|
@ -639,7 +652,10 @@ This flow also supports the EPP fee extension and can return pricing information
|
||||||
|
|
||||||
### Description
|
### Description
|
||||||
|
|
||||||
An EPP flow that updates a domain resource.
|
An EPP flow that updates a domain application.
|
||||||
|
|
||||||
|
Updates can change contacts, nameservers and delegation signer data of an application. Updates
|
||||||
|
cannot change the domain name that is being applied for.
|
||||||
|
|
||||||
### Errors
|
### Errors
|
||||||
|
|
||||||
|
@ -655,8 +671,8 @@ An EPP flow that updates a domain resource.
|
||||||
* 2103
|
* 2103
|
||||||
* Specified extension is not implemented.
|
* Specified extension is not implemented.
|
||||||
* 2201
|
* 2201
|
||||||
* Registrar is not authorized to access this TLD.
|
|
||||||
* The specified resource belongs to another client.
|
* The specified resource belongs to another client.
|
||||||
|
* Registrar is not authorized to access this TLD.
|
||||||
* 2303
|
* 2303
|
||||||
* Resource with this id does not exist.
|
* Resource with this id does not exist.
|
||||||
* Resource linked to this domain does not exist.
|
* Resource linked to this domain does not exist.
|
||||||
|
@ -668,9 +684,9 @@ An EPP flow that updates a domain resource.
|
||||||
* Application status prohibits this domain update.
|
* Application status prohibits this domain update.
|
||||||
* 2306
|
* 2306
|
||||||
* Cannot add and remove the same value.
|
* Cannot add and remove the same value.
|
||||||
* The secDNS:all element must have value 'true' if present.
|
|
||||||
* More than one contact for a given role is not allowed.
|
* More than one contact for a given role is not allowed.
|
||||||
* Missing type attribute for contact.
|
* Missing type attribute for contact.
|
||||||
|
* The secDNS:all element must have value 'true' if present.
|
||||||
* Too many DS records set on a domain.
|
* Too many DS records set on a domain.
|
||||||
* Too many nameservers set on this domain.
|
* Too many nameservers set on this domain.
|
||||||
|
|
||||||
|
|
|
@ -1,189 +0,0 @@
|
||||||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package google.registry.flows.domain;
|
|
||||||
|
|
||||||
import static com.google.common.collect.Sets.difference;
|
|
||||||
import static com.google.common.collect.Sets.union;
|
|
||||||
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.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.verifyNotInPendingDelete;
|
|
||||||
import static google.registry.model.domain.fee.Fee.FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER;
|
|
||||||
|
|
||||||
import com.google.common.base.Optional;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import google.registry.flows.EppException;
|
|
||||||
import google.registry.flows.EppException.ParameterValuePolicyErrorException;
|
|
||||||
import google.registry.flows.EppException.RequiredParameterMissingException;
|
|
||||||
import google.registry.flows.EppException.UnimplementedOptionException;
|
|
||||||
import google.registry.flows.ResourceUpdateFlow;
|
|
||||||
import google.registry.model.domain.DomainBase;
|
|
||||||
import google.registry.model.domain.DomainBase.Builder;
|
|
||||||
import google.registry.model.domain.DomainCommand.Update;
|
|
||||||
import google.registry.model.domain.fee.FeeTransformCommandExtension;
|
|
||||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
|
||||||
import google.registry.model.domain.secdns.SecDnsUpdateExtension;
|
|
||||||
import google.registry.model.domain.secdns.SecDnsUpdateExtension.Add;
|
|
||||||
import google.registry.model.domain.secdns.SecDnsUpdateExtension.Remove;
|
|
||||||
import java.util.Set;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An EPP flow that updates a domain application or resource.
|
|
||||||
*
|
|
||||||
* @param <R> the resource type being created
|
|
||||||
* @param <B> a builder for the resource
|
|
||||||
*/
|
|
||||||
public abstract class BaseDomainUpdateFlow<R extends DomainBase, B extends Builder<R, B>>
|
|
||||||
extends ResourceUpdateFlow<R, B, Update> {
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
protected FeeTransformCommandExtension feeUpdate;
|
|
||||||
|
|
||||||
protected Optional<RegistryExtraFlowLogic> extraFlowLogic;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void initResourceCreateOrMutateFlow() throws EppException {
|
|
||||||
registerExtensions(FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
|
|
||||||
feeUpdate =
|
|
||||||
eppInput.getFirstExtensionOfClasses(FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
|
|
||||||
command = cloneAndLinkReferences(command, now);
|
|
||||||
initDomainUpdateFlow();
|
|
||||||
extraFlowLogic = RegistryExtraFlowLogicProxy.newInstanceForDomain(existingResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
protected void initDomainUpdateFlow() throws EppException {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final B setUpdateProperties(B builder) throws EppException {
|
|
||||||
// Handle the secDNS extension.
|
|
||||||
SecDnsUpdateExtension secDnsUpdate = eppInput.getSingleExtension(SecDnsUpdateExtension.class);
|
|
||||||
if (secDnsUpdate != null) {
|
|
||||||
// We don't support 'urgent' because we do everything as fast as we can anyways.
|
|
||||||
if (Boolean.TRUE.equals(secDnsUpdate.getUrgent())) { // We allow both false and null.
|
|
||||||
throw new UrgentAttributeNotSupportedException();
|
|
||||||
}
|
|
||||||
// There must be at least one of add/rem/chg, and chg isn't actually supported.
|
|
||||||
if (secDnsUpdate.getAdd() == null && secDnsUpdate.getRemove() == null) {
|
|
||||||
// The only thing you can change is maxSigLife, and we don't support that at all.
|
|
||||||
throw (secDnsUpdate.getChange() == null)
|
|
||||||
? new EmptySecDnsUpdateException()
|
|
||||||
: new MaxSigLifeChangeNotSupportedException();
|
|
||||||
}
|
|
||||||
Set<DelegationSignerData> newDsData = existingResource.getDsData();
|
|
||||||
// RFC 5910 specifies that removes are processed before adds.
|
|
||||||
Remove remove = secDnsUpdate.getRemove();
|
|
||||||
if (remove != null) {
|
|
||||||
if (Boolean.FALSE.equals(remove.getAll())) { // Explicit all=false is meaningless.
|
|
||||||
throw new SecDnsAllUsageException();
|
|
||||||
}
|
|
||||||
newDsData = (remove.getAll() == null)
|
|
||||||
? difference(existingResource.getDsData(), remove.getDsData())
|
|
||||||
: ImmutableSet.<DelegationSignerData>of();
|
|
||||||
}
|
|
||||||
Add add = secDnsUpdate.getAdd();
|
|
||||||
if (add != null) {
|
|
||||||
newDsData = union(newDsData, add.getDsData());
|
|
||||||
}
|
|
||||||
builder.setDsData(ImmutableSet.copyOf(newDsData));
|
|
||||||
}
|
|
||||||
return setDomainUpdateProperties(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subclasses can override this to do set more specific properties.
|
|
||||||
*
|
|
||||||
* @throws EppException if the overriding method encounters an error that should be returned to
|
|
||||||
* the user as an EPP response
|
|
||||||
*/
|
|
||||||
protected B setDomainUpdateProperties(B builder) throws EppException {
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected final void verifyUpdateIsAllowed() throws EppException {
|
|
||||||
checkAllowedAccessToTld(getAllowedTlds(), existingResource.getTld());
|
|
||||||
verifyDomainUpdateIsAllowed();
|
|
||||||
verifyNotInPendingDelete(
|
|
||||||
command.getInnerAdd().getContacts(),
|
|
||||||
command.getInnerChange().getRegistrant(),
|
|
||||||
command.getInnerAdd().getNameservers());
|
|
||||||
validateContactsHaveTypes(command.getInnerAdd().getContacts());
|
|
||||||
validateContactsHaveTypes(command.getInnerRemove().getContacts());
|
|
||||||
validateRegistrantAllowedOnTld(
|
|
||||||
existingResource.getTld(), command.getInnerChange().getRegistrantContactId());
|
|
||||||
validateNameserversAllowedOnTld(
|
|
||||||
existingResource.getTld(), command.getInnerAdd().getNameserverFullyQualifiedHostNames());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Subclasses can override this to do more specific verification. */
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
protected void verifyDomainUpdateIsAllowed() throws EppException {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected final void verifyNewUpdatedStateIsAllowed() throws EppException {
|
|
||||||
validateNoDuplicateContacts(newResource.getContacts());
|
|
||||||
validateRequiredContactsPresent(newResource.getRegistrant(), newResource.getContacts());
|
|
||||||
validateDsData(newResource.getDsData());
|
|
||||||
validateNameserversCountForTld(newResource.getTld(), newResource.getNameservers().size());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Call the subclass method, then commit any extra flow logic. */
|
|
||||||
@Override
|
|
||||||
protected final void modifyRelatedResources() {
|
|
||||||
modifyUpdateRelatedResources();
|
|
||||||
if (extraFlowLogic.isPresent()) {
|
|
||||||
extraFlowLogic.get().commitAdditionalLogicChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Modify any other resources that need to be informed of this update. */
|
|
||||||
protected void modifyUpdateRelatedResources() {}
|
|
||||||
|
|
||||||
/** The secDNS:all element must have value 'true' if present. */
|
|
||||||
static class SecDnsAllUsageException extends ParameterValuePolicyErrorException {
|
|
||||||
public SecDnsAllUsageException() {
|
|
||||||
super("The secDNS:all element must have value 'true' if present");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** At least one of 'add' or 'rem' is required on a secDNS update. */
|
|
||||||
static class EmptySecDnsUpdateException extends RequiredParameterMissingException {
|
|
||||||
public EmptySecDnsUpdateException() {
|
|
||||||
super("At least one of 'add' or 'rem' is required on a secDNS update");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The 'urgent' attribute is not supported. */
|
|
||||||
static class UrgentAttributeNotSupportedException extends UnimplementedOptionException {
|
|
||||||
public UrgentAttributeNotSupportedException() {
|
|
||||||
super("The 'urgent' attribute is not supported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Changing 'maxSigLife' is not supported. */
|
|
||||||
static class MaxSigLifeChangeNotSupportedException extends UnimplementedOptionException {
|
|
||||||
public MaxSigLifeChangeNotSupportedException() {
|
|
||||||
super("Changing 'maxSigLife' is not supported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,77 +16,188 @@ package google.registry.flows.domain;
|
||||||
|
|
||||||
import static com.google.common.base.CaseFormat.LOWER_CAMEL;
|
import static com.google.common.base.CaseFormat.LOWER_CAMEL;
|
||||||
import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
|
import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
|
||||||
import static google.registry.flows.domain.DomainFlowUtils.DISALLOWED_TLD_STATES_FOR_LAUNCH_FLOWS;
|
import static google.registry.flows.ResourceFlowUtils.verifyExistence;
|
||||||
|
import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses;
|
||||||
|
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource;
|
||||||
|
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
|
||||||
|
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||||
|
import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences;
|
||||||
|
import static google.registry.flows.domain.DomainFlowUtils.updateDsData;
|
||||||
|
import static google.registry.flows.domain.DomainFlowUtils.validateContactsHaveTypes;
|
||||||
|
import static google.registry.flows.domain.DomainFlowUtils.validateDsData;
|
||||||
|
import static google.registry.flows.domain.DomainFlowUtils.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.verifyClientUpdateNotProhibited;
|
||||||
|
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete;
|
||||||
|
import static google.registry.flows.domain.DomainFlowUtils.verifyStatusChangesAreClientSettable;
|
||||||
|
import static google.registry.model.EppResourceUtils.loadDomainApplication;
|
||||||
|
import static google.registry.model.domain.fee.Fee.FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER;
|
||||||
|
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
|
||||||
|
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import com.googlecode.objectify.Key;
|
||||||
import google.registry.flows.EppException;
|
import google.registry.flows.EppException;
|
||||||
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||||
|
import google.registry.flows.FlowModule.ApplicationId;
|
||||||
|
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.exceptions.AddRemoveSameValueEppException;
|
||||||
|
import google.registry.model.ImmutableObject;
|
||||||
import google.registry.model.domain.DomainApplication;
|
import google.registry.model.domain.DomainApplication;
|
||||||
import google.registry.model.domain.DomainApplication.Builder;
|
import google.registry.model.domain.DomainApplication.Builder;
|
||||||
|
import google.registry.model.domain.DomainCommand.Update;
|
||||||
import google.registry.model.domain.launch.ApplicationStatus;
|
import google.registry.model.domain.launch.ApplicationStatus;
|
||||||
import google.registry.model.domain.launch.LaunchUpdateExtension;
|
import google.registry.model.domain.launch.LaunchUpdateExtension;
|
||||||
|
import google.registry.model.domain.metadata.MetadataExtension;
|
||||||
import google.registry.model.domain.secdns.SecDnsUpdateExtension;
|
import google.registry.model.domain.secdns.SecDnsUpdateExtension;
|
||||||
import google.registry.model.registry.Registry.TldState;
|
import google.registry.model.eppcommon.AuthInfo;
|
||||||
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
|
import google.registry.model.eppinput.ResourceCommand;
|
||||||
|
import google.registry.model.eppinput.ResourceCommand.AddRemoveSameValueException;
|
||||||
|
import google.registry.model.eppoutput.EppOutput;
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An EPP flow that updates a domain resource.
|
* An EPP flow that updates a domain application.
|
||||||
|
*
|
||||||
|
* <p>Updates can change contacts, nameservers and delegation signer data of an application. Updates
|
||||||
|
* cannot change the domain name that is being applied for.
|
||||||
*
|
*
|
||||||
* @error {@link google.registry.flows.EppException.UnimplementedExtensionException}
|
* @error {@link google.registry.flows.EppException.UnimplementedExtensionException}
|
||||||
* @error {@link google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException}
|
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException}
|
||||||
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
|
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
|
||||||
* @error {@link google.registry.flows.ResourceMutateFlow.ResourceDoesNotExistException}
|
* @error {@link google.registry.flows.exceptions.AddRemoveSameValueEppException}
|
||||||
* @error {@link google.registry.flows.ResourceUpdateFlow.AddRemoveSameValueEppException}
|
* @error {@link google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException}
|
||||||
* @error {@link google.registry.flows.ResourceUpdateFlow.ResourceHasClientUpdateProhibitedException}
|
* @error {@link google.registry.flows.exceptions.ResourceStatusProhibitsOperationException}
|
||||||
* @error {@link google.registry.flows.ResourceUpdateFlow.StatusNotClientSettableException}
|
* @error {@link google.registry.flows.exceptions.StatusNotClientSettableException}
|
||||||
* @error {@link google.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException}
|
|
||||||
* @error {@link BaseDomainUpdateFlow.EmptySecDnsUpdateException}
|
|
||||||
* @error {@link BaseDomainUpdateFlow.MaxSigLifeChangeNotSupportedException}
|
|
||||||
* @error {@link BaseDomainUpdateFlow.SecDnsAllUsageException}
|
|
||||||
* @error {@link BaseDomainUpdateFlow.UrgentAttributeNotSupportedException}
|
|
||||||
* @error {@link DomainFlowUtils.DuplicateContactForRoleException}
|
* @error {@link DomainFlowUtils.DuplicateContactForRoleException}
|
||||||
|
* @error {@link DomainFlowUtils.EmptySecDnsUpdateException}
|
||||||
* @error {@link DomainFlowUtils.LinkedResourcesDoNotExistException}
|
* @error {@link DomainFlowUtils.LinkedResourcesDoNotExistException}
|
||||||
|
* @error {@link DomainFlowUtils.MaxSigLifeChangeNotSupportedException}
|
||||||
* @error {@link DomainFlowUtils.MissingAdminContactException}
|
* @error {@link DomainFlowUtils.MissingAdminContactException}
|
||||||
* @error {@link DomainFlowUtils.MissingContactTypeException}
|
* @error {@link DomainFlowUtils.MissingContactTypeException}
|
||||||
* @error {@link DomainFlowUtils.MissingTechnicalContactException}
|
* @error {@link DomainFlowUtils.MissingTechnicalContactException}
|
||||||
* @error {@link DomainFlowUtils.NameserversNotAllowedException}
|
* @error {@link DomainFlowUtils.NameserversNotAllowedException}
|
||||||
|
* @error {@link DomainFlowUtils.NotAuthorizedForTldException}
|
||||||
* @error {@link DomainFlowUtils.RegistrantNotAllowedException}
|
* @error {@link DomainFlowUtils.RegistrantNotAllowedException}
|
||||||
|
* @error {@link DomainFlowUtils.SecDnsAllUsageException}
|
||||||
* @error {@link DomainFlowUtils.TooManyDsRecordsException}
|
* @error {@link DomainFlowUtils.TooManyDsRecordsException}
|
||||||
* @error {@link DomainFlowUtils.TooManyNameserversException}
|
* @error {@link DomainFlowUtils.TooManyNameserversException}
|
||||||
|
* @error {@link DomainFlowUtils.UrgentAttributeNotSupportedException}
|
||||||
* @error {@link DomainApplicationUpdateFlow.ApplicationStatusProhibitsUpdateException}
|
* @error {@link DomainApplicationUpdateFlow.ApplicationStatusProhibitsUpdateException}
|
||||||
*/
|
*/
|
||||||
public class DomainApplicationUpdateFlow
|
public class DomainApplicationUpdateFlow extends LoggedInFlow implements TransactionalFlow {
|
||||||
extends BaseDomainUpdateFlow<DomainApplication, Builder> {
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note that CLIENT_UPDATE_PROHIBITED is intentionally not in this list. This is because it
|
||||||
|
* requires special checking, since you must be able to clear the status off the object with an
|
||||||
|
* update.
|
||||||
|
*/
|
||||||
|
private static final ImmutableSet<StatusValue> UPDATE_DISALLOWED_STATUSES =
|
||||||
|
Sets.immutableEnumSet(
|
||||||
|
StatusValue.PENDING_DELETE,
|
||||||
|
StatusValue.SERVER_UPDATE_PROHIBITED);
|
||||||
|
|
||||||
|
private static final ImmutableSet<ApplicationStatus> UPDATE_DISALLOWED_APPLICATION_STATUSES =
|
||||||
|
Sets.immutableEnumSet(
|
||||||
|
ApplicationStatus.INVALID,
|
||||||
|
ApplicationStatus.REJECTED,
|
||||||
|
ApplicationStatus.ALLOCATED);
|
||||||
|
|
||||||
|
@Inject ResourceCommand resourceCommand;
|
||||||
|
@Inject Optional<AuthInfo> authInfo;
|
||||||
|
@Inject @ClientId String clientId;
|
||||||
|
@Inject @TargetId String targetId;
|
||||||
|
@Inject @ApplicationId String applicationId;
|
||||||
|
@Inject HistoryEntry.Builder historyBuilder;
|
||||||
@Inject DomainApplicationUpdateFlow() {}
|
@Inject DomainApplicationUpdateFlow() {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initDomainUpdateFlow() throws EppException {
|
protected final void initLoggedInFlow() throws EppException {
|
||||||
registerExtensions(LaunchUpdateExtension.class, SecDnsUpdateExtension.class);
|
registerExtensions(FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
|
||||||
|
registerExtensions(
|
||||||
|
MetadataExtension.class, LaunchUpdateExtension.class, SecDnsUpdateExtension.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected final void verifyDomainUpdateIsAllowed() throws EppException {
|
public final EppOutput run() throws EppException {
|
||||||
switch (existingResource.getApplicationStatus()) {
|
Update command = cloneAndLinkReferences((Update) resourceCommand, now);
|
||||||
case PENDING_ALLOCATION:
|
DomainApplication existingApplication = verifyExistence(
|
||||||
case PENDING_VALIDATION:
|
DomainApplication.class, applicationId, loadDomainApplication(applicationId, now));
|
||||||
case VALIDATED:
|
verifyNoDisallowedStatuses(existingApplication, UPDATE_DISALLOWED_STATUSES);
|
||||||
return;
|
verifyOptionalAuthInfoForResource(authInfo, existingApplication);
|
||||||
default:
|
verifyUpdateAllowed(existingApplication, command);
|
||||||
|
HistoryEntry historyEntry = buildHistory(existingApplication);
|
||||||
|
DomainApplication newApplication = updateApplication(existingApplication, command);
|
||||||
|
validateNewApplication(newApplication);
|
||||||
|
ofy().save().<ImmutableObject>entities(newApplication, historyEntry);
|
||||||
|
return createOutput(SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void verifyUpdateAllowed(
|
||||||
|
DomainApplication existingApplication, Update command) throws EppException {
|
||||||
|
if (!isSuperuser) {
|
||||||
|
verifyResourceOwnership(clientId, existingApplication);
|
||||||
|
verifyClientUpdateNotProhibited(command, existingApplication);
|
||||||
|
verifyStatusChangesAreClientSettable(command);
|
||||||
|
}
|
||||||
|
String tld = existingApplication.getTld();
|
||||||
|
checkAllowedAccessToTld(getAllowedTlds(), tld);
|
||||||
|
if (UPDATE_DISALLOWED_APPLICATION_STATUSES
|
||||||
|
.contains(existingApplication.getApplicationStatus())) {
|
||||||
throw new ApplicationStatusProhibitsUpdateException(
|
throw new ApplicationStatusProhibitsUpdateException(
|
||||||
existingResource.getApplicationStatus());
|
existingApplication.getApplicationStatus());
|
||||||
}
|
}
|
||||||
|
verifyNotInPendingDelete(
|
||||||
|
command.getInnerAdd().getContacts(),
|
||||||
|
command.getInnerChange().getRegistrant(),
|
||||||
|
command.getInnerAdd().getNameservers());
|
||||||
|
validateContactsHaveTypes(command.getInnerAdd().getContacts());
|
||||||
|
validateContactsHaveTypes(command.getInnerRemove().getContacts());
|
||||||
|
validateRegistrantAllowedOnTld(tld, command.getInnerChange().getRegistrantContactId());
|
||||||
|
validateNameserversAllowedOnTld(
|
||||||
|
tld, command.getInnerAdd().getNameserverFullyQualifiedHostNames());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private HistoryEntry buildHistory(DomainApplication existingApplication) {
|
||||||
protected final ImmutableSet<TldState> getDisallowedTldStates() {
|
return historyBuilder
|
||||||
return DISALLOWED_TLD_STATES_FOR_LAUNCH_FLOWS;
|
.setType(HistoryEntry.Type.DOMAIN_APPLICATION_UPDATE)
|
||||||
|
.setModificationTime(now)
|
||||||
|
.setParent(Key.create(existingApplication))
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private DomainApplication updateApplication(
|
||||||
protected final HistoryEntry.Type getHistoryEntryType() {
|
DomainApplication existingApplication, Update command) throws EppException {
|
||||||
return HistoryEntry.Type.DOMAIN_APPLICATION_UPDATE;
|
Builder builder = existingApplication.asBuilder();
|
||||||
|
try {
|
||||||
|
command.applyTo(builder);
|
||||||
|
} catch (AddRemoveSameValueException e) {
|
||||||
|
throw new AddRemoveSameValueEppException();
|
||||||
|
}
|
||||||
|
builder.setLastEppUpdateTime(now).setLastEppUpdateClientId(clientId);
|
||||||
|
// Handle the secDNS extension.
|
||||||
|
SecDnsUpdateExtension secDnsUpdate = eppInput.getSingleExtension(SecDnsUpdateExtension.class);
|
||||||
|
if (secDnsUpdate != null) {
|
||||||
|
builder.setDsData(updateDsData(existingApplication.getDsData(), secDnsUpdate));
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateNewApplication(DomainApplication newApplication) throws EppException {
|
||||||
|
validateNoDuplicateContacts(newApplication.getContacts());
|
||||||
|
validateRequiredContactsPresent(newApplication.getRegistrant(), newApplication.getContacts());
|
||||||
|
validateDsData(newApplication.getDsData());
|
||||||
|
validateNameserversCountForTld(newApplication.getTld(), newApplication.getNameservers().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Application status prohibits this domain update. */
|
/** Application status prohibits this domain update. */
|
||||||
|
|
|
@ -20,6 +20,7 @@ import static com.google.common.base.Predicates.equalTo;
|
||||||
import static com.google.common.collect.Iterables.any;
|
import static com.google.common.collect.Iterables.any;
|
||||||
import static com.google.common.collect.Iterables.concat;
|
import static com.google.common.collect.Iterables.concat;
|
||||||
import static com.google.common.collect.Sets.difference;
|
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.EppXmlTransformer.unmarshal;
|
||||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||||
import static google.registry.model.registry.Registries.findTldForName;
|
import static google.registry.model.registry.Registries.findTldForName;
|
||||||
|
@ -50,6 +51,8 @@ import google.registry.flows.EppException.ParameterValueSyntaxErrorException;
|
||||||
import google.registry.flows.EppException.RequiredParameterMissingException;
|
import google.registry.flows.EppException.RequiredParameterMissingException;
|
||||||
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||||
import google.registry.flows.EppException.UnimplementedOptionException;
|
import google.registry.flows.EppException.UnimplementedOptionException;
|
||||||
|
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException;
|
||||||
|
import google.registry.flows.exceptions.StatusNotClientSettableException;
|
||||||
import google.registry.model.EppResource;
|
import google.registry.model.EppResource;
|
||||||
import google.registry.model.billing.BillingEvent;
|
import google.registry.model.billing.BillingEvent;
|
||||||
import google.registry.model.billing.BillingEvent.Flag;
|
import google.registry.model.billing.BillingEvent.Flag;
|
||||||
|
@ -61,6 +64,7 @@ import google.registry.model.domain.DomainApplication;
|
||||||
import google.registry.model.domain.DomainBase;
|
import google.registry.model.domain.DomainBase;
|
||||||
import google.registry.model.domain.DomainCommand.CreateOrUpdate;
|
import google.registry.model.domain.DomainCommand.CreateOrUpdate;
|
||||||
import google.registry.model.domain.DomainCommand.InvalidReferencesException;
|
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.DomainResource;
|
||||||
import google.registry.model.domain.Period;
|
import google.registry.model.domain.Period;
|
||||||
import google.registry.model.domain.fee.Credit;
|
import google.registry.model.domain.fee.Credit;
|
||||||
|
@ -72,9 +76,11 @@ import google.registry.model.domain.launch.LaunchExtension;
|
||||||
import google.registry.model.domain.launch.LaunchPhase;
|
import google.registry.model.domain.launch.LaunchPhase;
|
||||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||||
import google.registry.model.domain.secdns.SecDnsInfoExtension;
|
import google.registry.model.domain.secdns.SecDnsInfoExtension;
|
||||||
|
import google.registry.model.domain.secdns.SecDnsUpdateExtension;
|
||||||
|
import google.registry.model.domain.secdns.SecDnsUpdateExtension.Add;
|
||||||
|
import google.registry.model.domain.secdns.SecDnsUpdateExtension.Remove;
|
||||||
import google.registry.model.eppcommon.StatusValue;
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
import google.registry.model.eppinput.EppInput;
|
import google.registry.model.eppinput.EppInput;
|
||||||
import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand;
|
|
||||||
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
|
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
|
||||||
import google.registry.model.host.HostResource;
|
import google.registry.model.host.HostResource;
|
||||||
import google.registry.model.mark.Mark;
|
import google.registry.model.mark.Mark;
|
||||||
|
@ -376,17 +382,6 @@ public class DomainFlowUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies that a launch extension's application id refers to an application with the same
|
|
||||||
* domain name as the one specified in the launch command.
|
|
||||||
*/
|
|
||||||
static void verifyLaunchApplicationIdMatchesDomain(
|
|
||||||
SingleResourceCommand command, DomainBase existingResource) throws EppException {
|
|
||||||
if (!Objects.equals(command.getTargetId(), existingResource.getFullyQualifiedDomainName())) {
|
|
||||||
throw new ApplicationDomainNameMismatchException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Verifies that an application's domain name matches the target id (from a command). */
|
/** Verifies that an application's domain name matches the target id (from a command). */
|
||||||
static void verifyApplicationDomainMatchesTargetId(
|
static void verifyApplicationDomainMatchesTargetId(
|
||||||
DomainApplication application, String targetId) throws EppException {
|
DomainApplication application, String targetId) throws EppException {
|
||||||
|
@ -488,7 +483,7 @@ public class DomainFlowUtils {
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SignedMark verifySignedMarks(
|
static SignedMark verifySignedMarks(
|
||||||
ImmutableList<AbstractSignedMark> signedMarks, String domainLabel, DateTime now)
|
ImmutableList<AbstractSignedMark> signedMarks, String domainLabel, DateTime now)
|
||||||
throws EppException {
|
throws EppException {
|
||||||
if (signedMarks.size() > 1) {
|
if (signedMarks.size() > 1) {
|
||||||
|
@ -787,6 +782,59 @@ public class DomainFlowUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Update {@link DelegationSignerData} based on an update extension command. */
|
||||||
|
static ImmutableSet<DelegationSignerData> updateDsData(
|
||||||
|
ImmutableSet<DelegationSignerData> oldDsData, SecDnsUpdateExtension secDnsUpdate)
|
||||||
|
throws EppException {
|
||||||
|
// We don't support 'urgent' because we do everything as fast as we can anyways.
|
||||||
|
if (Boolean.TRUE.equals(secDnsUpdate.getUrgent())) { // We allow both false and null.
|
||||||
|
throw new UrgentAttributeNotSupportedException();
|
||||||
|
}
|
||||||
|
// There must be at least one of add/rem/chg, and chg isn't actually supported.
|
||||||
|
if (secDnsUpdate.getChange() != null) {
|
||||||
|
// The only thing you can change is maxSigLife, and we don't support that at all.
|
||||||
|
throw new MaxSigLifeChangeNotSupportedException();
|
||||||
|
}
|
||||||
|
Add add = secDnsUpdate.getAdd();
|
||||||
|
Remove remove = secDnsUpdate.getRemove();
|
||||||
|
if (add == null && remove == null) {
|
||||||
|
throw new EmptySecDnsUpdateException();
|
||||||
|
}
|
||||||
|
if (remove != null && Boolean.FALSE.equals(remove.getAll())) {
|
||||||
|
throw new SecDnsAllUsageException(); // Explicit all=false is meaningless.
|
||||||
|
}
|
||||||
|
Set<DelegationSignerData> toAdd = (add == null)
|
||||||
|
? ImmutableSet.<DelegationSignerData>of()
|
||||||
|
: add.getDsData();
|
||||||
|
Set<DelegationSignerData> toRemove = (remove == null)
|
||||||
|
? ImmutableSet.<DelegationSignerData>of()
|
||||||
|
: (remove.getAll() == null) ? remove.getDsData() : oldDsData;
|
||||||
|
// RFC 5910 specifies that removes are processed before adds.
|
||||||
|
return ImmutableSet.copyOf(union(difference(oldDsData, toRemove), toAdd));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check that all of the status values added or removed in an update are client-settable. */
|
||||||
|
static void verifyStatusChangesAreClientSettable(Update command)
|
||||||
|
throws StatusNotClientSettableException {
|
||||||
|
for (StatusValue statusValue : union(
|
||||||
|
command.getInnerAdd().getStatusValues(),
|
||||||
|
command.getInnerRemove().getStatusValues())) {
|
||||||
|
if (!statusValue.isClientSettable()) {
|
||||||
|
throw new StatusNotClientSettableException(statusValue.getXmlName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** If a domain or application has "clientUpdateProhibited" set, updates must clear it or fail. */
|
||||||
|
public static void verifyClientUpdateNotProhibited(Update command, DomainBase existingResource)
|
||||||
|
throws ResourceHasClientUpdateProhibitedException {
|
||||||
|
if (existingResource.getStatusValues().contains(StatusValue.CLIENT_UPDATE_PROHIBITED)
|
||||||
|
&& !command.getInnerRemove().getStatusValues()
|
||||||
|
.contains(StatusValue.CLIENT_UPDATE_PROHIBITED)) {
|
||||||
|
throw new ResourceHasClientUpdateProhibitedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Encoded signed marks must use base64 encoding. */
|
/** Encoded signed marks must use base64 encoding. */
|
||||||
static class Base64RequiredForEncodedSignedMarksException
|
static class Base64RequiredForEncodedSignedMarksException
|
||||||
extends ParameterValuePolicyErrorException {
|
extends ParameterValuePolicyErrorException {
|
||||||
|
@ -1156,4 +1204,31 @@ public class DomainFlowUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The secDNS:all element must have value 'true' if present. */
|
||||||
|
static class SecDnsAllUsageException extends ParameterValuePolicyErrorException {
|
||||||
|
public SecDnsAllUsageException() {
|
||||||
|
super("The secDNS:all element must have value 'true' if present");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** At least one of 'add' or 'rem' is required on a secDNS update. */
|
||||||
|
static class EmptySecDnsUpdateException extends RequiredParameterMissingException {
|
||||||
|
public EmptySecDnsUpdateException() {
|
||||||
|
super("At least one of 'add' or 'rem' is required on a secDNS update");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The 'urgent' attribute is not supported. */
|
||||||
|
static class UrgentAttributeNotSupportedException extends UnimplementedOptionException {
|
||||||
|
public UrgentAttributeNotSupportedException() {
|
||||||
|
super("The 'urgent' attribute is not supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Changing 'maxSigLife' is not supported. */
|
||||||
|
static class MaxSigLifeChangeNotSupportedException extends UnimplementedOptionException {
|
||||||
|
public MaxSigLifeChangeNotSupportedException() {
|
||||||
|
super("Changing 'maxSigLife' is not supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,188 +15,307 @@
|
||||||
package google.registry.flows.domain;
|
package google.registry.flows.domain;
|
||||||
|
|
||||||
import static com.google.common.collect.Sets.symmetricDifference;
|
import static com.google.common.collect.Sets.symmetricDifference;
|
||||||
|
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
|
||||||
|
import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses;
|
||||||
|
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource;
|
||||||
|
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
|
||||||
|
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||||
|
import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences;
|
||||||
|
import static google.registry.flows.domain.DomainFlowUtils.updateDsData;
|
||||||
|
import static google.registry.flows.domain.DomainFlowUtils.validateContactsHaveTypes;
|
||||||
|
import static google.registry.flows.domain.DomainFlowUtils.validateDsData;
|
||||||
import static google.registry.flows.domain.DomainFlowUtils.validateFeeChallenge;
|
import static google.registry.flows.domain.DomainFlowUtils.validateFeeChallenge;
|
||||||
|
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.verifyClientUpdateNotProhibited;
|
||||||
|
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete;
|
||||||
|
import static google.registry.flows.domain.DomainFlowUtils.verifyStatusChangesAreClientSettable;
|
||||||
|
import static google.registry.model.domain.fee.Fee.FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER;
|
||||||
|
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
|
||||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||||
import static google.registry.util.DateTimeUtils.earliestOf;
|
import static google.registry.util.DateTimeUtils.earliestOf;
|
||||||
|
|
||||||
import com.google.common.base.Optional;
|
import com.google.common.base.Optional;
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.googlecode.objectify.Key;
|
||||||
import google.registry.dns.DnsQueue;
|
import google.registry.dns.DnsQueue;
|
||||||
import google.registry.flows.EppException;
|
import google.registry.flows.EppException;
|
||||||
|
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.DomainFlowUtils.FeesRequiredForNonFreeUpdateException;
|
import google.registry.flows.domain.DomainFlowUtils.FeesRequiredForNonFreeUpdateException;
|
||||||
import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations;
|
import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations;
|
||||||
|
import google.registry.flows.exceptions.AddRemoveSameValueEppException;
|
||||||
|
import google.registry.model.ImmutableObject;
|
||||||
import google.registry.model.billing.BillingEvent;
|
import google.registry.model.billing.BillingEvent;
|
||||||
import google.registry.model.billing.BillingEvent.Reason;
|
import google.registry.model.billing.BillingEvent.Reason;
|
||||||
|
import google.registry.model.domain.DomainCommand.Update;
|
||||||
import google.registry.model.domain.DomainResource;
|
import google.registry.model.domain.DomainResource;
|
||||||
import google.registry.model.domain.DomainResource.Builder;
|
|
||||||
import google.registry.model.domain.GracePeriod;
|
import google.registry.model.domain.GracePeriod;
|
||||||
|
import google.registry.model.domain.fee.FeeTransformCommandExtension;
|
||||||
import google.registry.model.domain.flags.FlagsUpdateCommandExtension;
|
import google.registry.model.domain.flags.FlagsUpdateCommandExtension;
|
||||||
|
import google.registry.model.domain.metadata.MetadataExtension;
|
||||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||||
import google.registry.model.domain.secdns.SecDnsUpdateExtension;
|
import google.registry.model.domain.secdns.SecDnsUpdateExtension;
|
||||||
|
import google.registry.model.eppcommon.AuthInfo;
|
||||||
import google.registry.model.eppcommon.StatusValue;
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
|
import google.registry.model.eppinput.ResourceCommand;
|
||||||
|
import google.registry.model.eppinput.ResourceCommand.AddRemoveSameValueException;
|
||||||
|
import google.registry.model.eppoutput.EppOutput;
|
||||||
import google.registry.model.registry.Registry;
|
import google.registry.model.registry.Registry;
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
import java.util.Set;
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import org.joda.money.Money;
|
import org.joda.money.Money;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An EPP flow that updates a domain resource.
|
* An EPP flow that updates a domain.
|
||||||
|
*
|
||||||
|
* <p>Updates can change contacts, nameservers and delegation signer data of a domain. Updates
|
||||||
|
* cannot change the domain's name.
|
||||||
|
*
|
||||||
|
* <p>Some status values (those of the form "serverSomethingProhibited") can only be applied by the
|
||||||
|
* superuser. As such, adding or removing these statuses incurs a billing event. There will be only
|
||||||
|
* one charge per update, even if several such statuses are updated at once.
|
||||||
|
*
|
||||||
|
* <p>If a domain was created during the sunrise or landrush phases of a TLD, is still within the
|
||||||
|
* sunrushAddGracePeriod and has not yet been delegated in DNS, then it will not yet have been
|
||||||
|
* billed for. Any update that causes the name to be delegated (such * as adding nameservers or
|
||||||
|
* removing a hold status) will cause the domain to convert to a normal create and be billed for
|
||||||
|
* accordingly.
|
||||||
*
|
*
|
||||||
* @error {@link google.registry.flows.EppException.UnimplementedExtensionException}
|
* @error {@link google.registry.flows.EppException.UnimplementedExtensionException}
|
||||||
* @error {@link google.registry.flows.ResourceCreateOrMutateFlow.OnlyToolCanPassMetadataException}
|
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException}
|
||||||
* @error {@link google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException}
|
|
||||||
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
|
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
|
||||||
* @error {@link google.registry.flows.ResourceMutateFlow.ResourceDoesNotExistException}
|
* @error {@link google.registry.flows.exceptions.AddRemoveSameValueEppException}
|
||||||
* @error {@link google.registry.flows.ResourceUpdateFlow.AddRemoveSameValueEppException}
|
* @error {@link google.registry.flows.exceptions.OnlyToolCanPassMetadataException}
|
||||||
* @error {@link google.registry.flows.ResourceUpdateFlow.ResourceHasClientUpdateProhibitedException}
|
* @error {@link google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException}
|
||||||
* @error {@link google.registry.flows.ResourceUpdateFlow.StatusNotClientSettableException}
|
* @error {@link google.registry.flows.exceptions.ResourceStatusProhibitsOperationException}
|
||||||
* @error {@link google.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException}
|
* @error {@link google.registry.flows.exceptions.StatusNotClientSettableException}
|
||||||
* @error {@link BaseDomainUpdateFlow.EmptySecDnsUpdateException}
|
|
||||||
* @error {@link BaseDomainUpdateFlow.MaxSigLifeChangeNotSupportedException}
|
|
||||||
* @error {@link BaseDomainUpdateFlow.SecDnsAllUsageException}
|
|
||||||
* @error {@link BaseDomainUpdateFlow.UrgentAttributeNotSupportedException}
|
|
||||||
* @error {@link DomainFlowUtils.DuplicateContactForRoleException}
|
* @error {@link DomainFlowUtils.DuplicateContactForRoleException}
|
||||||
|
* @error {@link DomainFlowUtils.EmptySecDnsUpdateException}
|
||||||
* @error {@link DomainFlowUtils.FeesMismatchException}
|
* @error {@link DomainFlowUtils.FeesMismatchException}
|
||||||
* @error {@link DomainFlowUtils.FeesRequiredForNonFreeUpdateException}
|
* @error {@link DomainFlowUtils.FeesRequiredForNonFreeUpdateException}
|
||||||
* @error {@link DomainFlowUtils.LinkedResourcesDoNotExistException}
|
* @error {@link DomainFlowUtils.LinkedResourcesDoNotExistException}
|
||||||
* @error {@link DomainFlowUtils.LinkedResourceInPendingDeleteProhibitsOperationException}
|
* @error {@link DomainFlowUtils.LinkedResourceInPendingDeleteProhibitsOperationException}
|
||||||
|
* @error {@link DomainFlowUtils.MaxSigLifeChangeNotSupportedException}
|
||||||
* @error {@link DomainFlowUtils.MissingAdminContactException}
|
* @error {@link DomainFlowUtils.MissingAdminContactException}
|
||||||
* @error {@link DomainFlowUtils.MissingContactTypeException}
|
* @error {@link DomainFlowUtils.MissingContactTypeException}
|
||||||
* @error {@link DomainFlowUtils.MissingTechnicalContactException}
|
* @error {@link DomainFlowUtils.MissingTechnicalContactException}
|
||||||
* @error {@link DomainFlowUtils.NameserversNotAllowedException}
|
* @error {@link DomainFlowUtils.NameserversNotAllowedException}
|
||||||
* @error {@link DomainFlowUtils.NameserversNotSpecifiedException}
|
* @error {@link DomainFlowUtils.NameserversNotSpecifiedException}
|
||||||
|
* @error {@link DomainFlowUtils.NotAuthorizedForTldException}
|
||||||
* @error {@link DomainFlowUtils.RegistrantNotAllowedException}
|
* @error {@link DomainFlowUtils.RegistrantNotAllowedException}
|
||||||
|
* @error {@link DomainFlowUtils.SecDnsAllUsageException}
|
||||||
* @error {@link DomainFlowUtils.TooManyDsRecordsException}
|
* @error {@link DomainFlowUtils.TooManyDsRecordsException}
|
||||||
* @error {@link DomainFlowUtils.TooManyNameserversException}
|
* @error {@link DomainFlowUtils.TooManyNameserversException}
|
||||||
|
* @error {@link DomainFlowUtils.UrgentAttributeNotSupportedException}
|
||||||
*/
|
*/
|
||||||
public class DomainUpdateFlow extends BaseDomainUpdateFlow<DomainResource, Builder> {
|
public final class DomainUpdateFlow extends LoggedInFlow implements TransactionalFlow {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note that CLIENT_UPDATE_PROHIBITED is intentionally not in this list. This is because it
|
||||||
|
* requires special checking, since you must be able to clear the status off the object with an
|
||||||
|
* update.
|
||||||
|
*/
|
||||||
|
private static final ImmutableSet<StatusValue> UPDATE_DISALLOWED_STATUSES = ImmutableSet.of(
|
||||||
|
StatusValue.PENDING_DELETE,
|
||||||
|
StatusValue.SERVER_UPDATE_PROHIBITED);
|
||||||
|
|
||||||
|
@Inject ResourceCommand resourceCommand;
|
||||||
|
@Inject Optional<AuthInfo> authInfo;
|
||||||
|
@Inject @ClientId String clientId;
|
||||||
|
@Inject @TargetId String targetId;
|
||||||
|
@Inject HistoryEntry.Builder historyBuilder;
|
||||||
@Inject DomainUpdateFlow() {}
|
@Inject DomainUpdateFlow() {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initDomainUpdateFlow() {
|
protected final void initLoggedInFlow() throws EppException {
|
||||||
registerExtensions(SecDnsUpdateExtension.class, FlagsUpdateCommandExtension.class);
|
registerExtensions(FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
|
||||||
|
registerExtensions(
|
||||||
|
MetadataExtension.class, SecDnsUpdateExtension.class, FlagsUpdateCommandExtension.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Builder setDomainUpdateProperties(Builder builder) throws EppException {
|
public EppOutput run() throws EppException {
|
||||||
// Check if the domain is currently in the sunrush add grace period.
|
Update command = cloneAndLinkReferences((Update) resourceCommand, now);
|
||||||
Optional<GracePeriod> sunrushAddGracePeriod = Iterables.tryFind(
|
DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now);
|
||||||
existingResource.getGracePeriods(),
|
verifyUpdateAllowed(command, existingDomain);
|
||||||
new Predicate<GracePeriod>() {
|
HistoryEntry historyEntry = buildHistoryEntry(existingDomain);
|
||||||
@Override
|
DomainResource newDomain = performUpdate(command, existingDomain);
|
||||||
public boolean apply(GracePeriod gracePeriod) {
|
// If the new domain is in the sunrush add grace period and is now publishable to DNS because we
|
||||||
return gracePeriod.isSunrushAddGracePeriod();
|
// have added nameserver or removed holds, we have to convert it to a standard add grace period.
|
||||||
}});
|
if (newDomain.shouldPublishToDns()) {
|
||||||
|
for (GracePeriod gracePeriod : newDomain.getGracePeriods()) {
|
||||||
|
if (gracePeriod.isSunrushAddGracePeriod()) {
|
||||||
|
newDomain = convertSunrushAddToAdd(newDomain, gracePeriod, historyEntry);
|
||||||
|
break; // There can only be one sunrush add grace period.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
validateNewState(newDomain);
|
||||||
|
DnsQueue.create().addDomainRefreshTask(targetId);
|
||||||
|
handleExtraFlowLogic(existingDomain, historyEntry);
|
||||||
|
ImmutableList.Builder<ImmutableObject> entitiesToSave = new ImmutableList.Builder<>();
|
||||||
|
entitiesToSave.add(newDomain, historyEntry);
|
||||||
|
Optional<BillingEvent.OneTime> statusUpdateBillingEvent =
|
||||||
|
createBillingEventForStatusUpdates(existingDomain, newDomain, historyEntry);
|
||||||
|
if (statusUpdateBillingEvent.isPresent()) {
|
||||||
|
entitiesToSave.add(statusUpdateBillingEvent.get());
|
||||||
|
}
|
||||||
|
ofy().save().entities(entitiesToSave.build());
|
||||||
|
return createOutput(SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
// If this domain is currently in the sunrush add grace period, and we're updating it in a way
|
/** Fail if the object doesn't exist or was deleted. */
|
||||||
// that will cause it to now get delegated (either by setting nameservers, or by removing a
|
private void verifyUpdateAllowed(Update command, DomainResource existingDomain)
|
||||||
// clientHold or serverHold), then that will remove the sunrush add grace period and convert
|
throws EppException {
|
||||||
// that to a standard add grace period.
|
verifyNoDisallowedStatuses(existingDomain, UPDATE_DISALLOWED_STATUSES);
|
||||||
DomainResource updatedDomain = builder.build();
|
verifyOptionalAuthInfoForResource(authInfo, existingDomain);
|
||||||
builder = updatedDomain.asBuilder();
|
if (!isSuperuser) {
|
||||||
if (sunrushAddGracePeriod.isPresent() && updatedDomain.shouldPublishToDns()) {
|
verifyResourceOwnership(clientId, existingDomain);
|
||||||
// Remove the sunrush grace period and write a billing event cancellation for it.
|
verifyClientUpdateNotProhibited(command, existingDomain);
|
||||||
builder.removeGracePeriod(sunrushAddGracePeriod.get());
|
verifyStatusChangesAreClientSettable(command);
|
||||||
BillingEvent.Cancellation billingEventCancellation = BillingEvent.Cancellation
|
}
|
||||||
.forGracePeriod(sunrushAddGracePeriod.get(), historyEntry, targetId);
|
String tld = existingDomain.getTld();
|
||||||
|
checkAllowedAccessToTld(getAllowedTlds(), tld);
|
||||||
|
EppCommandOperations commandOperations = TldSpecificLogicProxy.getUpdatePrice(
|
||||||
|
Registry.get(tld), targetId, clientId, now, eppInput);
|
||||||
|
|
||||||
|
FeeTransformCommandExtension feeUpdate =
|
||||||
|
eppInput.getFirstExtensionOfClasses(FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
|
||||||
|
// If the fee extension is present, validate it (even if the cost is zero, to check for price
|
||||||
|
// mismatches). Don't rely on the the validateFeeChallenge check for feeUpdate nullness, because
|
||||||
|
// it throws an error if the name is premium, and we don't want to do that here.
|
||||||
|
Money totalCost = commandOperations.getTotalCost();
|
||||||
|
if (feeUpdate != null) {
|
||||||
|
validateFeeChallenge(targetId, existingDomain.getTld(), now, feeUpdate, totalCost);
|
||||||
|
} else if (!totalCost.isZero()) {
|
||||||
|
// If it's not present but the cost is not zero, throw an exception.
|
||||||
|
throw new FeesRequiredForNonFreeUpdateException();
|
||||||
|
}
|
||||||
|
verifyNotInPendingDelete(
|
||||||
|
command.getInnerAdd().getContacts(),
|
||||||
|
command.getInnerChange().getRegistrant(),
|
||||||
|
command.getInnerAdd().getNameservers());
|
||||||
|
validateContactsHaveTypes(command.getInnerAdd().getContacts());
|
||||||
|
validateContactsHaveTypes(command.getInnerRemove().getContacts());
|
||||||
|
validateRegistrantAllowedOnTld(tld, command.getInnerChange().getRegistrantContactId());
|
||||||
|
validateNameserversAllowedOnTld(
|
||||||
|
tld, command.getInnerAdd().getNameserverFullyQualifiedHostNames());
|
||||||
|
}
|
||||||
|
|
||||||
|
private HistoryEntry buildHistoryEntry(DomainResource existingDomain) {
|
||||||
|
return historyBuilder
|
||||||
|
.setType(HistoryEntry.Type.DOMAIN_UPDATE)
|
||||||
|
.setModificationTime(now)
|
||||||
|
.setParent(Key.create(existingDomain))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private DomainResource performUpdate(Update command, DomainResource existingDomain)
|
||||||
|
throws EppException {
|
||||||
|
DomainResource.Builder builder = existingDomain.asBuilder()
|
||||||
|
.setLastEppUpdateTime(now)
|
||||||
|
.setLastEppUpdateClientId(clientId);
|
||||||
|
try {
|
||||||
|
command.applyTo(builder);
|
||||||
|
} catch (AddRemoveSameValueException e) {
|
||||||
|
throw new AddRemoveSameValueEppException();
|
||||||
|
}
|
||||||
|
// Handle the secDNS extension.
|
||||||
|
SecDnsUpdateExtension secDnsUpdate = eppInput.getSingleExtension(SecDnsUpdateExtension.class);
|
||||||
|
if (secDnsUpdate != null) {
|
||||||
|
builder.setDsData(updateDsData(existingDomain.getDsData(), secDnsUpdate));
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private DomainResource convertSunrushAddToAdd(
|
||||||
|
DomainResource newDomain, GracePeriod gracePeriod, HistoryEntry historyEntry) {
|
||||||
|
// Cancel the billing event for the sunrush add and replace it with a new billing event.
|
||||||
|
BillingEvent.Cancellation billingEventCancellation =
|
||||||
|
BillingEvent.Cancellation.forGracePeriod(gracePeriod, historyEntry, targetId);
|
||||||
|
BillingEvent.OneTime billingEvent =
|
||||||
|
createBillingEventForSunrushConversion(newDomain, historyEntry, gracePeriod);
|
||||||
|
ofy().save().entities(billingEvent, billingEventCancellation);
|
||||||
|
// Modify the grace periods on the domain.
|
||||||
|
return newDomain.asBuilder()
|
||||||
|
.removeGracePeriod(gracePeriod)
|
||||||
|
.addGracePeriod(GracePeriod.forBillingEvent(GracePeriodStatus.ADD, billingEvent))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private BillingEvent.OneTime createBillingEventForSunrushConversion(
|
||||||
|
DomainResource existingDomain, HistoryEntry historyEntry, GracePeriod sunrushAddGracePeriod) {
|
||||||
// Compute the expiration time of the add grace period. We will not allow it to be after the
|
// Compute the expiration time of the add grace period. We will not allow it to be after the
|
||||||
// sunrush add grace period expiration time (i.e. you can't get extra add grace period by
|
// sunrush add grace period expiration time (i.e. you can't get extra add grace period by
|
||||||
// setting a nameserver).
|
// setting a nameserver).
|
||||||
DateTime addGracePeriodExpirationTime = earliestOf(
|
DateTime addGracePeriodExpirationTime = earliestOf(
|
||||||
now.plus(Registry.get(existingResource.getTld()).getAddGracePeriodLength()),
|
now.plus(Registry.get(existingDomain.getTld()).getAddGracePeriodLength()),
|
||||||
sunrushAddGracePeriod.get().getExpirationTime());
|
sunrushAddGracePeriod.getExpirationTime());
|
||||||
|
|
||||||
// Create a new billing event for the add grace period. Note that we do this even if it would
|
// Create a new billing event for the add grace period. Note that we do this even if it would
|
||||||
// occur at the same time as the sunrush add grace period, as the event time will differ
|
// occur at the same time as the sunrush add grace period, as the event time will differ
|
||||||
// between them.
|
// between them.
|
||||||
BillingEvent.OneTime originalAddEvent =
|
BillingEvent.OneTime originalAddEvent =
|
||||||
ofy().load().key(sunrushAddGracePeriod.get().getOneTimeBillingEvent()).now();
|
ofy().load().key(sunrushAddGracePeriod.getOneTimeBillingEvent()).now();
|
||||||
BillingEvent.OneTime billingEvent = new BillingEvent.OneTime.Builder()
|
return new BillingEvent.OneTime.Builder()
|
||||||
.setReason(Reason.CREATE)
|
.setReason(Reason.CREATE)
|
||||||
.setTargetId(targetId)
|
.setTargetId(targetId)
|
||||||
.setFlags(originalAddEvent.getFlags())
|
.setFlags(originalAddEvent.getFlags())
|
||||||
.setClientId(sunrushAddGracePeriod.get().getClientId())
|
.setClientId(sunrushAddGracePeriod.getClientId())
|
||||||
.setCost(originalAddEvent.getCost())
|
.setCost(originalAddEvent.getCost())
|
||||||
.setPeriodYears(originalAddEvent.getPeriodYears())
|
.setPeriodYears(originalAddEvent.getPeriodYears())
|
||||||
.setEventTime(now)
|
.setEventTime(now)
|
||||||
.setBillingTime(addGracePeriodExpirationTime)
|
.setBillingTime(addGracePeriodExpirationTime)
|
||||||
.setParent(historyEntry)
|
.setParent(historyEntry)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Set the add grace period on the domain.
|
|
||||||
builder.addGracePeriod(GracePeriod.forBillingEvent(GracePeriodStatus.ADD, billingEvent));
|
|
||||||
|
|
||||||
// Save the billing events.
|
|
||||||
ofy().save().entities(billingEvent, billingEventCancellation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle extra flow logic, if any.
|
private void validateNewState(DomainResource newDomain) throws EppException {
|
||||||
if (extraFlowLogic.isPresent()) {
|
validateNoDuplicateContacts(newDomain.getContacts());
|
||||||
extraFlowLogic.get().performAdditionalDomainUpdateLogic(
|
validateRequiredContactsPresent(newDomain.getRegistrant(), newDomain.getContacts());
|
||||||
existingResource, getClientId(), now, eppInput, historyEntry);
|
validateDsData(newDomain.getDsData());
|
||||||
}
|
validateNameserversCountForTld(newDomain.getTld(), newDomain.getNameservers().size());
|
||||||
return builder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/** Some status updates cost money. Bill only once no matter how many of them are changed. */
|
||||||
protected final void verifyDomainUpdateIsAllowed() throws EppException {
|
private Optional<BillingEvent.OneTime> createBillingEventForStatusUpdates(
|
||||||
EppCommandOperations commandOperations = TldSpecificLogicProxy.getUpdatePrice(
|
DomainResource existingDomain, DomainResource newDomain, HistoryEntry historyEntry) {
|
||||||
Registry.get(existingResource.getTld()),
|
MetadataExtension metadataExtension = eppInput.getSingleExtension(MetadataExtension.class);
|
||||||
existingResource.getFullyQualifiedDomainName(),
|
|
||||||
getClientId(),
|
|
||||||
now,
|
|
||||||
eppInput);
|
|
||||||
|
|
||||||
// If the fee extension is present, validate it (even if the cost is zero, to check for price
|
|
||||||
// mismatches). Don't rely on the the validateFeeChallenge check for feeUpdate nullness, because
|
|
||||||
// it throws an error if the name is premium, and we don't want to do that here.
|
|
||||||
Money totalCost = commandOperations.getTotalCost();
|
|
||||||
if (feeUpdate != null) {
|
|
||||||
validateFeeChallenge(targetId, existingResource.getTld(), now, feeUpdate, totalCost);
|
|
||||||
// If it's not present but the cost is not zero, throw an exception.
|
|
||||||
} else if (!totalCost.isZero()) {
|
|
||||||
throw new FeesRequiredForNonFreeUpdateException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected final void modifyUpdateRelatedResources() {
|
|
||||||
// Determine the status changes, and filter to server statuses.
|
|
||||||
// If any of these statuses have been added or removed, bill once.
|
|
||||||
if (metadataExtension != null && metadataExtension.getRequestedByRegistrar()) {
|
if (metadataExtension != null && metadataExtension.getRequestedByRegistrar()) {
|
||||||
Set<StatusValue> statusDifferences =
|
for (StatusValue statusValue
|
||||||
symmetricDifference(existingResource.getStatusValues(), newResource.getStatusValues());
|
: symmetricDifference(existingDomain.getStatusValues(), newDomain.getStatusValues())) {
|
||||||
if (Iterables.any(statusDifferences, new Predicate<StatusValue>() {
|
if (statusValue.isChargedStatus()) {
|
||||||
@Override
|
// Only charge once.
|
||||||
public boolean apply(StatusValue statusValue) {
|
return Optional.of(new BillingEvent.OneTime.Builder()
|
||||||
return statusValue.isChargedStatus();
|
|
||||||
}})) {
|
|
||||||
BillingEvent.OneTime billingEvent = new BillingEvent.OneTime.Builder()
|
|
||||||
.setReason(Reason.SERVER_STATUS)
|
.setReason(Reason.SERVER_STATUS)
|
||||||
.setTargetId(targetId)
|
.setTargetId(targetId)
|
||||||
.setClientId(getClientId())
|
.setClientId(clientId)
|
||||||
.setCost(Registry.get(existingResource.getTld()).getServerStatusChangeCost())
|
.setCost(Registry.get(existingDomain.getTld()).getServerStatusChangeCost())
|
||||||
.setEventTime(now)
|
.setEventTime(now)
|
||||||
.setBillingTime(now)
|
.setBillingTime(now)
|
||||||
.setParent(historyEntry)
|
.setParent(historyEntry)
|
||||||
.build();
|
.build());
|
||||||
ofy().save().entity(billingEvent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return Optional.absent();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
private void handleExtraFlowLogic(DomainResource existingDomain, HistoryEntry historyEntry)
|
||||||
protected void enqueueTasks() {
|
throws EppException {
|
||||||
DnsQueue.create().addDomainRefreshTask(existingResource.getFullyQualifiedDomainName());
|
Optional<RegistryExtraFlowLogic> extraFlowLogic =
|
||||||
|
RegistryExtraFlowLogicProxy.newInstanceForDomain(existingDomain);
|
||||||
|
if (extraFlowLogic.isPresent()) {
|
||||||
|
extraFlowLogic.get().performAdditionalDomainUpdateLogic(
|
||||||
|
existingDomain, clientId, now, eppInput, historyEntry);
|
||||||
|
extraFlowLogic.get().commitAdditionalLogicChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected final HistoryEntry.Type getHistoryEntryType() {
|
|
||||||
return HistoryEntry.Type.DOMAIN_UPDATE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,27 +32,27 @@ import com.google.common.collect.ImmutableSet;
|
||||||
import com.googlecode.objectify.Key;
|
import com.googlecode.objectify.Key;
|
||||||
import google.registry.flows.EppException.UnimplementedExtensionException;
|
import google.registry.flows.EppException.UnimplementedExtensionException;
|
||||||
import google.registry.flows.ResourceFlowTestCase;
|
import google.registry.flows.ResourceFlowTestCase;
|
||||||
|
import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
|
||||||
import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
|
import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
|
||||||
import google.registry.flows.ResourceMutateFlow.ResourceDoesNotExistException;
|
|
||||||
import google.registry.flows.ResourceUpdateFlow.AddRemoveSameValueEppException;
|
|
||||||
import google.registry.flows.ResourceUpdateFlow.ResourceHasClientUpdateProhibitedException;
|
|
||||||
import google.registry.flows.ResourceUpdateFlow.StatusNotClientSettableException;
|
|
||||||
import google.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException;
|
|
||||||
import google.registry.flows.domain.BaseDomainUpdateFlow.EmptySecDnsUpdateException;
|
|
||||||
import google.registry.flows.domain.BaseDomainUpdateFlow.MaxSigLifeChangeNotSupportedException;
|
|
||||||
import google.registry.flows.domain.BaseDomainUpdateFlow.SecDnsAllUsageException;
|
|
||||||
import google.registry.flows.domain.BaseDomainUpdateFlow.UrgentAttributeNotSupportedException;
|
|
||||||
import google.registry.flows.domain.DomainApplicationUpdateFlow.ApplicationStatusProhibitsUpdateException;
|
import google.registry.flows.domain.DomainApplicationUpdateFlow.ApplicationStatusProhibitsUpdateException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.DuplicateContactForRoleException;
|
import google.registry.flows.domain.DomainFlowUtils.DuplicateContactForRoleException;
|
||||||
|
import google.registry.flows.domain.DomainFlowUtils.EmptySecDnsUpdateException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.LinkedResourcesDoNotExistException;
|
import google.registry.flows.domain.DomainFlowUtils.LinkedResourcesDoNotExistException;
|
||||||
|
import google.registry.flows.domain.DomainFlowUtils.MaxSigLifeChangeNotSupportedException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.MissingAdminContactException;
|
import google.registry.flows.domain.DomainFlowUtils.MissingAdminContactException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.MissingContactTypeException;
|
import google.registry.flows.domain.DomainFlowUtils.MissingContactTypeException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.MissingTechnicalContactException;
|
import google.registry.flows.domain.DomainFlowUtils.MissingTechnicalContactException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.NameserversNotAllowedException;
|
import google.registry.flows.domain.DomainFlowUtils.NameserversNotAllowedException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
|
import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.RegistrantNotAllowedException;
|
import google.registry.flows.domain.DomainFlowUtils.RegistrantNotAllowedException;
|
||||||
|
import google.registry.flows.domain.DomainFlowUtils.SecDnsAllUsageException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.TooManyDsRecordsException;
|
import google.registry.flows.domain.DomainFlowUtils.TooManyDsRecordsException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.TooManyNameserversException;
|
import google.registry.flows.domain.DomainFlowUtils.TooManyNameserversException;
|
||||||
|
import google.registry.flows.domain.DomainFlowUtils.UrgentAttributeNotSupportedException;
|
||||||
|
import google.registry.flows.exceptions.AddRemoveSameValueEppException;
|
||||||
|
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException;
|
||||||
|
import google.registry.flows.exceptions.ResourceStatusProhibitsOperationException;
|
||||||
|
import google.registry.flows.exceptions.StatusNotClientSettableException;
|
||||||
import google.registry.model.contact.ContactResource;
|
import google.registry.model.contact.ContactResource;
|
||||||
import google.registry.model.domain.DesignatedContact;
|
import google.registry.model.domain.DesignatedContact;
|
||||||
import google.registry.model.domain.DesignatedContact.Type;
|
import google.registry.model.domain.DesignatedContact.Type;
|
||||||
|
@ -323,10 +323,10 @@ public class DomainApplicationUpdateFlowTest
|
||||||
|
|
||||||
private void doSecDnsFailingTest(Class<? extends Exception> expectedException, String xmlFilename)
|
private void doSecDnsFailingTest(Class<? extends Exception> expectedException, String xmlFilename)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
thrown.expect(expectedException);
|
|
||||||
setEppInput(xmlFilename);
|
setEppInput(xmlFilename);
|
||||||
persistReferencedEntities();
|
persistReferencedEntities();
|
||||||
persistNewApplication();
|
persistNewApplication();
|
||||||
|
thrown.expect(expectedException);
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,7 +357,6 @@ public class DomainApplicationUpdateFlowTest
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_secDnsTooManyDsRecords() throws Exception {
|
public void testFailure_secDnsTooManyDsRecords() throws Exception {
|
||||||
thrown.expect(TooManyDsRecordsException.class);
|
|
||||||
ImmutableSet.Builder<DelegationSignerData> builder = new ImmutableSet.Builder<>();
|
ImmutableSet.Builder<DelegationSignerData> builder = new ImmutableSet.Builder<>();
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
builder.add(DelegationSignerData.create(i, 2, 3, new byte[]{0, 1, 2}));
|
builder.add(DelegationSignerData.create(i, 2, 3, new byte[]{0, 1, 2}));
|
||||||
|
@ -365,6 +364,7 @@ public class DomainApplicationUpdateFlowTest
|
||||||
|
|
||||||
setEppInput("domain_update_sunrise_dsdata_add.xml");
|
setEppInput("domain_update_sunrise_dsdata_add.xml");
|
||||||
persistResource(newApplicationBuilder().setDsData(builder.build()).build());
|
persistResource(newApplicationBuilder().setDsData(builder.build()).build());
|
||||||
|
thrown.expect(TooManyDsRecordsException.class);
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,64 +383,64 @@ public class DomainApplicationUpdateFlowTest
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_tooManyNameservers() throws Exception {
|
public void testFailure_tooManyNameservers() throws Exception {
|
||||||
thrown.expect(TooManyNameserversException.class);
|
|
||||||
setEppInput("domain_update_sunrise_add_nameserver.xml");
|
|
||||||
persistReferencedEntities();
|
persistReferencedEntities();
|
||||||
persistApplication();
|
persistApplication();
|
||||||
// Modify application to have 13 nameservers. We will then remove one and add one in the test.
|
// Modify application to have 13 nameservers. We will then remove one and add one in the test.
|
||||||
modifyApplicationToHave13Nameservers();
|
modifyApplicationToHave13Nameservers();
|
||||||
|
setEppInput("domain_update_sunrise_add_nameserver.xml");
|
||||||
|
thrown.expect(TooManyNameserversException.class);
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_wrongExtension() throws Exception {
|
public void testFailure_wrongExtension() throws Exception {
|
||||||
thrown.expect(UnimplementedExtensionException.class);
|
|
||||||
setEppInput("domain_update_sunrise_wrong_extension.xml");
|
setEppInput("domain_update_sunrise_wrong_extension.xml");
|
||||||
|
thrown.expect(UnimplementedExtensionException.class);
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_neverExisted() throws Exception {
|
public void testFailure_neverExisted() throws Exception {
|
||||||
|
persistReferencedEntities();
|
||||||
thrown.expect(
|
thrown.expect(
|
||||||
ResourceDoesNotExistException.class,
|
ResourceDoesNotExistException.class,
|
||||||
String.format("(%s)", getUniqueIdFromCommand()));
|
String.format("(%s)", getUniqueIdFromCommand()));
|
||||||
persistReferencedEntities();
|
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_existedButWasDeleted() throws Exception {
|
public void testFailure_existedButWasDeleted() throws Exception {
|
||||||
|
persistReferencedEntities();
|
||||||
|
persistResource(newApplicationBuilder().setDeletionTime(START_OF_TIME).build());
|
||||||
thrown.expect(
|
thrown.expect(
|
||||||
ResourceDoesNotExistException.class,
|
ResourceDoesNotExistException.class,
|
||||||
String.format("(%s)", getUniqueIdFromCommand()));
|
String.format("(%s)", getUniqueIdFromCommand()));
|
||||||
persistReferencedEntities();
|
|
||||||
persistResource(newApplicationBuilder().setDeletionTime(START_OF_TIME).build());
|
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_clientUpdateProhibited() throws Exception {
|
public void testFailure_clientUpdateProhibited() throws Exception {
|
||||||
thrown.expect(ResourceHasClientUpdateProhibitedException.class);
|
|
||||||
setEppInput("domain_update_sunrise_authinfo.xml");
|
setEppInput("domain_update_sunrise_authinfo.xml");
|
||||||
persistReferencedEntities();
|
persistReferencedEntities();
|
||||||
persistResource(newApplicationBuilder().setStatusValues(
|
persistResource(newApplicationBuilder().setStatusValues(
|
||||||
ImmutableSet.of(StatusValue.CLIENT_UPDATE_PROHIBITED)).build());
|
ImmutableSet.of(StatusValue.CLIENT_UPDATE_PROHIBITED)).build());
|
||||||
|
thrown.expect(ResourceHasClientUpdateProhibitedException.class);
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_serverUpdateProhibited() throws Exception {
|
public void testFailure_serverUpdateProhibited() throws Exception {
|
||||||
thrown.expect(ResourceStatusProhibitsOperationException.class);
|
|
||||||
persistReferencedEntities();
|
persistReferencedEntities();
|
||||||
persistResource(newApplicationBuilder().setStatusValues(
|
persistResource(newApplicationBuilder().setStatusValues(
|
||||||
ImmutableSet.of(StatusValue.SERVER_UPDATE_PROHIBITED)).build());
|
ImmutableSet.of(StatusValue.SERVER_UPDATE_PROHIBITED)).build());
|
||||||
|
thrown.expect(ResourceStatusProhibitsOperationException.class);
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doIllegalApplicationStatusTest(ApplicationStatus status) throws Exception {
|
private void doIllegalApplicationStatusTest(ApplicationStatus status) throws Exception {
|
||||||
thrown.expect(ApplicationStatusProhibitsUpdateException.class);
|
|
||||||
persistReferencedEntities();
|
persistReferencedEntities();
|
||||||
persistResource(newApplicationBuilder().setApplicationStatus(status).build());
|
persistResource(newApplicationBuilder().setApplicationStatus(status).build());
|
||||||
|
thrown.expect(ApplicationStatusProhibitsUpdateException.class);
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -461,31 +461,30 @@ public class DomainApplicationUpdateFlowTest
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_missingHost() throws Exception {
|
public void testFailure_missingHost() throws Exception {
|
||||||
thrown.expect(
|
|
||||||
LinkedResourcesDoNotExistException.class,
|
|
||||||
"(ns2.example.tld)");
|
|
||||||
persistActiveHost("ns1.example.tld");
|
persistActiveHost("ns1.example.tld");
|
||||||
persistActiveContact("sh8013");
|
persistActiveContact("sh8013");
|
||||||
persistActiveContact("mak21");
|
persistActiveContact("mak21");
|
||||||
persistNewApplication();
|
persistNewApplication();
|
||||||
|
thrown.expect(
|
||||||
|
LinkedResourcesDoNotExistException.class,
|
||||||
|
"(ns2.example.tld)");
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_missingContact() throws Exception {
|
public void testFailure_missingContact() throws Exception {
|
||||||
thrown.expect(
|
|
||||||
LinkedResourcesDoNotExistException.class,
|
|
||||||
"(sh8013)");
|
|
||||||
persistActiveHost("ns1.example.tld");
|
persistActiveHost("ns1.example.tld");
|
||||||
persistActiveHost("ns2.example.tld");
|
persistActiveHost("ns2.example.tld");
|
||||||
persistActiveContact("mak21");
|
persistActiveContact("mak21");
|
||||||
persistNewApplication();
|
persistNewApplication();
|
||||||
|
thrown.expect(
|
||||||
|
LinkedResourcesDoNotExistException.class,
|
||||||
|
"(sh8013)");
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_addingDuplicateContact() throws Exception {
|
public void testFailure_addingDuplicateContact() throws Exception {
|
||||||
thrown.expect(DuplicateContactForRoleException.class);
|
|
||||||
persistReferencedEntities();
|
persistReferencedEntities();
|
||||||
persistActiveContact("foo");
|
persistActiveContact("foo");
|
||||||
persistNewApplication();
|
persistNewApplication();
|
||||||
|
@ -494,15 +493,16 @@ public class DomainApplicationUpdateFlowTest
|
||||||
persistResource(reloadDomainApplication().asBuilder().setContacts(ImmutableSet.of(
|
persistResource(reloadDomainApplication().asBuilder().setContacts(ImmutableSet.of(
|
||||||
DesignatedContact.create(Type.TECH, Key.create(
|
DesignatedContact.create(Type.TECH, Key.create(
|
||||||
loadByForeignKey(ContactResource.class, "foo", clock.nowUtc()))))).build());
|
loadByForeignKey(ContactResource.class, "foo", clock.nowUtc()))))).build());
|
||||||
|
thrown.expect(DuplicateContactForRoleException.class);
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_clientProhibitedStatusValue() throws Exception {
|
public void testFailure_clientProhibitedStatusValue() throws Exception {
|
||||||
thrown.expect(StatusNotClientSettableException.class);
|
|
||||||
setEppInput("domain_update_sunrise_prohibited_status.xml");
|
setEppInput("domain_update_sunrise_prohibited_status.xml");
|
||||||
persistReferencedEntities();
|
persistReferencedEntities();
|
||||||
persistNewApplication();
|
persistNewApplication();
|
||||||
|
thrown.expect(StatusNotClientSettableException.class);
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -521,35 +521,34 @@ public class DomainApplicationUpdateFlowTest
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_duplicateContactInCommand() throws Exception {
|
public void testFailure_duplicateContactInCommand() throws Exception {
|
||||||
thrown.expect(DuplicateContactForRoleException.class);
|
|
||||||
setEppInput("domain_update_sunrise_duplicate_contact.xml");
|
setEppInput("domain_update_sunrise_duplicate_contact.xml");
|
||||||
persistReferencedEntities();
|
persistReferencedEntities();
|
||||||
persistNewApplication();
|
persistNewApplication();
|
||||||
|
thrown.expect(DuplicateContactForRoleException.class);
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_missingContactType() throws Exception {
|
public void testFailure_missingContactType() throws Exception {
|
||||||
// We need to test for missing type, but not for invalid - the schema enforces that for us.
|
|
||||||
thrown.expect(MissingContactTypeException.class);
|
|
||||||
setEppInput("domain_update_sunrise_missing_contact_type.xml");
|
setEppInput("domain_update_sunrise_missing_contact_type.xml");
|
||||||
persistReferencedEntities();
|
persistReferencedEntities();
|
||||||
persistNewApplication();
|
persistNewApplication();
|
||||||
|
// We need to test for missing type, but not for invalid - the schema enforces that for us.
|
||||||
|
thrown.expect(MissingContactTypeException.class);
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_unauthorizedClient() throws Exception {
|
public void testFailure_unauthorizedClient() throws Exception {
|
||||||
thrown.expect(ResourceNotOwnedException.class);
|
|
||||||
sessionMetadata.setClientId("NewRegistrar");
|
sessionMetadata.setClientId("NewRegistrar");
|
||||||
persistReferencedEntities();
|
persistReferencedEntities();
|
||||||
persistApplication();
|
persistApplication();
|
||||||
|
thrown.expect(ResourceNotOwnedException.class);
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_notAuthorizedForTld() throws Exception {
|
public void testFailure_notAuthorizedForTld() throws Exception {
|
||||||
thrown.expect(NotAuthorizedForTldException.class);
|
|
||||||
persistResource(
|
persistResource(
|
||||||
Registrar.loadByClientId("TheRegistrar")
|
Registrar.loadByClientId("TheRegistrar")
|
||||||
.asBuilder()
|
.asBuilder()
|
||||||
|
@ -557,6 +556,7 @@ public class DomainApplicationUpdateFlowTest
|
||||||
.build());
|
.build());
|
||||||
persistReferencedEntities();
|
persistReferencedEntities();
|
||||||
persistApplication();
|
persistApplication();
|
||||||
|
thrown.expect(NotAuthorizedForTldException.class);
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -572,19 +572,18 @@ public class DomainApplicationUpdateFlowTest
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_sameNameserverAddedAndRemoved() throws Exception {
|
public void testFailure_sameNameserverAddedAndRemoved() throws Exception {
|
||||||
thrown.expect(AddRemoveSameValueEppException.class);
|
|
||||||
setEppInput("domain_update_sunrise_add_remove_same_host.xml");
|
setEppInput("domain_update_sunrise_add_remove_same_host.xml");
|
||||||
persistReferencedEntities();
|
persistReferencedEntities();
|
||||||
persistResource(newApplicationBuilder()
|
persistResource(newApplicationBuilder()
|
||||||
.setNameservers(ImmutableSet.of(Key.create(
|
.setNameservers(ImmutableSet.of(Key.create(
|
||||||
loadByForeignKey(HostResource.class, "ns1.example.tld", clock.nowUtc()))))
|
loadByForeignKey(HostResource.class, "ns1.example.tld", clock.nowUtc()))))
|
||||||
.build());
|
.build());
|
||||||
|
thrown.expect(AddRemoveSameValueEppException.class);
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_sameContactAddedAndRemoved() throws Exception {
|
public void testFailure_sameContactAddedAndRemoved() throws Exception {
|
||||||
thrown.expect(AddRemoveSameValueEppException.class);
|
|
||||||
setEppInput("domain_update_sunrise_add_remove_same_contact.xml");
|
setEppInput("domain_update_sunrise_add_remove_same_contact.xml");
|
||||||
persistReferencedEntities();
|
persistReferencedEntities();
|
||||||
persistResource(newApplicationBuilder()
|
persistResource(newApplicationBuilder()
|
||||||
|
@ -593,12 +592,12 @@ public class DomainApplicationUpdateFlowTest
|
||||||
Key.create(
|
Key.create(
|
||||||
loadByForeignKey(ContactResource.class, "sh8013", clock.nowUtc())))))
|
loadByForeignKey(ContactResource.class, "sh8013", clock.nowUtc())))))
|
||||||
.build());
|
.build());
|
||||||
|
thrown.expect(AddRemoveSameValueEppException.class);
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_removeAdmin() throws Exception {
|
public void testFailure_removeAdmin() throws Exception {
|
||||||
thrown.expect(MissingAdminContactException.class);
|
|
||||||
setEppInput("domain_update_sunrise_remove_admin.xml");
|
setEppInput("domain_update_sunrise_remove_admin.xml");
|
||||||
persistReferencedEntities();
|
persistReferencedEntities();
|
||||||
persistResource(newApplicationBuilder()
|
persistResource(newApplicationBuilder()
|
||||||
|
@ -606,12 +605,12 @@ public class DomainApplicationUpdateFlowTest
|
||||||
DesignatedContact.create(Type.ADMIN, Key.create(sh8013Contact)),
|
DesignatedContact.create(Type.ADMIN, Key.create(sh8013Contact)),
|
||||||
DesignatedContact.create(Type.TECH, Key.create(sh8013Contact))))
|
DesignatedContact.create(Type.TECH, Key.create(sh8013Contact))))
|
||||||
.build());
|
.build());
|
||||||
|
thrown.expect(MissingAdminContactException.class);
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_removeTech() throws Exception {
|
public void testFailure_removeTech() throws Exception {
|
||||||
thrown.expect(MissingTechnicalContactException.class);
|
|
||||||
setEppInput("domain_update_sunrise_remove_tech.xml");
|
setEppInput("domain_update_sunrise_remove_tech.xml");
|
||||||
persistReferencedEntities();
|
persistReferencedEntities();
|
||||||
persistResource(newApplicationBuilder()
|
persistResource(newApplicationBuilder()
|
||||||
|
@ -619,6 +618,7 @@ public class DomainApplicationUpdateFlowTest
|
||||||
DesignatedContact.create(Type.ADMIN, Key.create(sh8013Contact)),
|
DesignatedContact.create(Type.ADMIN, Key.create(sh8013Contact)),
|
||||||
DesignatedContact.create(Type.TECH, Key.create(sh8013Contact))))
|
DesignatedContact.create(Type.TECH, Key.create(sh8013Contact))))
|
||||||
.build());
|
.build());
|
||||||
|
thrown.expect(MissingTechnicalContactException.class);
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,23 +43,16 @@ import com.google.common.collect.ImmutableSet;
|
||||||
import com.googlecode.objectify.Key;
|
import com.googlecode.objectify.Key;
|
||||||
import google.registry.flows.EppException.UnimplementedExtensionException;
|
import google.registry.flows.EppException.UnimplementedExtensionException;
|
||||||
import google.registry.flows.EppRequestSource;
|
import google.registry.flows.EppRequestSource;
|
||||||
import google.registry.flows.ResourceCreateOrMutateFlow.OnlyToolCanPassMetadataException;
|
|
||||||
import google.registry.flows.ResourceFlowTestCase;
|
import google.registry.flows.ResourceFlowTestCase;
|
||||||
|
import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
|
||||||
import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
|
import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
|
||||||
import google.registry.flows.ResourceMutateFlow.ResourceDoesNotExistException;
|
|
||||||
import google.registry.flows.ResourceUpdateFlow.AddRemoveSameValueEppException;
|
|
||||||
import google.registry.flows.ResourceUpdateFlow.ResourceHasClientUpdateProhibitedException;
|
|
||||||
import google.registry.flows.ResourceUpdateFlow.StatusNotClientSettableException;
|
|
||||||
import google.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException;
|
|
||||||
import google.registry.flows.domain.BaseDomainUpdateFlow.EmptySecDnsUpdateException;
|
|
||||||
import google.registry.flows.domain.BaseDomainUpdateFlow.MaxSigLifeChangeNotSupportedException;
|
|
||||||
import google.registry.flows.domain.BaseDomainUpdateFlow.SecDnsAllUsageException;
|
|
||||||
import google.registry.flows.domain.BaseDomainUpdateFlow.UrgentAttributeNotSupportedException;
|
|
||||||
import google.registry.flows.domain.DomainFlowUtils.DuplicateContactForRoleException;
|
import google.registry.flows.domain.DomainFlowUtils.DuplicateContactForRoleException;
|
||||||
|
import google.registry.flows.domain.DomainFlowUtils.EmptySecDnsUpdateException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.FeesMismatchException;
|
import google.registry.flows.domain.DomainFlowUtils.FeesMismatchException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.FeesRequiredForNonFreeUpdateException;
|
import google.registry.flows.domain.DomainFlowUtils.FeesRequiredForNonFreeUpdateException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.LinkedResourceInPendingDeleteProhibitsOperationException;
|
import google.registry.flows.domain.DomainFlowUtils.LinkedResourceInPendingDeleteProhibitsOperationException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.LinkedResourcesDoNotExistException;
|
import google.registry.flows.domain.DomainFlowUtils.LinkedResourcesDoNotExistException;
|
||||||
|
import google.registry.flows.domain.DomainFlowUtils.MaxSigLifeChangeNotSupportedException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.MissingAdminContactException;
|
import google.registry.flows.domain.DomainFlowUtils.MissingAdminContactException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.MissingContactTypeException;
|
import google.registry.flows.domain.DomainFlowUtils.MissingContactTypeException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.MissingTechnicalContactException;
|
import google.registry.flows.domain.DomainFlowUtils.MissingTechnicalContactException;
|
||||||
|
@ -67,8 +60,15 @@ import google.registry.flows.domain.DomainFlowUtils.NameserversNotAllowedExcepti
|
||||||
import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedException;
|
import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
|
import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.RegistrantNotAllowedException;
|
import google.registry.flows.domain.DomainFlowUtils.RegistrantNotAllowedException;
|
||||||
|
import google.registry.flows.domain.DomainFlowUtils.SecDnsAllUsageException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.TooManyDsRecordsException;
|
import google.registry.flows.domain.DomainFlowUtils.TooManyDsRecordsException;
|
||||||
import google.registry.flows.domain.DomainFlowUtils.TooManyNameserversException;
|
import google.registry.flows.domain.DomainFlowUtils.TooManyNameserversException;
|
||||||
|
import google.registry.flows.domain.DomainFlowUtils.UrgentAttributeNotSupportedException;
|
||||||
|
import google.registry.flows.exceptions.AddRemoveSameValueEppException;
|
||||||
|
import google.registry.flows.exceptions.OnlyToolCanPassMetadataException;
|
||||||
|
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException;
|
||||||
|
import google.registry.flows.exceptions.ResourceStatusProhibitsOperationException;
|
||||||
|
import google.registry.flows.exceptions.StatusNotClientSettableException;
|
||||||
import google.registry.model.billing.BillingEvent;
|
import google.registry.model.billing.BillingEvent;
|
||||||
import google.registry.model.billing.BillingEvent.Reason;
|
import google.registry.model.billing.BillingEvent.Reason;
|
||||||
import google.registry.model.contact.ContactResource;
|
import google.registry.model.contact.ContactResource;
|
||||||
|
@ -822,6 +822,8 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
|
||||||
public void testFailure_wrongExtension() throws Exception {
|
public void testFailure_wrongExtension() throws Exception {
|
||||||
thrown.expect(UnimplementedExtensionException.class);
|
thrown.expect(UnimplementedExtensionException.class);
|
||||||
setEppInput("domain_update_wrong_extension.xml");
|
setEppInput("domain_update_wrong_extension.xml");
|
||||||
|
persistReferencedEntities();
|
||||||
|
persistDomain();
|
||||||
runFlow();
|
runFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue